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.
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.