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.
Vous devez vous connecter pour publier un commentaire.
Il semblerait que le HttpsUrlConnection installation construite dans Sun Java ne peut pas gérer le grand HTTP PUT, avec certificat du client scénario dans un serveur de manière conviviale (c'est à dire sans déborder les serveurs SSL renégocier tampon).
J'ai cherché ce que curl a été fait pour voir ce que le serveur accueillant voulait dire", et il s'avère qu'il y est une de HTTP 1.1-tête nommée "s'Attendre à", qui curl envoie avec la valeur "100-continue" (voir spec http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20). Cet en-tête dit essentiellement: "j'ai une énorme charge utile, mais avant de me l'envoyer s'il vous plaît laissez-moi savoir si vous pouvez le manipuler". Cela donne les points de terminaison du temps pour renégocier le certificat du client avant la charge utile est envoyé.
Dans le Soleil HttpUrlConnection mise en œuvre, il semble que cet en-tête n'est pas autorisé, et est en fait dans le cadre limité des en-têtes de liste; ce qui signifie que même si vous définissez avec le HttpUrlConnection.setRequestProperty méthode de l'en-tête n'est pas envoyé au serveur. Vous pouvez remplacer la restreint en-têtes avec le système de la propriété soleil.net.http.allowRestrictedHeaders, mais alors le client se bloque avec un socket exception puisque le Soleil de la mise en œuvre ne sait pas comment gérer cette partie du protocole.
Fait intéressant, il semble que l'OpenJDK mise en œuvre de Java prend en charge cet en-tête. Aussi, le Client HTTP Apache bibliothèque prend en charge cette en-tête (http://hc.apache.org/); j'ai mis en place un programme de test avec le serveur Apache HTTP bibliothèque de client et il peut effectuer correctement et de requête HTTP PUT d'un fichier volumineux à l'aide d'un certificat du client et le fait de s'Attendre d'en-tête.
Pour résumer, les solutions sont:
C'est une erreur d'application. Elle n'est pas causée par la couche SSL. Je ne suis pas sûr pourquoi vous obtenez
401 Unauthorized
pour les gros fichiers, mais vous omettez également de ce qui estgetMyCustomClientCertSocketFactory()
Aussi, avez-vous essayé une autre méthode, par exemple
POST
? Avez-vous le même problème?I can bump the buffer up and the problem goes away, but this is a vulnerability for a denial of service and not ideal
Vous dites que le problème est la taille de la mémoire tampon d'Apache?Ensuite, vous devez augmenter, sans quoi il est vulnérable à DoS.Je veux dire, même si vous faites un peu de changement du côté client, la question sera encore là dans le serveur si le DoS est votre préoccupationHTTP
erreur etHTTP
s'exécute au-dessus deSSL
.Comment est alors possible qu'uneHTTP
réponse est à envoyer en tant que résultat d'un problème de poignée de main?Si vous pouvez écrire une réponse pour expliquer cela, je ne upvote et je suis sûr que d'autres pourraient trouver utile.401
.Comportement étrange.Tomcat ne fonctionne pas comme çaBasé sur toutes les informations supplémentaires disponibles, je pense que vous devriez écrire du XML en plusieurs morceaux, plutôt qu'un seul. À l'heure actuelle vous êtes à l'écriture d'un morceau, qui seront coupés par le protocole SSL dans 16k morceaux, ce qui est l'étouffement Apache pour une raison quelconque (il ne devrait pas). Je voudrais essayer des tailles de segment n'est pas plus grand que la 4k. Régler la taille de bloc jusqu'à ce qu'il fonctionne.
Vous pourriez bien trouver certificat du client des problèmes une fois que vous avez passé celui-ci. Ne vous découragez pas, c'est la preuve que vous avez au moins de résoudre ce problème.