title: SSL Renegotiation with Client Certificate causes Server Buffer Overflow
body: <p>I've coded a Java client application which connects to an Apache web server over HTTPS using a client certificate and performs an HTTP PUT of a file to the server.  It works fine with small files, but crashes with large ones.  </p>
  
  <p>The Apache server log shows the following:</p>
  
  <pre><code>...
  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
  </code></pre>
  
  <p>The response on the client is:</p>
  
  <pre><code>java.io.IOException: Server returned HTTP response code: 401 for URL
  </code></pre>
  
  <p>I'm not familiar with this process so I'm not sure if renegotiation is necessary here or if there is something I can do to prevent it.  Or perhaps I can have the client wait until the renegotiation is complete before sending application data?  Here is an excerpt of the client code (error handling removed):</p>
  
  <pre><code>        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());
  </code></pre>
  
  <p>I'm thinking maybe I need to use a lower level API like SSLSocket and leverage the HandshakeCompletedListener?</p>
  
  <p>I'm also wondering if the Apache SSLVerifyDepth directive has anything to do with why a renegotiation is occurring.  I've got the directive in a per-directory context (only one upload directory) with value 2 and The Apache manual says this about it:</p>
  
  <blockquote>
    <p>In per-directory context it forces a SSL renegotation with the
    reconfigured client verification depth after the HTTP request was read
    but before the HTTP response is sent.</p>
  </blockquote>
  
  <p>As requested here is the Java debugging output:</p>
  
  <pre><code>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: &lt;empty&gt;
  ***
  %% 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 
  </code></pre>
  
  <p>As requested here is the getMyCustomClientCertSocketFactory source (obtains certificate and key from a PEM file):</p>
  
  <pre><code>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();
  }
  </code></pre>