我必须能够在后端系统的配置文件(不是系统密钥库,而是用户配置)中指定用于验证 HTTPS 连接的证书和密钥,并尝试了以下方法:
- 首先将用户证书和私钥编码为 config.xml 中 Base64 编码的 PKCS#12 文件。(我已从
openssl pkcs12
命令中导出信息并转换为 base64 格式,以便能够从硬连线String
实例中读取它) InputStream
从from a中读取该信息Base64.Decode
并输入到KeyStore#load()
方法。(信息被正确读取,KeyStore
实例存储实际用户信息,已检查)- 在(来自库)中指定它
KeyStore
作为关键信息的来源(这是我认为不正确的做法)SSLContext.Builder
org.apache.http.conn.ssl
- 发出
HTTPS
调用以访问客户端身份验证服务器,该服务器根据客户端提供的证书对客户端进行身份验证。
用于测试连接的代码如下所示:
/**
* $Id: $
*
* Copyright (C) 2017 BaseN.
*
* All rights reserved.
*/
package net.basen.testing;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Base64;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
/**
* Tester for https connections.
*/
public class CheckSSLAccess {
/**
* Main program
* @param args
* @throws KeyStoreException
* @throws IOException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws UnrecoverableKeyException
*/
public static void main( String[] args ) {
String ksContents = /* Keystore contents altered intentionally, but correctly reflect a private key and certificate */
"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";
try {
InputStream is = Base64.getMimeDecoder().wrap( new ByteArrayInputStream( ksContents.getBytes() ) );
KeyStore kst = KeyStore.getInstance( "pkcs12" );
kst.load( is, null );
KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() )
.init( kst, null );
SSLContext.setDefault( SSLContexts.custom()
.loadTrustMaterial(kst)
.loadKeyMaterial( kst, null )
.build() );
} catch( Exception e ) {
e.printStackTrace();
return;
}
try {
CloseableHttpClient cl = HttpClients.createDefault();
/* server address has been also altered to protect access privacy */
HttpGet get = new HttpGet( "https://www.server.com/autheticated_url" );
CloseableHttpResponse resp = cl.execute( get );
System.out.println(resp.getStatusLine());
Header[] headers = resp.getAllHeaders();
for (int i = 0; i < headers.length; i++)
System.out.println(headers[i]);
System.out.println();
InputStream in = resp.getEntity().getContent();
byte[] buffer = new byte[1024];
int n;
while ((n = in.read(buffer)) > 0) {
System.out.write( buffer, 0, n );
}
if (n < 0) {
System.err.println( "n == " + n );
}
} catch( Exception e ) {
e.printStackTrace();
return;
}
}
} /* CheckSSLAccess */
当与服务器协商凭据时,库发出以下异常,SSL
就像没有使用用户证书一样。我不确切知道此代码中缺少什么,以便客户端选择正确的证书并与服务器正确协商。
抱歉,但由于项目问题,示例中显示的证书已更改为新生成的证书,以及要访问的服务器的 URL。
javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
at sun.security.ssl.ClientHandshaker.handshakeAlert(ClientHandshaker.java:1438)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2016)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at net.basen.testing.CheckSSLAccess.main(CheckSSLAccess.java:109)
问题是
在初始化中应该缺少SSLContext
什么来选择(或链接SSLContext
到实际的 HTTPS 调用)HTTPS 调用中的KeyStore
凭据?
编辑
在激活有问题的选项和调试模式后,在 ssl 握手中永远不会考虑用户证书,所以恐怕这个问题与所提到的无关。出于某种原因,服务器证书交换中的 hanshake 停止并且客户端证书永远不会交换:
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
[...]
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
[...]
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1.1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 for TLSv1.1
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1471524485 bytes = { 109, 133, 165, 223, 114, 170, 197, 226, 115, 115, 12, 46, 74, 145, 95, 17, 242, 119,
57, 167, 76, 6, 147, 14, 32, 63, 62, 177 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256,
[...]
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1,
sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1,
secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA,
SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
***
[write] MD5 and SHA1 hashes: len = 195
0000: 01 00 00 BF 03 03 58 B6 AF 85 6D 85 A5 DF 72 AA ......X...m...r.
[...]
00B0: 03 05 01 04 03 04 01 03 03 03 01 02 03 02 01 02 ................
00C0: 02 01 01 ...
main, WRITE: TLSv1.2 Handshake, length = 195
[Raw write]: length = 200
0000: 16 03 03 00 C3 01 00 00 BF 03 03 58 B6 AF 85 6D ...........X...m
[...]
00B0: 06 03 06 01 05 03 05 01 04 03 04 01 03 03 03 01 ................
00C0: 02 03 02 01 02 02 01 01 ........
[Raw read]: length = 5
0000: 16 03 01 00 31 ....1
[Raw read]: length = 49
0000: 02 00 00 2D 03 01 58 B6 AF 85 5B 50 5E 9A C0 D5 ...-..X...[P^...
0010: 74 38 A3 6F F8 14 C1 F1 0E 35 E0 D8 66 4A 13 23 t8.o.....5..fJ.#
0020: 1E BC 39 EE 70 8D 00 00 2F 00 00 05 FF 01 00 01 ..9.p.../.......
0030: 00 .
main, READ: TLSv1 Handshake, length = 49
*** ServerHello, TLSv1
RandomCookie: GMT: 1471524485 bytes = { 91, 80, 94, 154, 192, 213, 116, 56, 163, 111, 248, 20, 193, 241, 14, 53, 224, 216, 102,
74, 19, 35, 30, 188, 57, 238, 112, 141 }
Session ID: {}
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
** TLS_RSA_WITH_AES_128_CBC_SHA
[read] MD5 and SHA1 hashes: len = 49
0000: 02 00 00 2D 03 01 58 B6 AF 85 5B 50 5E 9A C0 D5 ...-..X...[P^...
0010: 74 38 A3 6F F8 14 C1 F1 0E 35 E0 D8 66 4A 13 23 t8.o.....5..fJ.#
0020: 1E BC 39 EE 70 8D 00 00 2F 00 00 05 FF 01 00 01 ..9.p.../.......
0030: 00 .
[Raw read]: length = 5
0000: 16 03 01 08 5F ...._
[Raw read]: length = 2143
0000: 0B 00 08 5B 00 08 58 00 04 AC 30 82 04 A8 30 82 ...[..X...0...0.
0010: 03 90 A0 03 02 01 02 02 01 13 30 0D 06 09 2A 86 ..........0...*.
0020: 48 86 F7 0D 01 01 05 05 00 30 81 92 31 0B 30 09 H........0..1.0.
[...]
0810: 3F 7F 8E 25 F0 6C 0E 10 75 D9 D6 C6 51 77 AE 1D ?..%.l..u...Qw..
0820: 96 0A E9 72 1B 65 2F C5 E4 96 20 67 0E 75 76 E0 ...r.e/... g.uv.
0830: D5 42 27 4B 81 22 15 C4 F9 7E A2 78 24 91 A4 A2 .B'K.".....x$...
0840: A7 96 1A B4 D0 D7 88 94 B5 36 B7 B3 19 78 37 E1 .........6...x7.
0850: C2 2E 28 61 D4 E0 4B 42 20 27 6C 99 40 A2 8E ..(a..KB 'l.@..
main, READ: TLSv1 Handshake, length = 2143
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: EMAILADDRESS=admin@actility.com, CN=rms.poc1.actility.com, OU=Actility, O=Actility, ST=FRANCE, C=FR
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
modulus:
2292398721841540[...]592209023
public exponent: 65537
Validity: [From: Thu Apr 03 15:25:07 CEST 2014,
To: Wed Mar 29 15:25:07 CEST 2034]
Issuer: EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR
SerialNumber: [ 13]
Certificate Extensions: 4
[1]: ObjectId: 2.16.840.1.113730.1.13 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 1F 16 1D 4F 70 65 6E 53 53 4C 20 47 65 6E 65 ....OpenSSL Gene
0010: 72 61 74 65 64 20 43 65 72 74 69 66 69 63 61 74 rated Certificat
0020: 65 e
[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
[EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR]
SerialNumber: [ 9d5d3f4c 2ee0bcd9]
]
[3]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
[4]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 0B 82 48 98 6B E2 66 02 2E 30 7E 3C 0E 55 18 9E ..H.k.f..0.<.U..
0010: DF 78 BC E9 .x..
]
]
]
Algorithm: [SHA1withRSA]
Signature:
0000: C7 E2 0C 87 AE AF 97 E8 DD 9C 9F B5 1E C0 48 AA ..............H.
[...]
00E0: 10 AB 42 B8 C2 F9 42 C8 BC 60 99 12 FE A1 CE 9F ..B...B..`......
00F0: 2C 9A 66 DF AE 03 88 61 9A 24 0B 1A D2 89 22 EA ,.f....a.$....".
]
chain [1] = [
[
Version: V1
Subject: EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
modulus:
2828950521283455293132606[...]047773313891689356150673882060816455169139045423472889
public exponent: 65537
Validity: [From: Thu Apr 03 15:23:19 CEST 2014,
To: Wed Mar 29 15:23:19 CEST 2034]
Issuer: EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR
SerialNumber: [ 9d5d3f4c 2ee0bcd9]
]
Algorithm: [SHA1withRSA]
Signature:
0000: A7 FB 5B 7F 90 9D 31 85 CC CA 1D 19 C5 B6 34 2A ..[...1.......4*
[...]
00F0: E1 C2 2E 28 61 D4 E0 4B 42 20 27 6C 99 40 A2 8E ...(a..KB 'l.@..
]
***
%% Invalidated: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 2E .......
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building
failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
[....]
java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 26 more