PeculiarVentures / x509

@peculiar/x509 is an easy to use TypeScript/Javascript library based on @peculiar/asn1-schema that makes generating X.509 Certificates and Certificate Requests as well as validating certificate chains easy
https://peculiarventures.github.io/x509/
MIT License
78 stars 10 forks source link

PrintableString, UTF8String & OCSP #16

Closed ghost closed 2 years ago

ghost commented 2 years ago

I'm building an OCSP request, and I noticed that there is a small discrepancy between what the library produces vs other implementations (namely openssl & java's bouncycastle) which, in turn, make the OSCP request to fail.

When I create the OCSP request (using asn1-ocsp package) I use this code snippet:

import { Name as X509Name } from "@peculiar/x509";
import { createHash } from "crypto";

const nameBuffer = new X509Name(issuer.subject).toArrayBuffer();
const issuerNameHash = createHash("sha1").update(Buffer.from(nameBuffer)).digest();

// and later

new CertID({
  hashAlgorithm: sha1,
  issuerNameHash: new OctetString(issuerNameHash),
  issuerKeyHash: new OctetString(...),
  serialNumber: Convert.FromHex(cert.serialNumber),
}),

This snippet generates the following ASN.1 (specifically for the issuerNameHash)

SEQUENCE (1 elem)
  SET (1 elem)
    SEQUENCE (2 elem)
      OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
      PrintableString NTRPY01PG1

However, sending that OCSP request to the server fails and the server returns:

SEQUENCE (1 elem)
  ENUMERATED 6

I tested OpenSSL and BouncyCastle for the same certificate, and I noticed that the only difference is on that field, they produce it like this:

SEQUENCE (1 elem)
  SET (1 elem)
    SEQUENCE (2 elem)
      OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
      UTF8String NTRPY01PG1

Namely, with UTF8String instead of PrintableString

If I hack the code and create that ASN manually to be UTF8String it works fine, with PrintableScreen the request fails.

Is this something on my end, or is it a bug in the library?

microshine commented 2 years ago

RFC5280 defines

-- Naming attributes of type X520CommonName

id-at-commonName        AttributeType ::= { id-at 3 }

-- Naming attributes of type X520CommonName:
--   X520CommonName ::= DirectoryName (SIZE (1..ub-common-name))
--
-- Expanded to avoid parameterized type:
X520CommonName ::= CHOICE {
      teletexString     TeletexString   (SIZE (1..ub-common-name)),
      printableString   PrintableString (SIZE (1..ub-common-name)),
      universalString   UniversalString (SIZE (1..ub-common-name)),
      utf8String        UTF8String      (SIZE (1..ub-common-name)),
      bmpString         BMPString       (SIZE (1..ub-common-name)) }

The real problem is in the Issuer name serialization. You are using Name class which recreates the issuer name and doesn't use the same string types.

const nameBuffer = new X509Name(issuer.subject).toArrayBuffer();

I think we need to fix it in @peculiar/x509 and add something like issuerName: Name and subjectName: Name fields to X509Certificate class

microshine commented 2 years ago

I've published the new version of @peculiar/x509@1.6.0. It must solve your problem. Please try it.

https://peculiarventures.github.io/x509/classes/X509Certificate.html#issuerName https://peculiarventures.github.io/x509/classes/Name.html#getThumbprint

// browser
const issuerNameHash = await cert.issuerName.getThumbprint("SHA-256", crypto);

// nodejs ^16.x
const issuerNameHash = await cert.issuerName.getThumbprint("SHA-256", crypto.webcrypto);
ghost commented 2 years ago

This worked fine, thank you very much for the extremely fast response