/*
 * @(#)WsseCredential.java
 *
 * Copyright (C) 2007 Infocity Inc. All Rights Reserved.
 */
package info.dragonlady.wsse;

import info.dragonlady.crypto.Crypto;

import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import org.apache.commons.codec.binary.Base64;

/**
 * WSSE認証情報クラス。
 * 
 * @author Hiroshi.Ebata(INFOCITY)
 * @version $Revision: 1.0 $ $Date: 2007/03/08 15:06:51 $
 */
public class WsseCredential {
	public static final String WSSE_HEADER_NAME = "X-WSSE";

	public static final String WSSE_TOKEN_NAME = "UsernameToken";

	public static final String WSSE_TOKEN_USERNAME = "Username";

	public static final String WSSE_TOKEN_PASSWORD_DIGEST = "PasswordDigest";

	public static final String WSSE_TOKEN_CREATED = "Created";

	public static final String WSSE_TOKEN_NONCE = "Nonce";

	private String userName = null;

	private String passwordDigest = null;

	private String created = null;

	private String nonce = null;

	private WsseCredential() {
	}

	private WsseCredential(String userName, String passwordDigest, String created, String nonce) {
		this.userName = userName;
		this.passwordDigest = passwordDigest;
		this.created = created;
		this.nonce = nonce;
	}

	/**
	 * セキュリティトークン作成日時取得。
	 * 
	 * @return created
	 */
	public String getCreated() {
		return created;
	}

	/**
	 * セキュリティトークン取得。
	 * 
	 * @return nonce
	 */
	public String getNonce() {
		return nonce;
	}

	/**
	 * パスワードダイジェスト取得。
	 * 
	 * @return passwordDigest パスワードダイジェスト
	 */
	public String getPasswordDigest() {
		return passwordDigest;
	}

	/**
	 * ユーザ名取得。
	 * 
	 * @return userName ユーザ名
	 */
	public String getUserName() {
		return userName;
	}

	/**
	 * WSSE認証情報を生成する。
	 * 
	 * @param userName ユーザ名
	 * @param password パスワード
	 * @return 認証情報オブジェクト
	 * @throws NoSuchAlgorithmException
	 */
	public static final WsseCredential generate(String userName, String password) throws NoSuchAlgorithmException {
		Calendar cal = Calendar.getInstance();

		String nonce = Crypto.md5(String.valueOf(cal.getTimeInMillis()));
		nonce = Crypto.sha1(nonce);
		String created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(cal.getTime());
		String passwordDigest = Crypto.sha1(nonce + created + password);

		return new WsseCredential(userName, passwordDigest, created, nonce);
	}

	/**
	 * WSSE認証トークンを解析する。
	 * 
	 * @param credential WSSE認証トークン
	 * @return WSSE認証情報オブジェクト
	 */
	public static final WsseCredential parse(String credential) {
		WsseCredential wc = null;
		if(credential.startsWith(WSSE_TOKEN_NAME)) {
			wc = new WsseCredential();
			String tokenStr = credential.replaceAll(WSSE_TOKEN_NAME, "");

			List<Integer> indexList = new ArrayList<Integer>();
			indexList.add(tokenStr.indexOf(WSSE_TOKEN_USERNAME));
			indexList.add(tokenStr.indexOf(WSSE_TOKEN_PASSWORD_DIGEST));
			indexList.add(tokenStr.indexOf(WSSE_TOKEN_CREATED));
			indexList.add(tokenStr.indexOf(WSSE_TOKEN_NONCE));
			Collections.sort(indexList);

			for(int i = 0; i < indexList.size(); i++) {
				String token = null;
				if(i < indexList.size() - 1) {
					token = tokenStr.substring(indexList.get(i), indexList.get(i+1));
				} else {
					token = tokenStr.substring(indexList.get(i));
				}
				token = token.trim();
				if(token.endsWith(",")) {
					token = token.substring(0, token.length()-1);
				}
				String[] set = token.split("=", 2);
				String key = set[0];
				String value = set[1].substring(1, set[1].length()-1);
				if(key.equals(WSSE_TOKEN_USERNAME)) {
					wc.userName = value;
				}
				if(key.equals(WSSE_TOKEN_PASSWORD_DIGEST)) {
					wc.passwordDigest = Crypto.toHexString(Base64.decodeBase64(value.getBytes()));
				}
				if(key.equals(WSSE_TOKEN_CREATED)) {
					wc.created = value;
				}
				if(key.equals(WSSE_TOKEN_NONCE)) {
					wc.nonce = Crypto.toHexString(Base64.decodeBase64(value.getBytes()));
				}
			}
		}
		return wc;
	}

	/**
	 * WSSE認証情報の妥当性を検証する。
	 * 
	 * @param userName ユーザ名
	 * @param password パスワード
	 * @return boolean true：妥当である／false：妥当でない
	 * @throws NoSuchAlgorithmException 
	 */
	public boolean validate(String userName, String password) throws NoSuchAlgorithmException {
		// ユーザ名の一致を確認
		if(!userName.equals(this.userName)) {
			return false;
		}

		// ハッシュ値の一致を確認
		String hash = this.nonce + this.created + password;
		hash = Crypto.sha1(hash);
		return hash.equals(this.passwordDigest);
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(WSSE_TOKEN_NAME + " ");
		sb.append(WSSE_TOKEN_USERNAME + "=\"" + userName + "\", ");
		sb.append(WSSE_TOKEN_PASSWORD_DIGEST + "=\"" + new String(Base64.encodeBase64(Crypto.toByteArray(passwordDigest))) + "\", ");
		sb.append(WSSE_TOKEN_CREATED + "=\"" + created + "\", ");
		sb.append(WSSE_TOKEN_NONCE + "=\"" + new String(Base64.encodeBase64(Crypto.toByteArray(nonce))) + "\"");
		return sb.toString();
	}

}
