Search code examples
c#.netcryptographycng

RSACng and CngKeyBlobFormat import and export formats


I have a ASN.1 encoded RSA private key in a pem file in this format:

-----BEGIN RSA PRIVATE KEY-----
base64 encoded pkcs8 key
-----END RSA PRIVATE KEY-----

Now, to import it into my RSACng object I neeed to follow these steps:

  1. Read the file and extract the encoded key

  2. Convert base64 to bytes to get the byte[] for pkcs8 key

  3. Decode the byte[] from ASN.1 (DER) to key information (modulus, exponent etc.)

  4. Load these parameters into the RSACng object

I have two following questions:

1. Why doesn't the CngKeyBlobFormat.Pkcs8PrivateBlob allow you to automatically import the PKCS8 byte[] key into the RSACng object?

For instance, why couldn't it work in this way:

var keyData = GetBytesFromPEM(pemstring); // Get the bytes for key that is ASN.1 DER encoded
CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob);

CngKeyBlobFormat clearly specifies that it is a PKCS8 private blob.

2. What is the ASN.1 encoding format of the RSACng.Key.Export(CngKeyBlobFormat.Pkcs8PrivateBlob)?

I noticed that if I load the key into the RSACng as I described above and I then export the same key using the above code, I get the BLOB which is encoded in a different ASN.1 format, and which INSIDE it contains the ASN.1 DER encoded key. Basically, to get information from this exported key I would need to DECODE it from this ASN.1 format again to get the original key parameters stored inside it which are AGAIN encoded in ASN.1 DER.

Why is it such a mess? And is the reason why you cannot import the ASN.1 DER encoded key into the RSACng that CngKeyBlobFormat.Pkcs8PrivateBlob has a different ASN.1 encoding format and it is not DER? Would the potential workaround then be to encode the original RSA private key to that another ASN.1 format, since this is exactly how the key is exported?

EDIT: apparently, RSACng.Key.Export(CngKeyBlobFormat.Pkcs8PrivateBlob) uses Object Identifiers (I'm not yet familiar with that), but it seems to still be in DER format


Solution

    1. Why doesn't the CngKeyBlobFormat.Pkcs8PrivateBlob allow you to automatically import the PKCS8 byte[] key into the RSACng object?

    Because BEGIN RSA PRIVATE KEY indicates a PKCS#1 RSAPrivateKey, not a PKCS#8 PrivateKeyInfo. A PKCS#8 non-encrypted private key would say BEGIN PRIVATE KEY.

    1. What is the ASN.1 encoding format of the RSACng.Key.Export(CngKeyBlobFormat.Pkcs8PrivateBlob)?

    PKCS#8 PrivateKeyInfo. https://www.rfc-editor.org/rfc/rfc5208#section-5.

      PrivateKeyInfo ::= SEQUENCE {
        version                   Version,
        privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
        privateKey                PrivateKey,
        attributes           [0]  IMPLICIT Attributes OPTIONAL }
    
      Version ::= INTEGER
    
      PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
    
      PrivateKey ::= OCTET STRING
    
      Attributes ::= SET OF Attribute
    

    The value in PrivateKey for an RSA key is the PKCS#1 RSAPrivateKey value.

    Why is it such a mess? ... Object Identifiers ...

    The PKCS#8 PrivateKeyInfo format is a container for any kind of private key. The Object Identifier (OID) tells the reader what kind of thing the payload is. If the OID is rsaEncryption (1.2.840.113549.1.1.1) then the payload is RSAPrivateKey, if it's id-dsa (1.2.840.10040.4.1) then the payload is an INTEGER representing the private key (and the context parameters are in privateKeyAlgorithm.Parameters), if it's id-ecPublicKey (1.2.840.10045.2.1) (yes "public", 'cuz names) then the payload is an ECPrivateKey, et cetera.