Search code examples
javabouncycastlepublic-keyecdsa

ECDSA public key compressed to pem format java


I have compressed public keys for ECDSA for curve secp256k1

0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2 0318ed2e1ec629e2d3dae7be1103d4f911c24e0c80e70038f5eb5548245c475f50

I need to convert these to pem format in same compressed way using java. I tried to write pem format using SubjectPublicKeyInfo. But it is not giving me expected results.


Solution

  • One option is BouncyCastle and the following implementation:

    import java.io.StringWriter;
    import java.util.HexFormat;
    import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
    import org.bouncycastle.util.io.pem.PemObject;
    import org.bouncycastle.util.io.pem.PemWriter;
    import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
    import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
    import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
    ...
    byte[] compressedKey = HexFormat.of().parseHex("03CD26952CFFB40A3C2B7E41BE80F02F8A21BA73958ED22276CC28D7BBAA1AA07A");
    AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1);
    SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algId, compressedKey);
    StringWriter sw = new StringWriter(); 
    try (PemWriter pw = new PemWriter(sw)) {
        pw.writeObject (new PemObject ("PUBLIC KEY", subjectPublicKeyInfo.getEncoded("DER")));  
    }
    System.out.println(sw.toString());
    

    This results e.g. for the compressed key:

    0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2
    

    in the following X.509/SPKI PEM key:

    -----BEGIN PUBLIC KEY-----
    MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgACUpclctRl0BbUxQGIe43zA+7j7WAsBWse
    sJJg36DaCrI=
    -----END PUBLIC KEY-----
    

    The PEM key can be loaded with an ASN.1 parser, e.g. https://lapo.it/asn1js/. The encapsulated key corresponds to the compressed key.


    Note that the import of an X.509/SPKI EC key with a compressed key is not always supported (e.g. JCA/JCE: Invalid EC key / Only uncompressed point format supported):

    Security.addProvider(new BouncyCastleProvider()); // Commenting out causes an exception: Invalid EC key / Only uncompressed point format supported
    String spkiDerWithCompressed = "MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgADzSaVLP+0CjwrfkG+gPAviiG6c5WO0iJ2zCjXu6oaoHo=";
    KeyFactory kf = KeyFactory.getInstance("EC");
    X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.getDecoder().decode(spkiDerWithCompressed));
    PublicKey publicKey = (ECPublicKey) kf.generatePublic(keySpecX509);
    String spkiDerWithUncompressed = Base64.getEncoder().encodeToString(publicKey.getEncoded());
    System.out.println(spkiDerWithUncompressed); // MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzSaVLP+0CjwrfkG+gPAviiG6c5WO0iJ2zCjXu6oaoHr7WDbGHaR3hDq6+PtVm4+G5gWiYCWrh3fxe4ahjeW3Sw==
    

    For completeness: A conversion of the compressed key into an uncompressed key is possible with BouncyCastle, e.g.:

    import org.bouncycastle.asn1.x9.X9ECParameters;
    import org.bouncycastle.asn1.x9.ECNamedCurveTable;
    import org.bouncycastle.math.ec.ECPoint;
    ...     
    byte[] compressedKey = HexFormat.of().parseHex("0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2");
    X9ECParameters x9Params = ECNamedCurveTable.getByName("secp256k1");
    ECPoint ecpoint = x9Params.getCurve().decodePoint(compressedKey);
    byte[] uncompressedKey = ecpoint.getEncoded(false);
    

    An X.509/SPKI PEM key encapsulating the uncompressed key can be generated using the same code that was used for the compressed key.