面对javax.net.ssl.SSLHandshakeException:实现SSL双向身份验证时收到致命警报:bad_certificate问题

时间:2016-04-21 05:04:40

标签: java ssl network-programming openssl keytool

我正在使用自签名证书进行 SSL双向身份验证。 我为客户端(client-keystore.jks)服务器(server-keystore.jks)创建了两个密钥库,从密钥库导出证书并导入客户端证书到服务器密钥库和服务器证书进入客户端密钥库。并在 server.xml 中更新了所需的连接器条目,并将这两个证书添加到了Java信任库 cacerts

Java客户端代码:

KeyStore trustStore = KeyStore.getInstance("JKS", "SUN");
            trustStore.load(SSLImplemetation.class.getResourceAsStream("C:/Program Files/Java/jdk1.7.0_79/jre/lib/security/cacerts"), "changeit".toCharArray());
            String alg = KeyManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
            fac.init(trustStore);


            KeyStore keystore = KeyStore.getInstance("JKS", "SUN");
            keystore.load(SSLImplemetation.class.getResourceAsStream("<dir path>/client-keystore.jks"), "test".toCharArra());
            String keyAlg = KeyManagerFactory.getDefaultAlgorithm();
            KeyManagerFactory keyFac = KeyManagerFactory.getInstance(keyAlg);
            keyFac.init(keystore, "test".toCharArray());

                SSLContext ctx = SSLContext.getInstance("TLS", "SunJSSE");
            ctx.init(keyFac.getKeyManagers(),fac.getTrustManagers(), new SecureRandom());

            SslContextedSecureProtocolSocketFactory secureProtocolSocketFactory = new SslContextedSecureProtocolSocketFactory(ctx);
            Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) secureProtocolSocketFactory, 8443));

            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpPost request = new HttpPost("<rest service url>");

JSONObject obj = new JSONObject();
StringEntity params =new StringEntity(obj.toString());  

     request.addHeader("content-type", "application/json");
     request.setEntity(params);
     HttpResponse response = httpClient.execute(request);
     System.out.println(response.getStatusLine());

server.xml中:

<Connector
       protocol="org.apache.coyote.http11.Http11Protocol"
       port="8443" maxThreads="200" scheme="https" secure="true" SSLEnabled="true"
             clientAuth="true" sslProtocol="TLS"
             keystoreFile="<dir path>/server-keystore.jks" keystorePass="test"
             truststoreFile="C:/Program Files/Java/jdk1.7.0_79/jre/lib/security/cacerts"
             truststorePass="changeit" />

我是SSl的新手,所以有点困惑。 任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

下面的代码对我来说很好,在创建了两个客户端和服务器密钥库并将证书放入java信任库后,我使用了以下代码进行SSL相互认证。

static final private String KEY_STORE = "D:/sslcertificates/client-keystore.jks";
    static final private String KEY_STORE_TYPE = "JKS";
    static final private String KEY_STORE_PASS = "test";

        public static void main(String[] args) throws JSONException{

          try {
              URL url = new URL("<rest service url>");   
              HttpsURLConnection  conn = (HttpsURLConnection) url.openConnection();

conn.setHostnameVerifier(new HostnameVerifier(){

                    public boolean verify(String hostname,
                                          javax.net.ssl.SSLSession sslSession) {
                        if (hostname.equals("<hostname>")) {
                            return true;
                        }
                        return false;
                    }
                });


                conn.setDoOutput(true);
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "application/json");
              try
              {
              KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509","SunJSSE");
              KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE);
              File cert = new File(KEY_STORE);
              InputStream stream = new FileInputStream(cert);
              ks.load(stream, KEY_STORE_PASS.toCharArray());
              stream.close();
              kmf.init(ks,KEY_STORE_PASS.toCharArray());

              SSLContext context = SSLContext.getInstance("TLS");
              context.init(kmf.getKeyManagers(),null, new SecureRandom());            
              SSLSocketFactory factory = context.getSocketFactory();               
              conn.setSSLSocketFactory(factory);

              }
              catch(Exception e)
              {
                  System.out.println(e);
              }

            JSONObject obj = new JSONObject();
             obj.put("id","abc");
             obj.put("name","surya");
             obj.put( "domain","IT");     
             String input=obj.toString();

         //send request to server....            

             OutputStream os = conn.getOutputStream();
             os.write(input.getBytes());
             os.flush();
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    (conn.getInputStream())));

            String output;
            System.out.println("Output from Server .... \n");
            while ((output = br.readLine()) != null) {
                System.out.println(output);
            }
             System.out.println("Response code: "+conn.getResponseCode());
              System.out.println("Response message: "+conn.getResponseMessage());
            conn.disconnect();

          } catch (MalformedURLException e) {

            e.printStackTrace();

          } catch (IOException e) {

            e.printStackTrace();

         }

        }

的Server.xml

 <Connector
   protocol="org.apache.coyote.http11.Http11Protocol"
   port="8443" maxThreads="200" scheme="https" secure="true"   SSLEnabled="true"
   clientAuth="true" sslProtocol="TLS"
    keystoreFile="<dir path>/server-keystore.jks" keystorePass="test"/>

注意:在HttpsURLConnection对象中设置HostnameVerifier,客户端和服务器密钥库的CN应该是主机名。

如果有更好的解决方案,请提出建议。