概念
JAVA使用keystore文件来存储所有KEY,keystore文件可以存放多个KEY,访问它需要密码。
下面我介绍下如何将用OpenSSL做自签名的证书一文中介绍的OpenSSL产生的KEY与JAVA的KEY转换后使用,从而达到JAVA与OpenSSL通信的目的。
用OpenSSL生成CA根证书,即(P1,V1)
此步骤参见用OpenSSL做自签名的证书一文
在JAVA环境下生成自己的KEY,即(P2,V2)
keytool -genkey -alias clientapp -keystore mycerts
注意:这里会提示输入访问keystore的密码,以及组织、城市、省份,一定要与CA根证书的一致,否则不能被CA签名。
从keystore中导出public key,即P2
keytool -keystore mycerts -certreq -alias clientapp -file clientapp.crs
用CA根证书为这个public key进行签名,即用V1给P2加密
openssl ca -out clientapp.pem -config ./openssl.cnf -infiles clientapp.crs
转换PEM到DER格式
openssl x509 -in clientapp.pem -out clientapp.der -outform DER
导入CA证书,即P1
keytool -keystore mycerts -alias systemca -import -file cacert.pem
导入用户证书,即被V1加密过的P2
keytool -keystore mycerts -alias clientapp -import -file clientapp.der
注意:这里一定要先导入CA证书再导入用户证书,否则会报错。
现在我们就生成了JAVA服务器使用的所有KEY了,在程序中将mycerts这个keystore导入就可以了。
如果客户端是使用OpenSSL的程序,那么用CA证书cacert.pem就能正常通信了,如果也是JAVA程序,那么我们需要将CA证书也转换成keystore:
keytool -import -keystore clikeystore -import -trustcacerts -file cacert.pem
生成的clikeystore供JAVA客户端使用,就能通信。
再附上SVR和CLI的JAVA程序,我已经用上面的KEY都测试通过:
SVR端:
- import java.io. * ;
- import java.net. * ;
- import com.sun.net.ssl.KeyManagerFactory;
- import com.sun.net.ssl.KeyManager;
- import com.sun.net.ssl.TrustManagerFactory;
- import com.sun.net.ssl.TrustManager;
- import com.sun.net.ssl.SSLContext;
- import javax.net.ServerSocketFactory;
- import java.security.KeyStore;
- public class svr implements Runnable {
- public static final int PORT = 5555 ;
- public static final String HOST = " localhost " ;
- public static final String QUESTION = " Knock, knock. " ;
- public static final String ANSWER = " Who's there? " ;
- // The new constants that are used during setup.
- public static final String KEYSTORE_FILE = " mycerts " ; // "server_keystore";
- public static final String ALGORITHM = " sunx509 " ;
- public static final String PASSWORD = " churchillobjects " ;
- public static void main(String[] args) {
- new Thread( new svr()).start();
- }
- public void run() {
- ServerSocket ss = null ;
- try {
- // Local references used for clarity. Their presence
- // here is part of the reason we need to import
- // so many classes.
- KeyManagerFactory kmf;
- KeyManager[] km;
- KeyStore ks;
- TrustManagerFactory tmf;
- TrustManager[] tm;
- SSLContext sslc;
- // Create a keystore that will read the JKS (Java KeyStore)
- // file format which was created by the keytool utility.
- ks = KeyStore.getInstance( " JKS " );
- // Load the keystore object with the binary keystore file and
- // a byte array representing its password.
- ks.load( new FileInputStream(KEYSTORE_FILE), PASSWORD.toCharArray());
- // Gives us a factory for key managers that will let
- // us handle the asymetric keys we created earlier.
- kmf = KeyManagerFactory.getInstance(ALGORITHM);
- // Initialize the key manager factory with the keystore object,
- // again using the same password for security since it is going to
- // access the private key.
- kmf.init(ks, PASSWORD.toCharArray());
- // Now we can get the key managers from the factory, since it knows
- // what type we are using now.
- km = kmf.getKeyManagers();
- // Next, create a trust manager factory using the same algorithm.
- // This is to avoid using the certificates in cacerts that
- // represent an authentication security risk.
- tmf = TrustManagerFactory.getInstance(ALGORITHM);
- // then initialize it with the keystore object. This time we don't
- // need the keystore password. This is because trusted certificates
- // are not a sensitive element in the keystore, unlike the
- // private keys.
- tmf.init(ks);
- // Once that's initialized, get the trust managers from the factory.
- tm = tmf.getTrustManagers();
- // Almost done, we need a context object that will get our
- // server socket factory. We specify TLS to indicate that we will
- // need a server socket factory that supports SSL.
- sslc = SSLContext.getInstance( " TLS " );
- // Initialize the context object with the key managers and trust
- // managers we got earlier. The third parameter is an optional
- // SecureRandom object. By passing in null, we are letting the
- // context object create its own.
- sslc.init(km, tm, null );
- // Finally, we get the ordinary-looking server socket factory
- // from the context object.
- ServerSocketFactory ssf = sslc.getServerSocketFactory();
- // From the factory, we simply ask for an ordinary-looking
- // server socket on the port we wish.
- ss = ssf.createServerSocket(PORT);
- listen(ss);
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- finally {
- if (ss != null ) {
- try {
- ss.close();
- }
- catch (IOException e) {
- // oh, well
- }
- }
- System.exit( 0 );
- }
- }
- static void listen(ServerSocket ss) throws Exception {
- System.out.println( " Ready for connections. " );
- while ( true ) {
- Socket s = ss.accept();
- BufferedWriter bw = new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream()));
- BufferedReader br = new BufferedReader(
- new InputStreamReader(s.getInputStream()));
- String q = br.readLine();
- if ( ! QUESTION.equals(q)) {
- throw new RuntimeException( " Wrong question: / "" + q + " / "" );
- }
- System.out.println( " Question: / "" + q + " / "" );
- bw.write(ANSWER + " /n " );
- bw.flush();
- s.close();
- }
- }
- }
- import java.io. * ;
- import java.net. * ;
- import com.sun.net.ssl.KeyManagerFactory;
- import com.sun.net.ssl.TrustManagerFactory;
- import com.sun.net.ssl.SSLContext;
- import java.security.KeyStore;
- import javax.net.SocketFactory;
- public class cli implements Runnable {
- public static final int PORT = 5555 ;
- public static final String HOST = " localhost " ;
- public static final String KEYSTORE_FILE = " clikeystore " ; // "client_keystore";
- public static final String ALGORITHM = " sunx509 " ;
- public static final String PASSWORD = " churchillobjects " ;
- public static final String QUESTION = " Knock, knock. " ;
- public static final String ANSWER = " Who's there? " ;
- public static void main(String[] args) {
- new Thread( new cli()).start();
- }
- public void run() {
- Socket socket = null ;
- try {
- KeyManagerFactory kmf;
- KeyStore ks;
- TrustManagerFactory tmf;
- SSLContext sslc;
- kmf = KeyManagerFactory.getInstance(ALGORITHM);
- ks = KeyStore.getInstance( " JKS " );
- ks.load( new FileInputStream(KEYSTORE_FILE), PASSWORD.toCharArray());
- kmf.init(ks, PASSWORD.toCharArray());
- tmf = TrustManagerFactory.getInstance(ALGORITHM);
- tmf.init(ks);
- sslc = SSLContext.getInstance( " TLS " );
- sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null );
- // The process is different from here on the client. Instead of
- // getting a ServerSocketFactory, we ask for a SocketFactory from
- // the SSL context.
- SocketFactory sf = sslc.getSocketFactory();
- // Then we get the socket from the factory and treat it
- // as if it were a standard (plain) socket.
- socket = sf.createSocket(HOST, PORT);
- doQuery(socket);
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- finally {
- if (socket != null ) {
- try {
- socket.close();
- }
- catch (IOException e) {
- // oh, well
- }
- }
- System.exit( 0 );
- }
- }
- private void doQuery(Socket s) throws Exception {
- BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(s.getOutputStream()));
- BufferedReader br = new BufferedReader( new InputStreamReader(s.getInputStream()));
- bw.write(QUESTION + " /n " );
- bw.flush();
- String response = br.readLine();
- if ( ! ANSWER.equals(response)) {
- throw new RuntimeException( " Wrong answer: / "" + response + " / "" );
- }
- System.out.println( " Got the right answer: / "" + response + " / "" );
- }
- }
以上方法主要参考了如下两个网页:
http://www.churchillobjects.com/c/11201g.html
http://mark.foster.cc/kb/openssl-keytool.html
原文:http://www.blogjava.net/alwayscy/archive/2008/11/22/85161.html