用OpenSSL与JAVA(JSSE)通信

概念
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();
    }

  }

}


CLI端程序:

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 +  " / "" );
  }

}


你可能感兴趣的:(用OpenSSL与JAVA(JSSE)通信)