1
votes

I'm currently working on a project that involves getting an Elliptic Curve Certificate / Private Key package in either PEM or DER format from Vault and needing to import it into a Java Keystore. I can find plenty of information on doing this with keytool, but am unable to find information about how to successfully convert a DER encoded string into a PrivateKeyEntry to insert it into the keystore.

Below is my non-working code. certificate , key, and issuingCa are all PEM or DER encoded Strings (I can specify which format I want to get back from the issuer and pass whichever I can get to work)


private KeyStore packKeystore(String certificate, String key, String issuingCa, String name) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
    // Create the keystore 
    KeyStore retVal = KeyStore.getInstance(KeyStore.getDefaultType());
    retVal.load(null, sslKeystorePassword.toCharArray());
    var cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
    var cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificate.getBytes()));
    retVal.setCertificateEntry(name, cert);
    Certificate issuer = null;
    if (issuingCa != null) {
      issuer = cf.generateCertificate(new ByteArrayInputStream(issuingCa.getBytes(StandardCharsets.UTF_8)));
    }
    if (key != null) {
      var certs = new HashSet<Certificate>();
      certs.add(issuer);
      certs.add(cert);
      PrivateKeyEntry pk = /// How do I create this from what I have???? 
     
      retVal.setKeyEntry( pk, certs.toArray());
    }

    return retVal;
  }

After some experimentation and research I've learned that the PrivateKey class doesn't like the "old" PEM format where the private key looks like ---- BEGIN EC PRIVATE KEY-----.

I was eventually able to parse a PrivateKey object from a keypair like this:

   var parsedKey = new org.bouncycastle.openssl.PEMParser(new StringReader(key)).readObject();
      var pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair) parsedKey);
      retVal.setKeyEntry(name, pair.getPrivate(), "".toCharArray(), certArray);

From there I get an error on setKeyEntry about the certificate algorithm not matching the private key algorithm. Upon inspection it seems that the Certificate object says the algo is EC and the PK object says the algo is ECDSA.

1

1 Answers

1
votes

I eventually solved it this way. Note the key must be in base64 encoded DER format:

 private PrivateKey convertECPrivateKeyString(String key) {
   ...
    byte[] keyBytes = null;
    if (key != null) {
      keyBytes = Base64.getDecoder().decode(key);
    }
    try (var asnStream = new ASN1InputStream(keyBytes)) {
      var primitive = asnStream.readObject();
      asnStream.close();

      if (primitive instanceof ASN1Sequence) {
        var sequence = (ASN1Sequence) primitive;
        var pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(sequence);
        var pkInfo = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters()), pKey);
        return new JcaPEMKeyConverter().getPrivateKey(pkInfo);
      }
    } 
...
  }