/*
 * $Id: ClientKeyManager.java,v 0.0 2009/07/23 00:00:00 t-imamura Exp $
 *
 * Copyright (c) 2009 T.Imamura, All rights reserved.
 */
package jp.co.nkc.gaia.tools.net;

import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

import javax.net.ssl.KeyManager;
import javax.net.ssl.X509KeyManager;

/**
 * X509KeyManager インタフェースを実装した鍵マネージャクラス.<br>
 * クライアント側のセキュアソケットを認証するときの別名を指定します。<br>
 */
public class ClientKeyManager implements X509KeyManager {

	/**
	 * ClientKeyManager を作成します。<br>
	 * {@code bases}か{@code alias}が{@code null}の場合は、
	 * {@code bases}をそのまま返します。
	 * {@code bases}の各要素で、{@link X509KeyManager}の実装クラスの場合は、
	 * {@link ClientKeyManager}でラップし直します。
	 *
	 * @param bases 元となる鍵マネージャ
	 * @param alias 設定するする鍵の別名
	 * @return 鍵マネージャ
	 */
	public static KeyManager[] create(KeyManager[] bases, String alias) {
		if (bases == null) return null;
		if (alias == null) return bases;
		for (int k = 0; k < bases.length; k++) {
			if (bases[k] instanceof X509KeyManager) {
				bases[k] = new ClientKeyManager((X509KeyManager) bases[k], alias);
			}
		}
		return bases;
	}

	/* ----------==========----------==========----------==========----------==========---------- */

	private X509KeyManager base;

	private String alias;

	/**
	 * 別名を指定する鍵マネージャオブジェクトを構築します。
	 *
	 * @param base 基礎となる鍵マネージャ
	 * @param alias 使用する鍵の別名
	 */
	private ClientKeyManager(X509KeyManager base, String alias) {
		super();
		this.base = base;
		this.alias = alias;
	}

	/**
	 * 公開鍵のタイプおよびピアによって認識される証明書発行局のリストに基づいて、
	 * クライアント側のセキュアソケットを認証するときの別名を取得します。
	 *
	 * @param keyType 鍵アルゴリズムのタイプ名。 優先順位の高い鍵タイプから順に指定する。
	 * @param issuers 受け入れることができるCA発行者の被認証者名。
	 *                任意の発行者を受け入れることができる場合は{@code null}。
	 * @param socket  この接続に使用するソケット。このパラメータは{@code null}も可。
	 *                {@code null}の場合は、このインタフェースの実装が、
	 *                任意のソケットに適用可能な別名を自由に選択できることを示す。
	 * @return 指定された鍵の別名。対応する別名がない場合は{@code null}。
	 * @see X509KeyManager#chooseClientAlias(String[], Principal[], Socket)
	 */
	public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
		// return this.base.chooseClientAlias(keyType, issuers, socket);
		if (this.alias == null) return null;
		for (int i = 0; i < keyType.length; i++) {
			String[] aliases = this.base.getClientAliases(keyType[i], issuers);
			if (aliases == null) continue;
			for (int j = 0; j < aliases.length; j++) {
				if (aliases[j].equals(this.alias)) return (this.alias);
			}
		}
		return null;
	}

	/**
	 * 公開鍵のタイプおよびピアによって認識される証明書発行局のリストに基づいて、
	 * サーバー側のセキュアソケットを認証するときの別名を取得します。
	 *
	 * @param keyType 鍵アルゴリズムのタイプ名。
	 * @param issuers 受け入れることができるCA発行者の被認証者名。
	 *                任意の発行者を受け入れることができる場合は{@code null}。
	 * @param socket  この接続に使用するソケット。このパラメータは{@code null}も可。
	 *                {@code null}の場合は、このインタフェースの実装が、
	 *                任意のソケットに適用可能な別名を自由に選択できることを示す。
	 * @return 指定された鍵の別名。対応する別名がない場合は{@code null}。
	 * @see X509KeyManager#chooseServerAlias(String, Principal[], Socket)
	 */
	public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
		return this.base.chooseServerAlias(keyType, issuers, socket);
	}

	/**
	 * 指定された別名に関連付けられた証明書チェーンを返します。
	 *
	 * @param alias 別名
	 * @return 証明書チェーン。
	 *         ユーザーの証明書が最初に配置され、ルート証明書発行局が最後に配置されている。
	 *         別名が見つからない場合は{@code null}。
	 * @see X509KeyManager#getCertificateChain(String)
	 */
	public X509Certificate[] getCertificateChain(String alias) {
		return this.base.getCertificateChain(alias);
	}

	/**
	 * 公開鍵のタイプおよびピアによって認識される証明書発行局のリストに基づいて、
	 * クライアント側のセキュアソケットを認証するときの別名を取得します。
	 *
	 * @param keyType 鍵アルゴリズムのタイプ名。
	 * @param issuers 受け入れることができるCA発行者の被認証者名。
	 *                任意の発行者を受け入れることができる場合は{@code null}。
	 * @return 条件に一致する別名の配列。一致する別名がない場合は{@code null}。
	 * @see X509KeyManager#getClientAliases(String, Principal[])
	 */
	public String[] getClientAliases(String keyType, Principal[] issuers) {
		return this.base.getClientAliases(keyType, issuers);
	}

	/**
	 * 公開鍵のタイプおよびピアによって認識される証明書発行局のリストに基づいて、
	 * サーバー側のセキュアソケットを認証するときの別名を取得します。
	 *
	 * @param keyType 鍵アルゴリズムのタイプ名。
	 * @param issuers 受け入れることができるCA発行者の被認証者名。
	 *                任意の発行者を受け入れることができる場合は{@code null}。
	 * @return 条件に一致する別名の配列。一致する別名がない場合は{@code null}。
	 * @see X509KeyManager#getServerAliases(String, Principal[])
	 */
	public String[] getServerAliases(String keyType, Principal[] issuers) {
		return this.base.getServerAliases(keyType, issuers);
	}

	/**
	 * 指定された別名に関連付けられた鍵を返します。
	 *
	 * @param alias 別名
	 * @return 要求された鍵。別名が見つからない場合は null。
	 */
	public PrivateKey getPrivateKey(String alias) {
		return this.base.getPrivateKey(alias);
	}

}
