本文介绍了为 https 使用 Apache httpclient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 tomcat 中启用了 https 并且有一个用于服务器身份验证的自签名证书.我使用 Apache httpClient 创建了一个 http 客户端.我已经设置了一个加载服务器证书的信任管理器.http客户端可以连接服务器没问题.要查看发生了什么,我启用了调试:

System.setProperty("javax.net.debug", "ssl");

我看到了以下我完全无法理解的内容:

***添加为受信任的证书:主题:CN=Me,OU=MyHouse,O=Home,L=X,ST=X,C=BB发行人:CN=Me,OU=MyHouse,O=Home,L=X,ST=X,C=BB算法:RSA;序列号:0x4d72356b有效期从 2011 年 3 月 5 日星期六 15:06:51 EET 到 2011 年 6 月 3 日星期五 16:06:51 EET

显示我的证书并添加到信任库(如我所见).然后:

 触发 SecureRandom 的种子完成播种 SecureRandom

这是我没有得到的调试跟踪部分:

trustStore 是:C:Program FilesJavajre6libsecuritycacertstrustStore 类型是:jkstrustStore 提供者是:初始化信任库添加为受信任的证书:主题:CN=SwissSign Platinum CA - G2,O=SwissSign AG,C=CH发行人:CN=SwissSign Platinum CA - G2,O=SwissSign AG,C=CH算法:RSA;序列号:0x4eb200670c035d4f有效期为 2006 年 10 月 25 日星期三 11:36:00 EEST 至 2036 年 10 月 25 日星期六 11:36:00 EEST添加为受信任的证书:主题:[email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert 验证网络颁发者:[email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network算法:RSA;序列号:0x1有效期从 1999 年 6 月 26 日星期六 01:23:48 EEST 到 2019 年 6 月 26 日星期三 01:23:48 EEST

好像也用了默认的java信任库!我的问题是为什么会发生这种情况?

在我的代码中,我明确指定了要使用的特定信任库(通过 truststoremanagers).我期待只使用这个.似乎同时使用了我的信任库和 java 的默认值.这是它应该如何工作?

更新:
我尝试了以下方法:

System.out.println("TMF 编号:"+tmf.getTrustManagers().length);System.out.println("类是"+tmf.getTrustManagers()[0].getClass().getName());

我认为我应该看到 2 个信任管理器,因为 2 个密钥库(似乎使用了我的和 java 的默认值).
但结果只有1个信任经理!

TMF No:1类是 com.sun.net.ssl.internal.ssl.X509TrustManagerImpl

UPDATE2: 正如您在下面的代码中看到的,我指定了我的密钥库.我的期望是只应该使用它(而不是这个 cacert)>

 HttpClient client = new DefaultHttpClient();SSLContext sslContext = SSLContext.getInstance("TLS");TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());KeyStore ks = KeyStore.getInstance("JKS");File trustFile = new File("clientTrustStore.jks");ks.load(new FileInputStream(trustFile), null);tmf.init(ks);sslContext.init(null, tmf.getTrustManagers(),null);SSLSocketFactory sf = 新 SSLSocketFactory(sslContext);sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);Scheme scheme = new Scheme("https", sf, 443);client.getConnectionManager().getSchemeRegistry().register(scheme);httpGet = new HttpGet("https://localhost:8443/myApp");HttpResponse httpResponse = client.execute(httpGet);

对我来说没有意义.

解决方案

我将这个测试应用放在一起,使用 Apache HttpClient 包中的 HTTP 测试框架重现问题:

ClassLoader cl = HCTest.class.getClassLoader();URL url = cl.getResource("test.keystore");KeyStore keystore = KeyStore.getInstance("jks");char[] pwd = "nopassword".toCharArray();keystore.load(url.openStream(), pwd);TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(密钥库);TrustManager[] tm = tmf.getTrustManagers();KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmfactory.init(keystore, pwd);KeyManager[] km = kmfactory.getKeyManagers();SSLContext sslcontext = SSLContext.getInstance("TLS");sslcontext.init(km, tm, null);LocalTestServer localServer = new LocalTestServer(sslcontext);localServer.registerDefaultHandlers();localServer.start();尝试 {DefaultHttpClient httpclient = new DefaultHttpClient();TrustStrategy trustStrategy = new TrustStrategy() {public boolean isTrusted(X509Certificate[] chain, String authType) 抛出 CertificateException {对于(X509Certificate 证书:链){System.err.println(cert);}返回假;}};SSLSocketFactory sslsf = new SSLSocketFactory("TLS", null, null, keystore, null,trustStrategy, new AllowAllHostnameVerifier());Scheme https = new Scheme("https", 443, sslsf);httpclient.getConnectionManager().getSchemeRegistry().register(https);InetSocketAddress 地址 = localServer.getServiceAddress();HttpHost target1 = new HttpHost(address.getHostName(), address.getPort(), "https");HttpGet httpget1 = new HttpGet("/random/100");HttpResponse response1 = httpclient.execute(target1, httpget1);System.err.println(response1.getStatusLine());HttpEntity entity1 = response1.getEntity();EntityUtils.consume(entity1);HttpHost target2 = new HttpHost("www.verisign.com", 443, "https");HttpGet httpget2 = new HttpGet("/");HttpResponse response2 = httpclient.execute(target2, httpget2);System.err.println(response2.getStatusLine());HttpEntity entity2 = response2.getEntity();EntityUtils.consume(entity2);} 最后 {localServer.stop();}

尽管出于某种原因,Sun 的 JSSE 实现似乎总是从默认信任存储中读取信任材料,但它似乎并未添加到 SSL 上下文中,也不会影响 SSL 握手期间的信任验证过程.

这是测试应用的输出.如您所见,第一个请求成功,而第二个请求失败,因为到 www.verisign.com 的连接因不受信任而被拒绝.

[[版本:V1主题:CN=Simple Test Http Server,OU=Jakarta HttpClient 项目,O=Apache Software Foundation,L=Unknown,ST=Unknown,C=Unknown签名算法:SHA1withDSA,OID = 1.2.840.10040.4.3密钥:Sun DSA 公钥参数:DSAp: fd7f5381 1d751229 52df4a9c 2eece4e7 f611b752 3cef4400 c31e3f80 b6512669455d4022 51fb593d 8d58fabf c5f5ba30 f6cb9b55 6cd7813b 801d346f f26660b76b9950a5 a49f9fe8 047b1022 c24fbba9 d7feb7c6 1bf83b57 e7c6a8a6 150f04fb83f6d3c5 1ec30235 54135a16 9132f675 f3ae2b61 d72aeff2 2203199d d14801c7q: 9760508f 15230bcc b292b982 a2eb840b f0581cf5g: f7e1a085 d69b3dde cbbcab5c 36b857b9 7994afbb fa3aea82 f9574c0b 3d0782675159578e bad4594f e6710710 8180b449 167123e8 4c281613 b7cf0932 8cc8a6e13c167a8b 547c8d28 e0a3ae1e 2bb3a675 916ea37f 0bfa2135 62f1fb62 7a01243bcca4f1be a8519089 a883dfe1 5ae59f06 928b665e 807b5525 64014c3b fecf492ay:f0cc639f 702fd3b1 03fa8fa6 676c3756 ea505448 23cd1147 fdfa2d7f 662f7c59a02ddc1a fd76673e 25210344 cebbc0e7 6250fff1 a814a59f 30ff5c7e c4f186d8f0fd346c 29ea270d b054c040 c74a9fc0 55a7020f eacf9f66 a0d86d04 4f4d23de7f1d681f 45c4c674 5762b71b 808ded17 05b74baf 8de3c4ab 2ef662e3 053af09e有效期:[自:CET 2004 年 12 月 11 日星期六 14:48:35,至:欧洲中部时间 2014 年 12 月 9 日星期二 14:48:35]颁发者:CN=Simple Test Http Server,OU=Jakarta HttpClient Project,O=Apache Software Foundation,L=Unknown,ST=Unknown,C=Unknown序列号:[41bafab3]]算法:[SHA1withDSA]签名:0000: 30 2D 02 15 00 85 BE 6B D0 91 EF 34 72 05 FF 1A 0-.....k...4r...0010:DB F6 DE BF 92 53 9B 14 27 02 14 37 8D E8 CB AC .....S..'..7..0020: 4E 6C 93 F2 1F 7D 20 A1 2D 6F 80 5F 58 AE 33 Nl....-o._X.3]HTTP/1.1 200 正常[[版本:V3主题:CN=www.verisign.com, OU="Production Security Services", O="VeriSign, Inc.", STREET=487 East Middlefield Road, L=Mountain View, ST=California, OID.2.5.4.17=94043, C=US, SERIALNUMBER=2497886, OID.2.5.4.15="V1.0, 第 5.(b) 条", OID.1.3.6.1.4.1.311.60.2.1.2=特拉华州, OID.1.3.6.1.4.1.311.60.2.1.3=美国签名算法:SHA1withRSA,OID = 1.2.840.113549.1.1.5密钥:Sun RSA 公钥,2048 位模量:20699622354183393041832954221256409980425015218949582822286196083815087464214375375678538878841956356687753084333860738385445545061253653910861690581771234068858443439641948884498053425403458465980515883570440998475638309355278206558031134532548167239684215445939526428677429035048018486881592078320341210422026566944903775926801017506416629554190534665876551381066249522794321313235316733139718653035476771717662585319643139144923795822646805045585537550376512087897918635167815735560529881178122744633480557211052246428978388768010050150525266771462988042507883304193993556759733514505590387262811565107773578140271公共指数:65537有效期:[自:CEST 2010 年 5 月 26 日星期三 02:00:00,至:2012 年 5 月 26 日星期六 01:59:59 CEST]发行人:CN=VeriSign Class 3 Extended Validation SSL SGC CA,OU=https://www.verisign.com/rpa (c)06 上的使用条款,OU=VeriSign Trust Network,O="VeriSign, Inc.",C=美国序列号:[53d2bef9 24a7245e 83ca01e4 6caa2477]证书扩展:10[1]: ObjectId: 1.3.6.1.5.5.7.1.1 严重性=false权限信息访问 [[访问方法:1.3.6.1.5.5.7.48.1accessLocation: URIName: http://EVIntl-ocsp.verisign.com, accessMethod: 1.3.6.1.5.5.7.48.2accessLocation: URIName: http://EVIntl-aia.verisign.com/EVIntl2006.cer]]...]线程main"中的异常 javax.net.ssl.SSLPeerUnverifiedException: peer 未经过身份验证在 com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:345)在 org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)在 org.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:446)...

I have enabled https in tomcat and have a self-signed certificate for server auth. I have created an http client using Apache httpClient. I have set a trust manager loading the server certificate. The http client can connect with server no problem. To see what is going on I enabled debugging:

System.setProperty("javax.net.debug", "ssl");

I saw the following which I can not understand at all:

***
adding as trusted cert:
  Subject: CN=Me, OU=MyHouse, O=Home, L=X, ST=X, C=BB
  Issuer:  CN=Me, OU=MyHouse, O=Home, L=X, ST=X, C=BB
  Algorithm: RSA; Serial number: 0x4d72356b
  Valid from Sat Mar 05 15:06:51 EET 2011 until Fri Jun 03 16:06:51 EEST 2011

My certificate is displayed and is added to truststore (as I see). Then:

trigger seeding of SecureRandom
done seeding SecureRandom

Here is the part from debugging traces I do not get:

trustStore is: C:Program FilesJavajre6libsecuritycacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
  Subject: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
  Issuer:  CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
  Algorithm: RSA; Serial number: 0x4eb200670c035d4f
  Valid from Wed Oct 25 11:36:00 EEST 2006 until Sat Oct 25 11:36:00 EEST 2036

adding as trusted cert:
  Subject: [email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network
  Issuer:  [email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network
  Algorithm: RSA; Serial number: 0x1
  Valid from Sat Jun 26 01:23:48 EEST 1999 until Wed Jun 26 01:23:48 EEST 2019

It seems that it also uses the default java trust store! My question is why does this happen?

In my code I specify explicitly a specific trust-store to use (via truststoremanagers). I was expecting only this to be used. It seems that both my truststore and java's default is being used. Is this how it is supposed to work?

UPDATE:
I tried the following:

System.out.println("TMF No:"+tmf.getTrustManagers().length);
System.out.println("Class is "+tmf.getTrustManagers()[0].getClass().getName());

I thought that I should see 2 trust managers, since 2 keystores (mine and java's default appear to be used).
But the result was only 1 trust manager!

TMF No:1
Class is com.sun.net.ssl.internal.ssl.X509TrustManagerImpl

UPDATE2: As you see in the code bellow I specify my keystore.My expectation is that only this should be used (not this and cacert as well)

    HttpClient client = new DefaultHttpClient();
    SSLContext sslContext = SSLContext.getInstance("TLS");

    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore ks = KeyStore.getInstance("JKS");
    File trustFile = new File("clientTrustStore.jks");
    ks.load(new FileInputStream(trustFile), null);
    tmf.init(ks);
    sslContext.init(null, tmf.getTrustManagers(),null);
    SSLSocketFactory sf = new SSLSocketFactory(sslContext);
    sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    Scheme scheme = new Scheme("https", sf, 443);
    client.getConnectionManager().getSchemeRegistry().register(scheme);
    httpGet = new HttpGet("https://localhost:8443/myApp");
    HttpResponse httpResponse = client.execute(httpGet);

Does not make sense to me.

解决方案

I put together this test app to reproduce the issue using the HTTP testing framework from the Apache HttpClient package:

ClassLoader cl = HCTest.class.getClassLoader();
URL url = cl.getResource("test.keystore");
KeyStore keystore  = KeyStore.getInstance("jks");
char[] pwd = "nopassword".toCharArray();
keystore.load(url.openStream(), pwd);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
TrustManager[] tm = tmf.getTrustManagers();

KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
        KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keystore, pwd);
KeyManager[] km = kmfactory.getKeyManagers();

SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(km, tm, null);

LocalTestServer localServer = new LocalTestServer(sslcontext);
localServer.registerDefaultHandlers();

localServer.start();
try {

    DefaultHttpClient httpclient = new DefaultHttpClient();
    TrustStrategy trustStrategy = new TrustStrategy() {

        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            for (X509Certificate cert: chain) {
                System.err.println(cert);
            }
            return false;
        }

    };

    SSLSocketFactory sslsf = new SSLSocketFactory("TLS", null, null, keystore, null,
            trustStrategy, new AllowAllHostnameVerifier());
    Scheme https = new Scheme("https", 443, sslsf);
    httpclient.getConnectionManager().getSchemeRegistry().register(https);

    InetSocketAddress address = localServer.getServiceAddress();
    HttpHost target1 = new HttpHost(address.getHostName(), address.getPort(), "https");
    HttpGet httpget1 = new HttpGet("/random/100");
    HttpResponse response1 = httpclient.execute(target1, httpget1);
    System.err.println(response1.getStatusLine());
    HttpEntity entity1 = response1.getEntity();
    EntityUtils.consume(entity1);
    HttpHost target2 = new HttpHost("www.verisign.com", 443, "https");
    HttpGet httpget2 = new HttpGet("/");
    HttpResponse response2 = httpclient.execute(target2, httpget2);
    System.err.println(response2.getStatusLine());
    HttpEntity entity2 = response2.getEntity();
    EntityUtils.consume(entity2);
} finally {
    localServer.stop();
}

Even though, Sun's JSSE implementation appears to always read the trust material from the default trust store for some reason, it does not seem to get added to the SSL context and to impact the process of trust verification during the SSL handshake.

Here's the output of the test app. As you can see, the first request succeeds whereas the second fails as the connection to www.verisign.com is rejected as untrusted.

[
[
  Version: V1
  Subject: CN=Simple Test Http Server, OU=Jakarta HttpClient Project, O=Apache Software Foundation, L=Unknown, ST=Unknown, C=Unknown
  Signature Algorithm: SHA1withDSA, OID = 1.2.840.10040.4.3

  Key:  Sun DSA Public Key
    Parameters:DSA
    p:     fd7f5381 1d751229 52df4a9c 2eece4e7 f611b752 3cef4400 c31e3f80 b6512669
    455d4022 51fb593d 8d58fabf c5f5ba30 f6cb9b55 6cd7813b 801d346f f26660b7
    6b9950a5 a49f9fe8 047b1022 c24fbba9 d7feb7c6 1bf83b57 e7c6a8a6 150f04fb
    83f6d3c5 1ec30235 54135a16 9132f675 f3ae2b61 d72aeff2 2203199d d14801c7
    q:     9760508f 15230bcc b292b982 a2eb840b f0581cf5
    g:     f7e1a085 d69b3dde cbbcab5c 36b857b9 7994afbb fa3aea82 f9574c0b 3d078267
    5159578e bad4594f e6710710 8180b449 167123e8 4c281613 b7cf0932 8cc8a6e1
    3c167a8b 547c8d28 e0a3ae1e 2bb3a675 916ea37f 0bfa2135 62f1fb62 7a01243b
    cca4f1be a8519089 a883dfe1 5ae59f06 928b665e 807b5525 64014c3b fecf492a

  y:
    f0cc639f 702fd3b1 03fa8fa6 676c3756 ea505448 23cd1147 fdfa2d7f 662f7c59
    a02ddc1a fd76673e 25210344 cebbc0e7 6250fff1 a814a59f 30ff5c7e c4f186d8
    f0fd346c 29ea270d b054c040 c74a9fc0 55a7020f eacf9f66 a0d86d04 4f4d23de
    7f1d681f 45c4c674 5762b71b 808ded17 05b74baf 8de3c4ab 2ef662e3 053af09e

  Validity: [From: Sat Dec 11 14:48:35 CET 2004,
               To: Tue Dec 09 14:48:35 CET 2014]
  Issuer: CN=Simple Test Http Server, OU=Jakarta HttpClient Project, O=Apache Software Foundation, L=Unknown, ST=Unknown, C=Unknown
  SerialNumber: [    41bafab3]

]
  Algorithm: [SHA1withDSA]
  Signature:
0000: 30 2D 02 15 00 85 BE 6B   D0 91 EF 34 72 05 FF 1A  0-.....k...4r...
0010: DB F6 DE BF 92 53 9B 14   27 02 14 37 8D E8 CB AC  .....S..'..7....
0020: 4E 6C 93 F2 1F 7D 20 A1   2D 6F 80 5F 58 AE 33     Nl.... .-o._X.3

]
HTTP/1.1 200 OK
[
[
  Version: V3
  Subject: CN=www.verisign.com, OU=" Production Security Services", O="VeriSign, Inc.", STREET=487 East Middlefield Road, L=Mountain View, ST=California, OID.2.5.4.17=94043, C=US, SERIALNUMBER=2497886, OID.2.5.4.15="V1.0, Clause 5.(b)", OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 2048 bits
  modulus: 20699622354183393041832954221256409980425015218949582822286196083815087464214375375678538878841956356687753084333860738385445545061253653910861690581771234068858443439641948884498053425403458465980515883570440998475638309355278206558031134532548167239684215445939526428677429035048018486881592078320341210422026566944903775926801017506416629554190534665876551381066249522794321313235316733139718653035476771717662585319643139144923795822646805045585537550376512087897918635167815735560529881178122744633480557211052246428978388768010050150525266771462988042507883304193993556759733514505590387262811565107773578140271
  public exponent: 65537
  Validity: [From: Wed May 26 02:00:00 CEST 2010,
               To: Sat May 26 01:59:59 CEST 2012]
  Issuer: CN=VeriSign Class 3 Extended Validation SSL SGC CA, OU=Terms of use at https://www.verisign.com/rpa (c)06, OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
  SerialNumber: [    53d2bef9 24a7245e 83ca01e4 6caa2477]

Certificate Extensions: 10
[1]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [accessMethod: 1.3.6.1.5.5.7.48.1
   accessLocation: URIName: http://EVIntl-ocsp.verisign.com, accessMethod: 1.3.6.1.5.5.7.48.2
   accessLocation: URIName: http://EVIntl-aia.verisign.com/EVIntl2006.cer]
]

...

]
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:345)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
    at org.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:446)
...

这篇关于为 https 使用 Apache httpclient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 18:28