lapo-luchini / asn1js

JavaScript generic ASN.1 parser
ISC License
581 stars 161 forks source link

Support PKCS#1 encoded RSA keys #76

Open cnotin opened 10 months ago

cnotin commented 10 months ago

PKCS#1 encoded RSA keys can be generated with OpenSSL using:

openssl genrsa [-out key.rsa] 1024

The format is described in RFC 8017, and in particular: https://www.rfc-editor.org/rfc/rfc8017#appendix-A.1

Currently, such a key is detected as a "PKCS#8 private key" which gives an incorrect mapping.

Here's a sample (zipped because GitHub requires it): 1024.zip

lapo-luchini commented 6 months ago

RFC 8017 uses CLASSes which parseRFC.js doesn't support at all at the moment. I wonder if commenting them all out will lead to a still useful definition.

lapo-luchini commented 6 months ago

Actually… that command does actually write a PKCS#8 object:

% openssl genrsa -out examples/pkcs1.pem 1024
% ./dumpASN1.js examples/pkcs1.pem
PrivateKeyInfo SEQUENCE @0+631 (constructed): (3 elem)
  version Version INTEGER @4+1: 0
  privateKeyAlgorithm AlgorithmIdentifier SEQUENCE @7+13 (constructed): (2 elem)
    algorithm OBJECT_IDENTIFIER @9+9: 1.2.840.113549.1.1.1|rsaEncryption|PKCS #1
    parameters NULL @20+0
  privateKey PrivateKey OCTET_STRING @22+609 (encapsulates): (609 byte)
    SEQUENCE @26+605 (constructed): (9 elem)
      INTEGER @30+1: 0
      INTEGER @33+129: (1024 bit)
      INTEGER @165+3: 65537
      INTEGER @170+129: (1024 bit)
      INTEGER @302+65: (512 bit)
      INTEGER @369+65: (512 bit)
      INTEGER @436+64: (511 bit)
      INTEGER @502+65: (512 bit)
      INTEGER @569+64: (511 bit)

It is the PrivateKey OCTET STRING which encapsulated a PKCS#1 RSAPrivateKey value, but it isn't easy to detect that in a generic way.

lapo-luchini commented 6 months ago

OK, in order to have a raw PKCS#1 you need to execute this as well:

openssl rsa -in examples/pkcs8-rsa.pem -out examples/pkcs1.pem -traditional

I added both examples and very rudimentary PKCS#1 support.

lapo-luchini commented 6 months ago

Raw PKCS#1 is now detected by itself, but not yet when inside a PKCS#8 like your example.

$ ./dumpASN1.js examples/pkcs1.pem
RSAPrivateKey SEQUENCE @0+605 (constructed): (9 elem)
  version Version INTEGER @4+1: 0
  modulus INTEGER @7+129: (1024 bit)
  publicExponent INTEGER @139+3: 65537
  privateExponent INTEGER @144+129: (1024 bit)
  prime1 INTEGER @276+65: (512 bit)
  prime2 INTEGER @343+65: (512 bit)
  exponent1 INTEGER @410+64: (511 bit)
  exponent2 INTEGER @476+65: (512 bit)
  coefficient INTEGER @543+64: (511 bit)
cnotin commented 6 months ago

Oh thank you for taking a look :) I don't have all the details in my head anymore but it seems to look better!

lapo-luchini commented 6 months ago

not yet when inside a PKCS#8 like your example.

This is more complex because I have to find a way to let it understand that the privateKeyAlgorithm.algorithm identifies the type of the value encapsulated by privateKey OCTET STRING (which is only defined in words inside section 5 of RFC 5208).

PrivateKeyInfo SEQUENCE (constructed):
  version Version INTEGER
  privateKeyAlgorithm AlgorithmIdentifier SEQUENCE (constructed):
    algorithm OBJECT_IDENTIFIER 1.2.840.113549.1.1.1
    parameters ANY NULL
  privateKey PrivateKey OCTET_STRING (encapsulates):
    SEQUENCE (constructed):
      INTEGER
      INTEGER
      …

(and at the time I prefer to avoid "magic code" and only use features that can be parsed directly from ASN.1 definitions inside RFCs)