1、如果想要在ssl通信中,明确使用某个Key(非对称密钥),那么我们需要自己手动实现X509KeyManager接口。在SSLContext的init方法中,第一个参数是keymanager数组(必须是X509KeyManager格式),这个数组中的第一个keyManager就是通信使用的key,其他的都会忽略。如果没有X509KeyManager格式的keyManager,那么不会抛出异常,只是不能正常工作。
实现我们自己的X509KeyManager,必须实现
public String[] getClientAliases(String keyType, Principal[] issuers)
Return all possible aliases (from the keystore) that could be used to perform client−side SSL
encryption for the given key type (e.g., RSA or DSA). If the issuers array is not null, it will
contain an array of principals who are CAs. The key belonging to an alias in the returned array must
have been issued by an entity contained in the array.
This method should not return null; if no appropriate aliases are found, it should return an array of
length 0.
返回该keystore里所有合适的别名,根据指定的密钥类型能够执行客户端ssl加密。如果issuers数组不是空,那么返回的数组中的数据必须是某个实体颁发的。
public String chooseClientAlias(String keyType, Principal[] issuers)
Select the alias (from the keystore) that should be used to perform client−side SSL encryption for the
given key type (e.g., RSA or DSA). If the issuers array is not null, the key must be provided by
an entity contained in the array. This method should return null if no appropriate alias is found.
根据密钥类型和颁发者,返回一个客户端可以使用的密钥别名
public String[] getServerAliases(String keyType, Principal[] issuers)
Return all possible aliases (from the keystore) that could be used to perform server−side SSL
encryption for the given key type (e.g., RSA or DSA). If the issuers array is not null, the key
must be provided by an entity contained in the array.
This method should not return null; if no appropriate aliases are found, it should return an array of
length 0.
返回所有的服务端可以执行ssl加密的密钥别名数组,根据知道的密钥类型和颁发类型
public String chooseServerAlias(String keyType, Principal[] issuers)
Select the alias (from the keystore) that should be used to perform server−side SSL encryption for the
given key type (e.g., RSA or DSA). If the issuers array is not null, the key must be provided by
an entity contained in the array. This method should return null if no appropriate alias is found.
选择一个服务器执行ssl加密的密钥别名
public X509Certificate[] getCertificateChain(String alias)
Return the array of X509 certificates associated with the given alias. If the keystore contains
non−X509 certificates for the given alias, those certificates should be ignored. Note that the method
returns an array of java.security.cert.X509Certificate objects, not (as do most other
classes within JSSE) javax.security.cert.X509Certificate objects.
返回指定别名的X509格式的证书数组
As the API indicates, key managers may be used by both clients and servers in an SSL conversation. They are
more frequently used by servers, since servers must always authenticate themselves to clients.
一般用于客户端和服务端的ssl会话期间,但是在服务端最频繁使用,因为一般情况客户端需要验证服务器
一下是一个实现X509KeyManager的实例
public class SSLKeyManager implements X509KeyManager {
protected String alias;
protected KeyStore ks;
protected char[] pw;
private String type;
private String issuer;
SSLKeyManager(KeyStore ks, String s, char[] pw) {
this.ks = ks;
alias = s;
this.pw = pw;
try {
java.security.cert.Certificate c = ks.getCertificate(s);
type = c.getPublicKey().getAlgorithm( );
issuer = ((X509Certificate) c).getIssuerDN().getName( );
} catch (Exception e) {
throw new IllegalArgumentException(s + " has a bad key");
}
}
public String chooseClientAlias(String type, Principal[] issuers) {
if (!type.equals(this.type))
return null;
if (issuers == null)
return alias;
for (int i = 0; i < issuers.length; i++) {
if (issuer.equals(issuers[i].getName( )))
return alias;
}
return null;
}
public String chooseServerAlias(String type, Principal[] issuers) {
return chooseClientAlias(type, issuers);
}
// Get the certificates −− make sure each is an X509Certificate
// before copying it into the array.
public X509Certificate[] getCertificateChain(String s) {
try {
java.security.cert.Certificate[] c =
ks.getCertificateChain(s);
Vector c2 = new Vector(c.length);
for (int i = 0; i < c.length; i++)
c2.add(c[i]);
return (X509Certificate[])
c2.toArray(new X509Certificate[0]);
} catch (KeyStoreException kse) {
return null;
}
}
public String[] getClientAliases(String type, Principal[] p) {
String[] s;
String alias = chooseClientAlias(type, p);
if (alias == null)
s = new String[0];
else {
s = new String[1];
s[0] = alias;
}
return s;
}
public String[] getServerAliases(String type, Principal[] p) {
return getClientAliases(type, p);
}
public PrivateKey getPrivateKey(String alias) {
try {
return (PrivateKey) ks.getKey(alias, pw);
} catch (Exception e) {
return null;
}
}
}
Since we've specified an alias we treat client and server authorization the same way, though you could extend
this idea to provide different aliases or otherwise treat the server authentication differently.
我们可以使用不同的别名在服务器和客户端
As a result of this simplification, most methods end up calling the choose−ClientAlias( ) method.
This method checks to see if the key algorithm type matches and, if appropriate, if the key was provided by an
entry in the issuers array. If everything matches, it returns the alias we want; otherwise it returns null.
2、实现KeyManagerFactorySpi引擎,
必须实现protected abstract void engineInit(KeyStore keystore,char[] password);
和protected abstract KeyManager[] engineGetKeyManager();
public class SSLKeyManagerFactory extends KeyManagerFactorySpi{
char[] pw;KeyStore ks;String alias;
public SSLKeyManagerFactory( ) { alias = System.getProperty("xyz.aliasName"); if (alias == null) throw new IllegalArgumentException( "Must specify alias property"); } protected KeyManager[] engineGetKeyManagers( ) { SSLKeyManager[] km = new SSLKeyManager[1]; km[0] = new SSLKeyManager(ks, alias, pw); return km;}
} protected void engineInit(KeyStore ks, char[] pw) { this.ks = ks; this.pw = new char[pw.length]; System.arraycopy(pw, 0, this.pw, 0, pw.length); } }
This engine class requires a security provider, of course; we'll use the provider from Chapter 8, which contains this mapping: put("KeyManagerFactory.XYZ", "javasec.samples.ch14.SSLKeyManagerFactory"); Finally, here's how we use the key manager within our server: package javasec.samples.ch14; import java.io.*; import java.net.*; import java.security.*; import javax.net.*; import javax.net.ssl.*; import javax.security.cert.*; import com.sun.net.ssl.*; import javasec.sample.ch08.XYZProvider; public class SSLServerKeyManager { public static void main(String[] args) throws Exception { Security.addProvider(new XYZProvider( )); SSLContext sc = SSLContext.getInstance("TLS"); KeyStore ks = KeyStore.getInstance("jceks"); char[] password = args[1].toCharArray( ); ks.load(new FileInputStream(args[0]), null); KeyManagerFactory kmf = KeyManagerFactory.getInstance("XYZ"); kmf.init(ks, password); sc.init(kmf.getKeyManagers( ), null, null); ServerSocketFactory ssf = sc.getServerSocketFactory( ); ServerSocket ss = ssf.createServerSocket(9096); while (true) { new SSLSimpleServer(ss.accept()).start( ); } } }