Rya*_*yan 6 java apache ssl jsse
我编写了一个Java客户端应用程序,它使用客户端证书通过HTTPS连接到Apache Web服务器,并对服务器执行文件的HTTP PUT.它适用于小文件,但与大文件崩溃.
Apache服务器日志显示以下内容:
...
OpenSSL: Handshake: done
...
Changed client verification type will force renegotiation
...
filling buffer, max size 131072 bytes
...
request body exceeds maximum size (131072) for SSL buffer
could not buffer message body to allow SSL renegotiation to proceed
...
OpenSSL: I/O error, 5 bytes expected to read on BIO
(104)Connection reset by peer: SSL input filter read failed.
(32)Broken pipe: core_output_filter: writing data to the network
Connection closed to child 20 with standard shutdown
Run Code Online (Sandbox Code Playgroud)
客户的回应是:
java.io.IOException: Server returned HTTP response code: 401 for URL
Run Code Online (Sandbox Code Playgroud)
我不熟悉这个过程所以我不确定这里是否需要重新谈判,或者我是否可以做些什么来阻止它.或者也许我可以让客户端等到重新协商完成后再发送应用程序数据?以下是客户端代码的摘录(删除了错误处理):
URL url = new URL("my url goes here");
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getMyCustomClientCertSocketFactory());
con.setRequestMethod("PUT");
con.setDoOutput(true);
con.connect();
writer = new OutputStreamWriter(con.getOutputStream());
writer.write(xml);
writer.close();
parseServerResponse(con.getInputStream());
Run Code Online (Sandbox Code Playgroud)
我想也许我需要使用像SSLSocket这样的低级API并利用HandshakeCompletedListener?
我也想知道Apache SSLVerifyDepth指令是否与重新协商的原因有关.我在每个目录上下文(只有一个上传目录)中获得了值为2的指令,Apache手册说明了这一点:
在每个目录上下文中,它会在读取HTTP请求之后但在发送HTTP响应之前强制使用重新配置的客户端验证深度进行SSL重新协商.
这里要求的是Java调试输出:
keyStore is :
keyStore type is : jks
keyStore provider is :
init keystore
init keymanager of type SunX509
trustStore is: C:\Program Files\Java\jdk1.6.0_35\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
...
trigger seeding of SecureRandom
done seeding SecureRandom
***
found key for : key-alias
chain [0] = [
[
...
]
***
trigger seeding of SecureRandom
done seeding SecureRandom
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1
RandomCookie: ...
Session ID: {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
***
main, WRITE: TLSv1 Handshake, length = 75
main, WRITE: SSLv2 client hello message, length = 101
main, READ: TLSv1 Handshake, length = 81
*** ServerHello, TLSv1
RandomCookie: ...
Session ID: ...
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Created: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
** TLS_RSA_WITH_AES_128_CBC_SHA
main, READ: TLSv1 Handshake, length = 4392
*** Certificate chain
chain [0] = [
[
...
Certificate Extensions: 8
[1]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: ...
accessLocation: URIName: ...
accessMethod: ...
accessLocation: URIName: ...
]
[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
...
]
]
[3]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
[4]: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: ...
]]
[5]: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
[CertificatePolicyId: ...
[PolicyQualifierInfo: [
qualifierID: ...
qualifier: ...
]] ]
]
[6]: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
clientAuth
]
[7]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_Encipherment
]
[8]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: ...
]
]
Algorithm: [SHA1withRSA]
Signature:
...
]
...
***
main, READ: TLSv1 Handshake, length = 4
*** ServerHelloDone
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
main, WRITE: TLSv1 Handshake, length = 518
SESSION KEYGEN:
PreMaster Secret:
...
CONNECTION KEYGEN:
Client Nonce:
...
Server Nonce:
...
Master Secret:
...
Client MAC write Secret:
...
Server MAC write Secret:
...
Client write key:
...
Server write key:
...
Client write IV:
...
Server write IV:
...
main, WRITE: TLSv1 Change Cipher Spec, length = 1
*** Finished
verify_data: { 18, 162, 18, 251, 82, 111, 87, 133, 53, 240, 114, 155 }
***
main, WRITE: TLSv1 Handshake, length = 48
main, READ: TLSv1 Change Cipher Spec, length = 1
main, READ: TLSv1 Handshake, length = 48
*** Finished
verify_data: { 46, 206, 8, 40, 63, 252, 99, 190, 251, 183, 110, 201 }
***
%% Cached client session: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
main, WRITE: TLSv1 Application Data, length = 256
main, WRITE: TLSv1 Application Data, length = 32
main, WRITE: TLSv1 Application Data, length = 16416
main, WRITE: TLSv1 Application Data, length = 16416
...
main, WRITE: TLSv1 Application Data, length = 16416
main, WRITE: TLSv1 Application Data, length = 16416
main, WRITE: TLSv1 Application Data, length = 512
main, READ: TLSv1 Application Data, length = 304
Run Code Online (Sandbox Code Playgroud)
这里要求的是getMyCustomClientCertSocketFactory源(从PEM文件获取证书和密钥):
public static SSLSocketFactory getMyCustomClientCertSocketFactory(String pemPath,
boolean verifyPeer)
throws NoSuchAlgorithmException, FileNotFoundException, IOException,
KeyStoreException, CertificateException, UnrecoverableKeyException,
KeyManagementException, InvalidKeySpecException {
SSLContext context = SSLContext.getInstance("TLS");
byte[] certAndKey = IOUtil.fileToBytes(new File(pemPath));
byte[] certBytes = parseDERFromPEM(certAndKey,
"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(certAndKey,
"-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateX509CertificateFromDER(certBytes);
RSAPrivateKey key = generateRSAPrivateKeyFromDER(keyBytes);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(),
new Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, "changeit".toCharArray());
KeyManager[] km = kmf.getKeyManagers();
TrustManager[] tm = null;
if (!verifyPeer) {
tm = new TrustManager[]{new TrustyTrustManager()};
}
context.init(km, tm, null);
return context.getSocketFactory();
}
Run Code Online (Sandbox Code Playgroud)
似乎Sun Java中内置的HttpsUrlConnection工具无法以服务器友好的方式处理具有客户端证书方案的大型HTTP PUT(即,不会使服务器SSL重新协商缓冲区溢出).
我检查了curl正在做什么来查看"服务器友好意味着什么",结果发现有一个名为"Expect"的HTTP 1.1标头,其卷曲发送值为"100-continue"(参见规范http://www.w3) .org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20).这个标题基本上说"我有一个巨大的有效载荷,但在我发送它之前请告诉我你是否可以处理它".这使端点有时间在发送有效负载之前重新协商客户端证书.
在Sun HttpUrlConnection实现中,似乎不允许此标头,并且实际上是在受限标头列表中; 意思是即使您使用HttpUrlConnection.setRequestProperty方法设置它,标头也不会实际发送到服务器.您可以使用系统属性sun.net.http.allowRestrictedHeaders覆盖受限制的标头,但是客户端只是因为套接字异常而崩溃,因为Sun实现不知道如何处理这部分协议.
有趣的是,似乎Java的OpenJDK实现确实支持这个头.此外,Apache HTTP Client库支持此标头(http://hc.apache.org/); 我已经使用Apache HTTP客户端库实现了一个测试程序,它可以使用客户端证书和Expect头成功执行大文件的HTTP PUT请求.
总结一下,解决方案是:
归档时间: |
|
查看次数: |
15594 次 |
最近记录: |