La Renégociation SSL avec un Certificat Client causes de Serveur de Débordement de la mémoire Tampon

J'ai codé un client Java de l'application qui se connecte à un serveur web Apache sur HTTPS à l'aide d'un certificat client et effectue un HTTP de METTRE un fichier sur le serveur. Il fonctionne très bien avec les petits fichiers, mais se bloque avec les grands.

Le serveur Apache journal affiche les éléments suivants:

...
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

La réponse sur le client est:

java.io.IOException: Server returned HTTP response code: 401 for URL

Je ne suis pas familier avec ce processus de sorte que je ne suis pas sûr si la renégociation est nécessaire ici ou si il y a quelque chose que je peux faire pour l'empêcher. Ou peut-être que je peut avoir le client attendre jusqu'à ce que la renégociation est terminée avant d'envoyer les données de l'application? Voici un extrait de code client (gestion d'erreur supprimé):

        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());

Je pense peut-être j'ai besoin d'utiliser un niveau inférieur de l'API comme SSLSocket et de tirer parti de la HandshakeCompletedListener?

Je me demande aussi si l'Apache SSLVerifyDepth directive n'a rien à voir avec la raison d'une renégociation est en cours. J'ai la directive dans un contexte de répertoire (un seul répertoire de téléchargement) avec la valeur 2 et Le manuel Apache dit à ce sujet:

Dans un contexte de répertoire, il force un SSL renegotation avec l'
vérification du client spécifié profondeur après la lecture d'une requête HTTP
mais avant de la réponse HTTP est envoyée.

Comme demandé ici est la Java de sortie de débogage:

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 

Comme demandé voici le getMyCustomClientCertSocketFactory source (obtient un certificat et une clé à partir d'un fichier 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();
}
  • Si j'utilise l'utilitaire UNIX "curl" je peux transférer des fichiers volumineux sans incident alors je me demande ce qu'il se fait de la même façon...
  • Voici un rapport de bogue qui traite de la question: bugzilla.redhat.com/show_bug.cgi?id=491763. La délivrance d'un HTTP GET (ou de la TÊTE ou des OPTIONS) sur le répertoire de téléchargement avant de le METTRE ne semble pas fonctionner lors de l'utilisation de HttpsUrlConnections - je suppose que keep-alive n'est pas respectée?
  • Exécuter votre client avec -Djavax.net.debug=ssl,la poignée de main et après la sortie dans votre réponse.
  • La sortie de débogage est trop verbeux pour la coller dans un commentaire, mais, fondamentalement, il ressemble à la demande de données est envoyé avant la renégociation est même demandé par le serveur et en ce moment c'est trop tard parce que le socket se ferme. Est-il un moyen fiable de prévoir une renégociation? Ce n'curl faire?
  • Monter dans votre question. Jusqu'à ce que vous faites, de sorte que personne ne peut les aider.
  • Ce problème, que vous avez correctement identifié, est à l'Apache fin. Changer les choses à la Java fin n'aura aucun effet.
  • Gardez à l'esprit que quand je télécharge un fichier énorme sur le certificat de client protégé répertoire à l'aide de l'utilitaire UNIX curl il fonctionne très bien. Donc il y a quelque chose que le client peut faire pour éviter d'inonder le serveur et mon client Java ne le fait pas.

InformationsquelleAutor Ryan | 2013-01-11