0

I want to import a lone public key (.pem) into a Java keystore (.jks). I don't have the private key. As I understand, the public key must be wrapped as a certificate, so that I can import it into a java keystore.

The .pem content looks like:

-----BEGIN PUBLIC KEY-----
key_content....
-----END PUBLIC KEY-----

Is there a possibility to convert a plain public key to a certificate, so that I can import it into a java keystore? Or is there any other option to import a public key into a java keystore?

Simply converting the public key from .pem to .der is not enough. The public key is used for programatic encryption of data, not for TLS.

3

The standard (or at least conventional) certificate request process does require the privatekey matching the publickey to be certified. The pre-packaged operations of Java's keytool utility and the openssl commandline are limited to this standard process, as are several other existing Qs.

However, it is not fundamentally necessary. As long as you have a privatekey for the certificate issuer, nominally a Certificate Authority, and a certificate for that CA key or at least some critical metadata normally embedded in such a certificate, it is possible to issue a child cert for a given child publickey.

This can be done with OpenSSL by calling the X509_* routines in libcrypto from custom code; in Java the classes used by standard keytool are not officially part of the JRE spec but are open source, but if you can use the BouncyCastle third-party libraries (bcprov + bcpkix) they directly expose the needed functions in a convenient and documented/guaranteed form, so I show that. This creates a very minimal certificate with little of the information and especially none of the extensions that are commonly present in real, CA-issued certificates. If this is not sufficient for your needs, ask a more complete question.

For simplicity I read the (subject) pubkey PEM from a named file, and the CA key and cert from a JKS in a named file at a named alias. These sources of information can be replaced by others as you wish. I also don't do any error handling leaving Java to do its default stacktrace; you may need better.

import java.security.*;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

static void Se179526FakeCert (String[] args) throws Exception {
    // pubkey.pem cakey.jks pw alias 
    Object spki  = new PEMParser (new FileReader (args[0])).readObject();
    KeyStore ks = KeyStore.getInstance("JKS"); 
    ks.load (new FileInputStream (args[1]), args[2].toCharArray());
    PrivateKey cakey = (PrivateKey) ks.getKey (args[3], args[2].toCharArray());
    X509Certificate cacert = (X509Certificate) ks.getCertificate (args[3]);
    Date now = new Date(), exp = new Date(now.getTime()+30*86400*1000); // 30 days validity

    ContentSigner signer = new JcaContentSignerBuilder ("SHA256with"+cakey.getAlgorithm()).build(cakey);
    byte[] enc = new X509v3CertificateBuilder(
            /*issuer*/new X500Name (cacert.getIssuerX500Principal().getName()), /*serial*/BigInteger.valueOf(now.getTime()),
            /*validity*/now, exp, /*subject*/new X500Name ("CN=dummy"), /*spki*/(SubjectPublicKeyInfo) spki 
        ) .build(signer) .getEncoded();
    // .addExtension's if&as needed before .build
    Certificate cert = CertificateFactory.getInstance ("X.509") .generateCertificate(new ByteArrayInputStream(enc));
    // could now store in a keystore; or to write out in PEM:
    JcaPEMWriter out = new JcaPEMWriter (new OutputStreamWriter (System.out));
    out.writeObject (cert); out.flush(); 
}
1

No, you cannot generate a valid certificate without providing the private key. If you need a certificate, the holder of the private key must request it.

Otherwise, certificates would meaningless because anyone could create a certificate for any domain for any public key of their choosing.

  • I just want to import the public key into the java keystore, therefore it needs to be wrapped as certificate. For me it doesn‘t matter, if the certificate is valid or not, the certificate information isn‘t used (no TLS). So yes, in my case the certificate would be meaningless. But still my question: is it technically possible to wrap a public key into a certificate without the original private key? – trunkc Feb 11 '18 at 8:24
  • I updated my answer to be more clear. No, you cannot sign a public key without the private key. – John Deters Feb 11 '18 at 22:28
  • 1
    An attacker would not want a cert issued for which they don't have the privatekey, and in any case can't get a cert from a proper CA linking any publickey to a domainname or other identity they don't own or control, FSVO own or control. But nearly all CAs use the PKCS10 standard for CSR, which does require (prove possession of) the privatekey. – dave_thompson_085 Feb 12 '18 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.