Convert RSA public key, from XML to PEM (PHP)

2019-04-02 08:02发布

问题:

How convert RSA public key, from XML to PEM (PHP)?

回答1:

we know

.pem - (Privacy Enhanced Mail) Base64 encoded DER certificate, enclosed between "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----"

X.509

The SignatureValue element contains the Base64 encoded signature result - the signature generated with the parameters specified in the SignatureMethod element - of the SignedInfo element after applying the algorithm specified by the CanonicalizationMethod.

XML_Signature

so we end up with

$xml = simplexml_load_file($xmlFile); // or simplexml_load_string

$pem = "-----BEGIN CERTIFICATE-----\n";
$pem .= $xml->SignatureValue;
$pem .= "\n-----END CERTIFICATE-----";

// save to file

if your xml-file isn't a XML_Signature

$xml = simplexml_load_file($xmlFile); // or simplexml_load_string
$pem = "-----BEGIN CERTIFICATE-----\n";
$pem .= $xml->nodeWithWantedValue; // use base64_encode if needed
$pem .= "\n-----END CERTIFICATE-----";


回答2:

I'm assuming that by XML format, you mean XML DSig RSAKeyValue, and that by PEM format you mean what OpenSSL exports in between -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----.

You need first to extract the modulus and public exponent from the XML.

   <RSAKeyValue>
     <Modulus>xA7SEU+e0yQH5rm9kbCDN9o3aPIo7HbP7tX6WOocLZAtNfyxSZDU16ksL6W
      jubafOqNEpcwR3RdFsT7bCqnXPBe5ELh5u4VEy19MzxkXRgrMvavzyBpVRgBUwUlV
      5foK5hhmbktQhyNdy/6LpQRhDUDsTvK+g9Ucj47es9AQJ3U=
     </Modulus>
     <Exponent>AQAB</Exponent>
   </RSAKeyValue>

You can easily convert these into a bit string using base64_decode.

Once this is done, you need to build the ASN.1 public key structure somehow.

What OpenSSL exports between BEGIN/END PUBLIC KEY is an X.509 SubjectPublicKeyInfo structure.

SubjectPublicKeyInfo ::= SEQUENCE {
   algorithm AlgorithmIdentifier,
   subjectPublicKey BIT STRING }

The subjectPublicKey is made of a sequnce is described in the PKCS#1 spec:

RSAPublicKey ::= SEQUENCE {
   modulus INTEGER,
   publicExponent INTEGER
}

The algorithm (an AlgorithmIdentifier) is also described in the PKCS#1 spec (see section A.1):

rsaEncryption
OBJECT IDENTIFIER ::= { pkcs-1 1 }

This structure needs to be serialized in DER form, then base64-encoded and then placed between the BEGIN/END delimiters.

I don't know of any PHP library to do the ASN.1/DER encoding unfortunately (the rest is relatively easy, but dealing with ASN.1 tends to be tedious).

The PHP/PEAR Crypt_RSA module can construct RSA public keys from modulus and exponent, but its toString() method uses a custom format (just the base64-encoding of the result of PHP serialize on the array structure, which has nothing to do with the ASN.1/DER encoding).



回答3:

There is no standard for storing RSA public keys in XML. So the manner of conversion will depend on the XML you have.



回答4:

Here's an example of how to read XML RSA keys in PHP:



回答5:

Just for completeness, here is a working example of creating the PEM from modulus in python. You could call it in a subprocess from PHP if necessary.

The meat of the solution is:

def big_endian(n):
    s = '%x' % n
    if len(s) & 1:
        s = '0' + s
    return s.decode('hex')

from M2Crypto import RSA

e = E_PREFIX + big_endian(public_exponent)
n = N_PREFIX + big_endian(modulus)

new = RSA.new_pub_key((e,n))
new.save_key('foo.pub')

Where E_PREFIX and N_PREFIX are constants that (as far as I can tell) depend on the exponent and key length. Here is a quick table I have constructed:

E_PREFIX = '\x00\x00\x00\x01' # 0x3 (3)
E_PREFIX = '\x00\x00\x00\x03' # 0x10001 (65537)

N_PREFIX = '\x00\x00\x00!\x00' # 256-bit
N_PREFIX = '\x00\x00\x00A\x00' # 512-bit (default)
N_PREFIX = '\x00\x00\x00\x81\x00' # 1024-bit
N_PREFIX = '\x00\x00\x01\x01\x00' # 2048-bit
N_PREFIX = '\x00\x00\x02\x01\x00' # 4096-bit

If someone knows a more general way to compute the prefixes, do tell.



回答6:

Maybe you should have a look here

Extract the two base64-encoded strings, convert and pass to PEAR::Crypt_RSA, then export as text file, then openssl convert?

Check this too