隨筆-124  評論-194  文章-0  trackbacks-0

          概念
          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?+? " \ "" );
          ??}

          }




          以上方法主要參考了如下兩個網頁:

          http://www.churchillobjects.com/c/11201g.html

          http://mark.foster.cc/kb/openssl-keytool.html

          posted on 2006-12-03 12:36 我愛佳娃 閱讀(11858) 評論(7)  編輯  收藏 所屬分類: SSL

          評論:
          # re: 用OpenSSL與JAVA(JSSE)通信 2008-05-15 21:03 | 不不
          你好,你在另一片文章里這樣寫的:

          公私鑰:公鑰可以唯一解密私鑰加密過的數據,反之亦然。
          SSL過程:需要兩對公私鑰(P1,V1),(P2,V2),假設通信雙方是A和B,B是服務器,A要確認和它通信的是B:
          A->B: hello
          B->A: 用V2加密過的P1(即用戶證書,A就用P2解密出P1)
          A->B: ok
          B->A: 用V1加密的一段信息
          A->B: 用P1加密一個自動生成的K(用之前的P1解密成功這段信息則認為B是可信的了)
          B->A: 用K加密的數據(之后兩對密鑰功能結束,由K來加解密數據)
          這里,P2就是第3方的CA證書,由于非對稱加密很慢,所以公私鑰只是用來保證K的傳送安全,之后通信是用K的對稱加密算法來保證。

          為什么這里是P2是CA證書,而上面的例子里變成了P1是CA證書?我剛接觸SSL不太久,所以不是很懂。抱歉把你06年的東西都搬出來了!  回復  更多評論
            
          # re: 用OpenSSL與JAVA(JSSE)通信 2008-05-16 18:02 | 我愛佳娃
          上文描述的是:
          用OpenSSL生成CA根證書,即(P1,V1)
          在JAVA環境下生成自己的KEY,即(P2,V2)

          這里,P1,V1指代的是CA根證書,所以P1是CA證書,V1是簽名公司需要保密,專門用來給別人簽字的私鑰。

          而,我的另一篇文章提到:
          B->A: 用V2加密過的P1(即用戶證書,A就用P2解密出P1)

          這里,V2相當于CA公司的私鑰,相當于上邊的V1,P2自然就是CA證書了。也即,這兩篇文章在變量指代上正好反了,但內容應該都沒有問題。

            回復  更多評論
            
          # re: 用OpenSSL與JAVA(JSSE)通信 2008-11-20 11:15 | llp20_2000
          @我愛佳娃
          我愛娃娃,按照上文,用OPENSSL的私鑰 ./private/cakey.pem 為javatool的公鑰簽名請求"clientapp.crs"做簽名的時候,也就是執行語句:
          openssl ca -out clientapp.pem -config ./openssl.cnf -infiles clientapp.crs
          結果如下:
          **************************************************
          [~~~~ CA]$ openssl ca -out clientapp.pem -config ./openssl.cnf -infiles clientapp.crs
          Using configuration from ./openssl.cnf
          Enter pass phrase for ./private/cakey.pem:
          Check that the request matches the signature
          Signature ok
          The Subject's Distinguished Name is as follows
          commonName :ASN.1 12:'liangleping'
          organizationName :ASN.1 12:'HuaWei'
          organizationalUnitName:ASN.1 12:'Develop'
          localityName :ASN.1 12:'Cheng Du'
          stateOrProvinceName :ASN.1 12:'Si Chuan'
          countryName :ASN.1 12:'ch'
          The countryName field needed to be the same in the
          CA certificate (ch) and the request (ch)
          ************************************************
          最后出現:The countryName field needed to be the same in the
            回復  更多評論
            
          # re: 用OpenSSL與JAVA(JSSE)通信 2008-11-20 11:18 | llp20_2000
          接樓上,剛才少發了:
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          最后出現:The countryName field needed to be the same in the
          ....CA certificate (ch) and the request (ch)
          并且生成的clientapp.pem大小為0(零)k,因此不能由pem生成dem.不知是何原因?
          我反復對比過兩次輸入的countryName ,并且在javatool中都和Openssl生成時候都輸入全部一樣的公司,組織,城市,國家等,都是一樣的結果.而博主的另一文章,openssl做自簽名,用V1給 P2簽名是可以的.并且此時-infiles為 req.pem.
          新人,不熟悉openssl.請博主幫助
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  回復  更多評論
            
          # re: 用OpenSSL與JAVA(JSSE)通信 2008-11-22 08:24 | 我愛佳娃
          俺也不知為什么了。
          國名好像可以為空的,你試下為空的情況。
          另外,你的提示符為什么會多ASNXXX:commonName :ASN.1 12:
          如果解決了,請貼上來。  回復  更多評論
            
          # re: 用OpenSSL與JAVA(JSSE)通信 2009-02-03 11:25 | Jamie
          那是因為他的openssl.conf里對于CN的設定是match, 而非optional.
          countryName = match

          如下:
          [ policy_match ]
          countryName = match
          stateOrProvinceName = match
          organizationName = match
          organizationalUnitName = optional
          commonName = supplied
          emailAddress = optional

          所以, 必須是匹配的才行.
          你可以改為optional來解決這個問題.  回復  更多評論
            
          # 7867 2009-11-25 05:32 | 周永松
          757  回復  更多評論
            
          主站蜘蛛池模板: 临安市| 延吉市| 永川市| 承德市| 比如县| 兰西县| 蒙阴县| 监利县| 万盛区| 南岸区| 镇沅| 桓台县| 荔浦县| 双辽市| 蒙自县| 白城市| 额尔古纳市| 恩施市| 新田县| 金乡县| 永修县| 平陆县| 长岭县| 平安县| 滦平县| 蕲春县| 大丰市| 丹棱县| 长乐市| 香格里拉县| 皮山县| 交口县| 上蔡县| 渭源县| 娄烦县| 乐清市| 济南市| 沙湾县| 延边| 寿阳县| 灵石县|