Java Android应用程序中的双向SSL不工作

Java Android应用程序中的双向SSL不工作,java,android,ssl,Java,Android,Ssl,我正在尝试创建一个Android应用程序,它使用双向SSL与NodeJS应用程序通信。我有两个版本的代码来发出请求,但两个版本都不起作用。如果我使用纯Java运行此代码的第一个版本,则此代码可以工作,但当我尝试将其拉入Android应用程序时,服务器无法识别客户端证书: 第1版: System.setProperty("javax.net.ssl.keyStore", "jks-keystore.jks"); System.setProperty("javax.net.ssl.k

我正在尝试创建一个Android应用程序,它使用双向SSL与NodeJS应用程序通信。我有两个版本的代码来发出请求,但两个版本都不起作用。如果我使用纯Java运行此代码的第一个版本,则此代码可以工作,但当我尝试将其拉入Android应用程序时,服务器无法识别客户端证书:

第1版:

    System.setProperty("javax.net.ssl.keyStore", "jks-keystore.jks");
    System.setProperty("javax.net.ssl.keyStorePassword", "pass1");
    System.setProperty("javax.net.ssl.trustStore", "jkstruststore.jks");
    System.setProperty("javax.net.ssl.trustStorePassword", "pass2");



    // specify url
    String url = "https://example.com/startup";

    System.out.println("Startup URL is " + url);

    // This block of code keeps self-signed certificates from causing errors.
    javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
        new javax.net.ssl.HostnameVerifier(){
            public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
                return true;
            }
        }
    );

    // initiate the request
    try
    {
        URL hp = new URL(url);
        HttpsURLConnection hpCon = (HttpsURLConnection)hp.openConnection();

        boolean isProxy = hpCon.usingProxy();
        InputStream obj = (InputStream) hpCon.getInputStream();

        // print out JSON response
        System.out.println(convertStreamToString(obj));
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
错误1:

03-17 12:25:18.616: W/System.err(331): javax.net.ssl.SSLHandshakeException:     
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
第2版:

          // load truststore certificate
          InputStream clientTruststoreIs = getResources().openRawResource(R.raw.tsserver);
          KeyStore trustStore = null;
          trustStore = KeyStore.getInstance("BKS");
          trustStore.load(clientTruststoreIs, "pass1".toCharArray());

          System.out.println("Loaded server certificates: " + trustStore.size());

          // initialize trust manager factory with the read truststore
          TrustManagerFactory trustManagerFactory = null;
          trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
          trustManagerFactory.init(trustStore);

          // setup client certificate

          // load client certificate
          InputStream keyStoreStream = getResources().openRawResource(R.raw.tsclient);
          KeyStore keyStore = null;
          keyStore = KeyStore.getInstance("BKS");
          keyStore.load(keyStoreStream, "pass2".toCharArray());

          System.out.println("Loaded client certificates: " + keyStore.size());

          // initialize key manager factory with the read client certificate
          KeyManagerFactory keyManagerFactory = null;
          keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
          keyManagerFactory.init(keyStore, "pass2".toCharArray());


          // initialize SSLSocketFactory to use the certificates
          SSLSocketFactory socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, keyStore, "pass2", trustStore, null, null);

          // Set basic data
          HttpParams params = new BasicHttpParams();
          HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
          HttpProtocolParams.setContentCharset(params, "UTF-8");
          HttpProtocolParams.setUseExpectContinue(params, true);
          HttpProtocolParams.setUserAgent(params, "Android app/1.0.0");

          // Make pool
          ConnPerRoute connPerRoute = new ConnPerRouteBean(12);
          ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
          ConnManagerParams.setMaxTotalConnections(params, 20);

          // Set timeout
          HttpConnectionParams.setStaleCheckingEnabled(params, false);
          HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
          HttpConnectionParams.setSoTimeout(params, 20 * 1000);
          HttpConnectionParams.setSocketBufferSize(params, 8192);

          // Some client params
          HttpClientParams.setRedirecting(params, false);

          // Register http/s schemas!
          SchemeRegistry schReg = new SchemeRegistry();
          schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
          schReg.register(new Scheme("https", socketFactory, 443));
          ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
          DefaultHttpClient sClient = new DefaultHttpClient(conMgr, params);

          try {
                String res = executeHttpGet(sClient, "https://example.com/startup");
                System.out.println("------- SSL RESULT IS = " + res);
            } catch (Exception e) {
                System.out.println("---- ex " + e.getMessage());
                e.printStackTrace();
            }
错误2:服务器看不到客户端证书

这两个代码示例都会导致服务器看不到客户端证书。你知道为什么这不起作用吗?谢谢。

如果我们仔细阅读,我们会发现:

自签名证书会抛出与您所遇到的错误类似的错误。对于服务器来说,信任自签名证书而不发生任何错误并不重要,在任何情况下,它都会抛出SSLHandshakeException

要通过此过程,需要组装一组受信任的CA证书,并将它们添加到您的信任库中。然后,从该CA发出的所有证书都将被信任,这将忽略直接将它们添加到da truststore文件中的需要

现在,如何将CA受信任证书添加到信任库:

建立自己的CA

然后:

给定CA证书cacert.pem,采用pem格式,您可以通过输入以下命令将证书添加到JKS信任库(或创建新的信任库):

keytool -import -file cacert.pem -alias CAAlias -keystore truststore.ts -storepass StorePass 
其中,CAAlias是一个方便的标记,使您能够使用keytool实用程序访问此特定CA证书。文件truststore.ts是一个包含CA证书的密钥库文件。如果该文件不存在,keytool实用程序将创建一个。StorePass密码提供对密钥库文件truststore.t的访问


请注意这一点。

@vzamanillo您发送给我的链接说,当您不信任服务器证书时,通常会发生错误1。但是我的代码已经将受信任证书添加到信任存储中,所以这部分不应该像编写时那样工作吗?谢谢。问题似乎是证书本身。@vzamanillo我已经使用相同的证书在Node中测试了脚本,它工作正常。我还可以用纯Java(而不是Android)运行test#2,它也可以工作。出于某种原因,在Android项目中尝试做同样的事情是行不通的。文档中说,自签名证书也会抛出该错误。它们是否可信并不重要:如果您对服务器使用自签名证书,它将抛出SSLHandshakeException。我建议创建您自己的CA,并将该CA添加到您的信任库中。然后,从该CA发出的所有证书都将被信任,而无需将它们直接添加到da truststore文件中。谢谢!我没有机会测试,但我想给你奖金。赏金已经过期了,所以我必须想办法给你:/在我验证了这一点后,我会开始一个新的赏金并分配给你。我仍然会收到同样的错误。我认为我没有正确设置密钥库。我的理解是信任存储应该只包含CA,对吗?客户端密钥库应该包含客户端密钥和证书?假设这是正确的,我缺少的部分是如何直接使用密钥库和/或证书在代码中正确地发出请求。非常感谢你。如果你能帮我让这项工作,将有100悬赏在它为你:)你不需要任何密钥库只有一个信任库,请阅读文档斯韦尔,我有好消息和坏消息。好消息是我能够从您提供的CA受信任的链接中获得一个工作代码示例。坏消息是,您发送给我的链接仅用于单向SSL,而不是双向SSL。为了让它工作,我需要Android应用程序也发送一个客户端证书和请求。既然我有了CA,你有什么办法吗?非常感谢你。