Can we create a keystore dynamically in memory to establish SSL handshake in vertx

215 views
Skip to first unread message

ranjit kollu

unread,
Jul 7, 2017, 4:29:43 PM7/7/17
to vert.x
Hi,

   Can we create a JKS keystore in memory (reading the certs and keys from DB) instead of a file and load it during vertx http server start ? and also manage it without restarting the server ?.
I am implementing my KeyCertOptions interface and passing it into HttpServerOptions. But looks like the SSL handshake is not happening. Any pointers would be really helpful.

openssl s_client -connect server:8443 -tls1_2
CONNECTED(00000003)
139633665279904:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:598:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : 0000
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1499458964
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---

Thanks,
Ranjit

Julien Viet

unread,
Jul 8, 2017, 2:47:29 AM7/8/17
to ve...@googlegroups.com
can you provide a reproducer ?

--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at https://groups.google.com/group/vertx.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/e3a46700-216a-44ef-8dbd-3a7c1374141a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

ranjit kollu

unread,
Jul 10, 2017, 10:35:30 AM7/10/17
to vert.x
Below is the code -

public class CycCertOptions implements KeyCertOptions, Cloneable {
  private final MyKeyManager keyManager = new MyKeyManager();
  private final MyKeyManagerFactory factory = new MyKeyManagerFactory(keyManager);

  public CycCertOptions() {
    super();
    //loadKeyStore();
  }

  public CycCertOptions(CycCertOptions other) {
    super();
    //loadKeyStore();
  }

  public void loadKeyStore() {
    try {
      keyManager.load();
    } catch(UnrecoverableKeyException | KeyStoreException e) {

    }
  }

  @Override
  public KeyManagerFactory getKeyManagerFactory(Vertx vertx) throws Exception {
    //return factory;
    return keyManager.getKeyManagerFactory();
  }

  @Override
  public CycCertOptions clone() {

    return new CycCertOptions(this);
  }

  static class MyKeyManager extends X509ExtendedKeyManager {
    KeystoreManager ksMgr = null;
    private volatile X509ExtendedKeyManager wrapped;
    KeyManagerFactory keyManagerFactory = null;

    public MyKeyManager() {

      try {
        ksMgr = new KeystoreManager();
        keyManagerFactory =
            KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(ksMgr.getKeyStore(), ksMgr.getKeystorePassword());
        wrapped = (X509ExtendedKeyManager) keyManagerFactory.getKeyManagers()[0];
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    public void load() throws UnrecoverableKeyException, KeyStoreException {
      try {
        keyManagerFactory =
            KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(ksMgr.getKeyStore(), ksMgr.getKeystorePassword());
        wrapped = (X509ExtendedKeyManager) keyManagerFactory.getKeyManagers()[0];
      } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
      }
    }

    public KeyManagerFactory getKeyManagerFactory() {
      return keyManagerFactory;
    }
    @Override
    public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine engine) {
      throw new UnsupportedOperationException();
    }

    @Override
    public String[] getServerAliases(String s, Principal[] principals) {
      throw new UnsupportedOperationException();
    }

    @Override
    public X509Certificate[] getCertificateChain(String s) {
      return wrapped == null ? null : wrapped.getCertificateChain(s);
    }

    @Override
    public PrivateKey getPrivateKey(String s) {
      return wrapped == null ? null : wrapped.getPrivateKey(s);
    }

    @Override
    public String[] getClientAliases(String s, Principal[] principals) {
      throw new UnsupportedOperationException();
    }

    @Override
    public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
      throw new UnsupportedOperationException();
    }

    @Override
    public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
      throw new UnsupportedOperationException();
    }
  }

  static class MyKeyManagerFactory extends KeyManagerFactory {
    MyKeyManagerFactory(final KeyManager keyManager) {
      super(new KeyManagerFactorySpi() {
        @Override
        protected void engineInit(KeyStore keyStore, char[] chars) throws KeyStoreException,
            NoSuchAlgorithmException, UnrecoverableKeyException {
          throw new UnsupportedOperationException();
        }

        @Override
        protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws
            InvalidAlgorithmParameterException {
          throw new UnsupportedOperationException();
        }

        @Override
        protected KeyManager[] engineGetKeyManagers() {
          return new KeyManager[]{keyManager};
        }
      }, new Provider("", 0.0, "") {
      }, "");
    }
  }
}

public class KeystoreManager {

  private static final char[] KEYSTORE_PASSWORD = "abc".toCharArray();
  private KeyStore mKeyStore = null;

  public KeystoreManager() {
    if( mKeyStore == null) {
      this.mKeyStore = createStore();
    }
  }

  public KeyStore getKeyStore() {
    return mKeyStore;
  }

  public char[] getKeystorePassword() {
    return KEYSTORE_PASSWORD;
  }

  public KeyStore createStore() {
    try {
      mKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      mKeyStore.load(null, KEYSTORE_PASSWORD);

      reloadServerCertificate("/home/cyc/test_certs/certs/foo.der", "/home/cyc/test_certs/private/fooKey.der");

    } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
      throw new RuntimeException("Unable to create empty keyStore", e);
    }

    return mKeyStore;
  }

  public void reloadServerCertificate(String certFilename, String keyFilename) {
    try {
      clearKeyStore();

      //load key
      InputStream keyInputStream = fullStream(keyFilename);
      byte[] key  = new byte[keyInputStream.available()];
      keyInputStream.read(key, 0, keyInputStream.available());
      keyInputStream.close();

      //load certificate
      InputStream certStream = fullStream(certFilename);
      
      loadServerCertificate(certStream, key);
      certStream.close();
    } catch(Exception ex) {
      System.out.println("Error while reloading the certificate :" + ex.getLocalizedMessage());
    }
  }

 
  public void loadServerCertificate(InputStream certstream, byte[] keyStr) {
    try {

      Certificate[] certs = getCertificateChain(certstream);
      PrivateKey pKey = getPrivateKey(keyStr);
      mKeyStore.setKeyEntry("test", pKey, KEYSTORE_PASSWORD, certs);

    } catch (Exception ex) {
      System.out.println("Error while adding the server certificate!!!" + ex.getLocalizedMessage());
    }
  }

  private Certificate[] getCertificateChain(InputStream certstream) {
    Certificate[] certs = null;

    try {
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      Collection c = cf.generateCertificates(certstream) ;
      certs = new Certificate[c.toArray().length];

      if (c.size() == 1) {
        certstream = fullStream ("/home/cyc/test_certs/certs/foo.der");
        System.out.println("One certificate, no chain.");
        Certificate cert = cf.generateCertificate(certstream) ;
        certs[0] = cert;
      } else {
        System.out.println("Certificate chain length: "+c.size());
        certs = (Certificate[])c.toArray();
      }
    } catch (Exception ex) {
    }

    return certs;
  }

  private PrivateKey getPrivateKey(byte[] keyStr) throws Exception {
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec( keyStr );

    return kf.generatePrivate(keysp);
  }

  private void clearKeyStore() {
    try {
      for (Enumeration<String> e = mKeyStore.aliases();
           e.hasMoreElements(); ) {
        final String alias = e.nextElement();
        mKeyStore.deleteEntry(alias);
      }
    } catch (KeyStoreException e) {
    }
  }

  private static InputStream fullStream(String fname) throws IOException {
    FileInputStream fis = null;
    DataInputStream dis = null;
    try {
      fis = new FileInputStream(fname);
      dis = new DataInputStream(fis);
      byte[] bytes = new byte[dis.available()];
      dis.readFully(bytes);
      ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
      return bais;
    } finally {
      fis.close();
      dis.close();
    }
  }
}

In the  main -

      Launcher.main(Stream.concat(Stream.of("run", TestMain.class.getName()), Stream.of(args)).toArray(String[]::new));

Verticle loading -

@Override
  public void start() {
    try {
      Vertx vertx = Vertx.vertx();
      HttpServerOptions serverOpts = getHttpServerOptions();
      vertx.createHttpServer(serverOpts)
          .requestHandler(this::handle)
          .listen(listenPort);

    } catch(Exception ex) {
      System.out.println("Error while running - " + ex.getLocalizedMessage());
    }
  }

  private void handle(HttpServerRequest req) {
  }

  private static HttpServerOptions getHttpServerOptions() {
   List<String> cipherSuites = Arrays.asList(
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
    );

    HttpServerOptions httpServerOptions =  new HttpServerOptions()
          .setReuseAddress(true)
          .setUsePooledBuffers(true)
          .setSsl(true)
          .setKeyCertOptions(new CycCertOptions())
          .addEnabledSecureTransportProtocol("TLSv1.2")
          .addEnabledSecureTransportProtocol("TLSv1.3")
          .setJdkSslEngineOptions(new JdkSSLEngineOptions());

    cipherSuites.forEach(httpServerOptions::addEnabledCipherSuite);
    return httpServerOptions;
  }

  static String javaCipherNameToOpenSSLName(String name) {
    return name.replace("TLS_", "")
        .replace("WITH_AES_", "AES")
        .replace('_', '-');
  }


Does this help ?. 

Julien Viet

unread,
Jul 10, 2017, 10:37:57 AM7/10/17
to ve...@googlegroups.com
can you provide a small project that gather all this together ?

ranjit kollu

unread,
Jul 10, 2017, 11:22:21 AM7/10/17
to vert.x

 I sent you files in a private reply to you, please take a look.

ranjit kollu

unread,
Jul 10, 2017, 11:25:45 AM7/10/17
to vert.x
I am here with attaching the files for everyone to view
certmgmt.tar.gz
certs.tar.gz

ranjit kollu

unread,
Jul 11, 2017, 10:14:54 AM7/11/17
to vert.x
Hi Julien,

    Did you get all the files you needed ? or do you need anything else ?. Please let me know. I am still stuck on this trying different things. Thanks again

Ranjit

Dan O'Reilly

unread,
Jul 11, 2017, 11:38:49 AM7/11/17
to vert.x
It helps if you create an actual project that reproduces the problem (via Maven/Gradle/etc), and push it to github. Then people can just clone your repository and load it into their IDE to quickly reproduce the issue.

--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at https://groups.google.com/group/vertx.

ranjit kollu

unread,
Jul 11, 2017, 6:07:22 PM7/11/17
to vert.x
I have created a new git hub project for this issue -


git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/ranjitkollu999/vertx_cert_mgmt.git
git push -u origin master

Instructions to run -

1) Import the project in intellij
2) Build the project
3) Run the project
4) Go to command line on linux or windows that has openssl client and run the following command -
   openssl s_client -connect <host_name>:<port>


Please take a look.

Thanks,
Ranjit

ranjit kollu

unread,
Jul 13, 2017, 6:40:29 PM7/13/17
to vert.x
Looks like the issue is happening during the SSL handshake -

The output from openssl client -

CONNECTED(00000003)
SSL_connect:before/connect initialization
write to 0x24515c0 [0x2451640] (247 bytes => 247 (0xF7))
0000 - 16 03 01 00 f2 01 00 00-ee 03 03 59 67 f5 ee 41   ...........Yg..A
0010 - ca c9 f4 58 79 33 73 11-d8 3c 63 ea 57 08 0b 3f   ...Xy3s..<c.W..?
0020 - 39 c1 bf 21 99 f8 70 51-bb 73 e9 00 00 84 c0 30   9..!..pQ.s.....0
0030 - c0 2c c0 28 c0 24 c0 14-c0 0a 00 a3 00 9f 00 6b   .,.(.$.........k
0040 - 00 6a 00 39 00 38 00 88-00 87 c0 32 c0 2e c0 2a   .j.9.8.....2...*
0050 - c0 26 c0 0f c0 05 00 9d-00 3d 00 35 00 84 c0 2f   .&.......=.5.../
0060 - c0 2b c0 27 c0 23 c0 13-c0 09 00 a2 00 9e 00 67   .+.'.#.........g
0070 - 00 40 00 33 00 32 00 9a-00 99 00 45 00 44 c0 31   .@.3.2.....E.D.1
0080 - c0 2d c0 29 c0 25 c0 0e-c0 04 00 9c 00 3c 00 2f   .-.).%.......<./
0090 - 00 96 00 41 c0 12 c0 08-00 16 00 13 c0 0d c0 03   ...A............
00a0 - 00 0a 00 07 c0 11 c0 07-c0 0c c0 02 00 05 00 04   ................
00b0 - 00 ff 01 00 00 41 00 0b-00 04 03 00 01 02 00 0a   .....A..........
00c0 - 00 08 00 06 00 19 00 18-00 17 00 23 00 00 00 0d   ...........#....
00d0 - 00 20 00 1e 06 01 06 02-06 03 05 01 05 02 05 03   . ..............
00e0 - 04 01 04 02 04 03 03 01-03 02 03 03 02 01 02 02   ................
00f0 - 02 03 00 0f 00 01 01                              .......
>>> TLS 1.2 Handshake [length 00f2], ClientHello
    01 00 00 ee 03 03 59 67 f5 ee 41 ca c9 f4 58 79
    33 73 11 d8 3c 63 ea 57 08 0b 3f 39 c1 bf 21 99
    f8 70 51 bb 73 e9 00 00 84 c0 30 c0 2c c0 28 c0
    24 c0 14 c0 0a 00 a3 00 9f 00 6b 00 6a 00 39 00
    38 00 88 00 87 c0 32 c0 2e c0 2a c0 26 c0 0f c0
    05 00 9d 00 3d 00 35 00 84 c0 2f c0 2b c0 27 c0
    23 c0 13 c0 09 00 a2 00 9e 00 67 00 40 00 33 00
    32 00 9a 00 99 00 45 00 44 c0 31 c0 2d c0 29 c0
    25 c0 0e c0 04 00 9c 00 3c 00 2f 00 96 00 41 c0
    12 c0 08 00 16 00 13 c0 0d c0 03 00 0a 00 07 c0
    11 c0 07 c0 0c c0 02 00 05 00 04 00 ff 01 00 00
    41 00 0b 00 04 03 00 01 02 00 0a 00 08 00 06 00
    19 00 18 00 17 00 23 00 00 00 0d 00 20 00 1e 06
    01 06 02 06 03 05 01 05 02 05 03 04 01 04 02 04
    03 03 01 03 02 03 03 02 01 02 02 02 03 00 0f 00
    01 01
SSL_connect:SSLv2/v3 write client hello A
read from 0x24515c0 [0x2456ba0] (7 bytes => 7 (0x7))
0000 - 15 03 03 00 02 01                                 ......
0007 - <SPACES/NULS>
<<< TLS 1.2 Alert [length 0002], warning close_notify
    01 00
SSL3 alert read:warning:close notify
SSL_connect:failed in SSLv3 read server hello A
139721100093344:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:184:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : 0000
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1499985391
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

All the ciphers between openssl and vertx are compatible, protocols are ok. I dont understand why this is failing. 
Any one has any ideas ???

Thanks,
Ranjit


On Friday, July 7, 2017 at 4:29:43 PM UTC-4, ranjit kollu wrote:

Andrei Iacob

unread,
Jun 21, 2018, 7:13:51 PM6/21/18
to vert.x
Hi Ranjit,

did you manage to find a solution? I believe I'm running into the exact same issue: https://groups.google.com/forum/#!topic/vertx/T2A2goENqSI

Thanks,
Andrei

Andrei Iacob

unread,
Jun 25, 2018, 7:52:01 PM6/25/18
to vert.x
Nevermind, I figured it out.
Reply all
Reply to author
Forward
0 new messages