I need to use a public key to verify some data in Java, but I can't seem to format the key in such a way that Java can use without third-party plugins.
I'm generating the key with Node.js's crypto
library, which gives me the option of PKCS#1
or SPKI
, and either .pem or .der file format.
I've heard that Java doesn't support PKCS#1
out-of-the box, and pretty much every other answer on StackOverflow recommends using BouncyCastle or similar, but in my case, I am writing an SDK, and simply cannot afford to use a library just to read this public key.
So I'm currently reading the key in .der format as it saves having to strip the PEM headers and decode the key from base-64. When I run this, I get the error:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000be:ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG
Here's what I have (sorry, it's in Kotlin, not Java like the title suggests)
// Here's a key for convenience
val key = Base64.getDecoder().decode("MFUCTgF/uLsPBS13Gy7C3dPpiDF6SYCLUyyl6CFqPtZT1h5bwKR9EDFLQjG/kMiwkRMcmEeaLKe5qdj9W/FfFitwRAm/8F53pQw2UETKQI2b2wIDAQAB");
val keySpec = X509EncodedKeySpec(key)
val keyFactory = KeyFactory.getInstance("RSA")
val publicKey = keyFactory.generatePublic(keySpec) // error thrown here
val cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, publicKey)
My best idea at the minute is to install a library on the Node.js side, which is less problematic, to support exporting the key as PKCS#8, but I thought I'd check first to see if I'm missing anything.
The following code turns a PKCS#1 encoded public key into a SubjectPublicKeyInfo encoded public key, which is the public key encoding accepted by the RSA
KeyFactory
usingX509EncodedKeySpec
- as SubjectPublicKeyInfo is defined in the X.509 specifications.Basically it is a low level DER encoding scheme which
0x03
, and a encoding for the number of unused bits, a byte valued0x00
);0x30
).No libraries are used. Actually, for
createSubjectPublicKeyInfoEncoding
, no import statements are even required.Notes:
NoSuchAlgorithmException
should probably be caught and put into aRuntimeException
;createDERLengthEncoding
should probably not accept negative sizes.createDERLengthEncoding
for those - I presume it works, but better be safe than sorry.