The "RSA PUBLIC KEY" format was used in very early SSLeay, which evolved into OpenSSL, but obsoleted before 2000 I believe, which was very early days for Java and I think before Java had any crypto even the restricted kind then allowed for export from the US. In short, the "RSA PUBLIC KEY" format is the RSA-specific format from PKCS#1, whereas "PUBLIC KEY" is the X.509 generic structure that handles numerous (and extensible) algorithm. So it would be interesting to know how the developers of your Java library got themselves into this bizarre limitation. But anyway ...
Although this format is long obsolete, OpenSSL still supports it. If you have the openssl commandline available (could be on another system which you copy the file/data to and back) just do:
openssl rsa -in publickey.pem -out rsapublickey.pem -pubin -RSAPublicKey_out
Argh! having written the below, now I notice the (more general) dupe at Generating RSA keys in PKCS#1 format in Java
(plus several links onward from there) if you use BouncyCastle in Java (or OpenSSL library in C, but you already have the OpenSSL commandline option above).
Anyway, here is an outline of two ways to code in plain Java if you prefer that:
Both of them start by converting the input PEM to bytes. Read the "----BEGIN" line and preferably check it's correct; read all following lines (of base64) up to but not including the "-----END" line; concatenate and decode the base64 to bytes. Java8 provides java.util.Base64
; before that you had to fiddle with "internal" classes or add one of several common (but not builtin) libraries like commons-codecs or write it yourself (which isn't that hard). Now choose either step 1 or step 2.
Parse those bytes as the ASN.1 DER encoding of an X.509 SubjectPublicKeyInfo as shown in RFC 5280 including the AlgorithmIdentifier. To be exact: skip the tag and length of the outer sequence; skip the tag, length and contents of the AlgorithmIdentifier -- or better extract the contents and check it is a SEQUENCE of an OID for rsaEncryption and NULL (or possibly omitted) params; then skip the tag and length and first byte (unused-bits) for the BIT STRING and take the (remaining) contents as the encoded key -- which is already the PKCS#1 RSAPublicKey structure you want. Proceed to step 3.
Or use standard JCE to read the X.509 format key: wrap the bytes in an X509EncodedKeySpec
, give it to .generatePublic()
of a KeyFactory
for RSA, and cast the result to RSAPublicKey
. Then call .getModulus()
and .getPublicExponent()
to get the mathematical values and encode them in ASN.1 DER with the structure RSAPublicKey defined in PKCS#1 rfc3447 (used for PKIX/X.509 in rfc3279 2.2.1). BigInteger.toByteArray()
gives exactly the big-endian signed two's-complement form that ASN.1 wants, so this consists of:
get both .toByteArray()
values, add tag=INTEGER (0x02) and length prefixes to each one, then add a tag=SEQUENCE-composite (0x30) and length prefix to their concatenation. Then proceed to step 3.
Now you have the bytes constituting a PKCS#1 RSAPublicKey, convert to PEM: encode to base64; break into lines (at 64 chars) if needed, or always to be safe; and add "BEGIN" and "END" lines, unless not needed.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…