Code Monkey home page Code Monkey logo

x509's Introduction

@peculiar/x509

License Node.js CI Coverage Status npm version

About

@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.

Installation

npm install @peculiar/x509

Documentation

https://peculiarventures.github.io/x509/

Usage

Browser

Every release of @peculiar/x509 will have new build of ./build/x509.js for use in the browser. To get access to module classes use x509 global variable.

WARN: We recommend hosting and controlling your own copy for security reasons

<script src="https://unpkg.com/@peculiar/x509"></script>

A simple web application examples

Set crypto provider for Node.js

In some cases you may want to use a different cryptographic implementation, for example when you want to work with an object that supports a cryptographic algorithm not supported by the platform you are on.

In these cases you can set a custom provider, these providers need to be compatible with the WebCrypto API, for example on NodeJS you can use @peculiar/webcrypto to allow @peculiar/x509 to work the same as it does in browser!

import * as x509 from "@peculiar/x509";
import { Crypto } from "@peculiar/webcrypto";

const crypto = new Crypto();
x509.cryptoProvider.set(crypto);

Create a self-signed certificate

const alg = {
  name: "RSASSA-PKCS1-v1_5",
  hash: "SHA-256",
  publicExponent: new Uint8Array([1, 0, 1]),
  modulusLength: 2048,
};
const keys = await crypto.subtle.generateKey(alg, false, ["sign", "verify"]);
const cert = await x509.X509CertificateGenerator.createSelfSigned({
  serialNumber: "01",
  name: "CN=Test",
  notBefore: new Date("2020/01/01"),
  notAfter: new Date("2020/01/02"),
  signingAlgorithm: alg,
  keys,
  extensions: [
    new x509.BasicConstraintsExtension(true, 2, true),
    new x509.ExtendedKeyUsageExtension(["1.2.3.4.5.6.7", "2.3.4.5.6.7.8"], true),
    new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true),
    await x509.SubjectKeyIdentifierExtension.create(keys.publicKey),
  ]
});

console.log(cert.toString("pem")); // Certificate in PEM format

Parse a x509 certificate

const base64 = "MIIDljCCAn6gAwIBAgIOSETcxtRwD...S+kAFXIwugUGYEnTWp0m5bAn5NlD314IEOg4mnS8Q==";

const cert = new x509.X509Certificate(base64);
console.log(cert.subject); // CN=Test, O=PeculiarVentures LLC

Create a PKCS#10 certificate request

const alg = {
  name: "ECDSA",
  namedCurve: "P-384",
  hash: "SHA-384",
}
const keys = await crypto.subtle.generateKey(alg, false, ["sign", "verify"]);
const csr = await x509.Pkcs10CertificateRequestGenerator.create({
  name: "CN=Test",
  keys,
  signingAlgorithm: alg,
  extensions: [
    new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment),
  ],
  attributes: [
    new x509.ChallengePasswordAttribute("password"),
  ]
});

console.log(csr.toString("base64")); // Certificate request in Base64 format

Decoded X509 certificate

X509Certificate {
  rawData: ArrayBuffer {
    [Uint8Contents]: <30 82 02 fc 30 82 01 e4 a0 03 02 01 02 02 01 01 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 0f 31 0d 30 0b 06 03 55 04 03 13 04 54 65 73 74 30 1e 17 0d 31 39 31 32 33 31 32 31 30 30 30 30 5a 17 0d 32 30 30 31 30 31 32 31 30 30 30 30 5a 30 0f 31 0d 30 0b 06 03 55 04 03 13 04 54 65 73 74 30 82 01 ... 668 more bytes>,
    byteLength: 768
  },
  tbs: ArrayBuffer {
    [Uint8Contents]: <30 82 01 e4 a0 03 02 01 02 02 01 01 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 0f 31 0d 30 0b 06 03 55 04 03 13 04 54 65 73 74 30 1e 17 0d 31 39 31 32 33 31 32 31 30 30 30 30 5a 17 0d 32 30 30 31 30 31 32 31 30 30 30 30 5a 30 0f 31 0d 30 0b 06 03 55 04 03 13 04 54 65 73 74 30 82 01 22 30 0d 06 ... 388 more bytes>,
    byteLength: 488
  },
  serialNumber: '01',
  subject: 'CN=Test',
  issuer: 'CN=Test',
  signatureAlgorithm: { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-256' } },
  signature: ArrayBuffer {
    [Uint8Contents]: <2e 78 fb 4b f6 c8 a1 9d b4 d1 8b 22 80 20 c1 68 46 39 a6 11 d1 a9 7a 13 03 8d 1e 0e 5e 87 b5 33 2a ba 44 1b 96 6d 91 e7 fd c0 ce b7 93 fe e4 df d3 d0 57 7c 9a eb 7e 3e 8b ed c6 07 ad 80 df fd 8f f7 ce 26 07 db 0e 9f af e6 cb 70 02 2d 17 9f f5 c1 0d ef d6 cf 1d ec 78 a0 dd 5d 46 2a 60 08 71 74 2c 26 ... 156 more bytes>,
    byteLength: 256
  },
  notBefore: 2019-12-31T21:00:00.000Z,
  notAfter: 2020-01-01T21:00:00.000Z,
  extensions: Extensions(4) [
    BasicConstraintsExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.19',
      critical: true,
      value: [ArrayBuffer],
      ca: true,
      pathLength: 2
    },
    ExtendedKeyUsageExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.37',
      critical: true,
      value: [ArrayBuffer],
      usages: [ExtendedKeyUsage]
    },
    KeyUsagesExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.15',
      critical: true,
      value: [ArrayBuffer],
      usages: 96
    },
    SubjectKeyIdentifierExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.14',
      critical: false,
      value: [ArrayBuffer],
      keyId: 'f525754650a3dee83f8bd777ee3b53ecc2c8d726'
    }
  ],
  publicKey: PublicKey {
    rawData: ArrayBuffer {
      [Uint8Contents]: <30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 b6 f4 f1 cf dd 26 a1 23 45 b6 6e 4e ec 3e 20 8a 3f 90 ec 84 46 49 87 a2 05 c5 eb da ac 84 37 eb a3 bf 46 b5 8e 82 75 25 8a 80 19 10 79 13 c0 13 6c 29 df 56 44 1c ec f8 7b 34 0a f2 13 41 b5 53 98 e1 f5 ... 194 more bytes>,
      byteLength: 294
    },
    algorithm: {
      name: 'RSASSA-PKCS1-v1_5',
      publicExponent: [Uint8Array],
      modulusLength: 2048
    }
  }
}

Build a certificate chain

const chain = new x509.X509ChainBuilder({
  certificates: [
    new x509.X509Certificate(raw1),
    new x509.X509Certificate(raw2),
    // ...
    new x509.X509Certificate(rawN),
  ],
});

const cert = x509.X509Certificate(raw);
const items = await chain.build(cert);
console.log(items); // [ X509Certificate, X509Certificate, X509Certificate ]

Export a list of X509 certificates to PKCS#7 format

const certs = new x509.X509Certificates([
  new x509.X509Certificate("MIIDljCCAn6gAwIBAgIOSETcxtRwD...S+kAFXIwugUGYEnTWp0m5bAn5NlD314IEOg4mnS8Q=="),
  new x509.X509Certificate("MIIDljCCAn6gAwIBAgIOSETcxtRwD...w8Y/o+hk3QzNBVa3ZUvzDhVAmamQflvw3lXMm/JG4U="),
]);

console.log(certs.export("base64")); // "MIICTAYJKoZIhvcNAQcCoIICPTCCAjkCAQAxADACBgCgggIq...F7EZPNo3pjbfznpIilRMRrmwf5dkgCdSKDdE94xAA==");

x509's People

Contributors

algv avatar bindon avatar microshine avatar nhw76 avatar shiny avatar turon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

x509's Issues

Extend CertificateIdentifier

  • Comparison with the Certificate
if (certID.equal(cert)) {
  // ...
}
  • Creation from the Certificate
const certID = CertificateIdentifier.create(cert);

Certificate chain that validated in v1.9.4 no longer does in v1.9.5

Hey @microshine โ€“ I updated to v1.9.5 today and found that #63 broke a chain that successfully validated in v1.9.4.

This is in Node.js v16 using the Web Crypto API module (import { webcrypto } from 'crypto').

Here's the error:

DOMException [NotSupportedError]: Unrecognized namedCurve
    at new DOMException (node:internal/per_context/domexception:53:5)
    at __node_internal_ (node:internal/util:505:10)
    at Object.ecImportKey (node:internal/crypto/ec:170:11)
    at SubtleCrypto.importKey (node:internal/crypto/webcrypto:541:10)
    at PublicKey.export (/Users/jstayton/Code/lens-api/node_modules/@peculiar/x509/build/x509.cjs.js:1289:30)
    at X509ChainBuilder.findIssuer (/Users/jstayton/Code/lens-api/node_modules/@peculiar/x509/build/x509.cjs.js:2340:59)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async X509ChainBuilder.build (/Users/jstayton/Code/lens-api/node_modules/@peculiar/x509/build/x509.cjs.js:2305:26)

Here are the X509Certificate objects:

X509Certificate {
  rawData: ArrayBuffer {
    [Uint8Contents]: <30 82 02 e3 30 82 02 69 a0 03 02 01 02 02 06 01 7b e0 d4 df b3 30 0a 06 08 2a 86 48 ce 3d 04 03 02 30 4f 31 23 30 21 06 03 55 04 03 0c 1a 41 70 70 6c 65 20 41 70 70 20 41 74 74 65 73 74 61 74 69 6f 6e 20 43 41 20 31 31 13 30 11 06 03 55 04 0a 0c 0a 41 70 70 6c 65 20 49 6e 63 2e 31 13 30 11 06 03 55 ... 643 more bytes>,
    byteLength: 743
  },
  tbs: ArrayBuffer {
    [Uint8Contents]: <30 82 02 69 a0 03 02 01 02 02 06 01 7b e0 d4 df b3 30 0a 06 08 2a 86 48 ce 3d 04 03 02 30 4f 31 23 30 21 06 03 55 04 03 0c 1a 41 70 70 6c 65 20 41 70 70 20 41 74 74 65 73 74 61 74 69 6f 6e 20 43 41 20 31 31 13 30 11 06 03 55 04 0a 0c 0a 41 70 70 6c 65 20 49 6e 63 2e 31 13 30 11 06 03 55 04 08 0c 0a ... 521 more bytes>,
    byteLength: 621
  },
  serialNumber: '017be0d4dfb3',
  subjectName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(4) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  subject: 'CN=a203e1588ab36ae2ffc362491c2948df5d03f3ed048d0c58a59c9e085724353c, OU=AAA Certification, O=Apple Inc., ST=California',
  issuerName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(3) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  issuer: 'CN=Apple App Attestation CA 1, O=Apple Inc., ST=California',
  signatureAlgorithm: { name: 'ECDSA', hash: { name: 'SHA-256' } },
  signature: Uint8Array(103) [
     48, 101,   2,  49,   0, 208,  64, 201,  24, 104,  16, 199,
     13,  42,   4,  49, 154,  56, 116, 122, 238,  30, 163, 218,
    163,  88,   5,  15,  21, 174, 134, 158,  25,   7, 184, 211,
    103, 252, 193,  63, 228, 194, 235,  27,  55, 213, 177, 195,
    111, 223,  82, 218, 192,   2,  48,  91, 142, 216, 103, 158,
     93,  89, 100, 104, 191, 133, 168, 167, 174, 232, 168, 228,
      6, 240, 223, 117, 197, 232, 126,  10, 212,  36, 100, 232,
    108, 195,  45, 172,  49, 191,  63, 209, 120, 167,   0, 255,
     17,  49,  27,  40,
    ... 3 more items
  ],
  notBefore: 2021-09-12T20:24:12.000Z,
  notAfter: 2021-09-15T20:24:12.000Z,
  extensions: Extensions(5) [
    BasicConstraintsExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.19',
      critical: true,
      value: [ArrayBuffer],
      ca: false,
      pathLength: undefined
    },
    KeyUsagesExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.15',
      critical: true,
      value: [ArrayBuffer],
      usages: 15
    },
    Extension {
      rawData: [ArrayBuffer],
      type: '1.2.840.113635.100.8.5',
      critical: false,
      value: [ArrayBuffer]
    },
    Extension {
      rawData: [ArrayBuffer],
      type: '1.2.840.113635.100.8.7',
      critical: false,
      value: [ArrayBuffer]
    },
    Extension {
      rawData: [ArrayBuffer],
      type: '1.2.840.113635.100.8.2',
      critical: false,
      value: [ArrayBuffer]
    }
  ],
  publicKey: PublicKey {
    rawData: ArrayBuffer {
      [Uint8Contents]: <30 59 30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a 86 48 ce 3d 03 01 07 03 42 00 04 09 1a ae 9f d2 0b 89 e6 6b ab 68 3e 70 e1 6d 0f b1 2f 8b 4b bd c9 d2 54 ec 15 2c b4 fc 4c 8d fb e1 49 0d 90 34 80 10 82 08 6c 49 58 7e 2c 5b 90 2b 80 2d 1f f3 e9 36 59 51 d2 3e 1d d2 f8 75 e3>,
      byteLength: 91
    },
    algorithm: { name: 'ECDSA', namedCurve: 'P-256' },
    tag: 'PUBLIC KEY'
  },
  tag: 'CERTIFICATE'
}

X509Certificate {
  rawData: ArrayBuffer {
    [Uint8Contents]: <30 82 02 43 30 82 01 c8 a0 03 02 01 02 02 10 09 ba c5 e1 bc 40 1a d9 d4 53 95 bc 38 1a 08 54 30 0a 06 08 2a 86 48 ce 3d 04 03 03 30 52 31 26 30 24 06 03 55 04 03 0c 1d 41 70 70 6c 65 20 41 70 70 20 41 74 74 65 73 74 61 74 69 6f 6e 20 52 6f 6f 74 20 43 41 31 13 30 11 06 03 55 04 0a 0c 0a 41 70 70 6c ... 483 more bytes>,
    byteLength: 583
  },
  tbs: ArrayBuffer {
    [Uint8Contents]: <30 82 01 c8 a0 03 02 01 02 02 10 09 ba c5 e1 bc 40 1a d9 d4 53 95 bc 38 1a 08 54 30 0a 06 08 2a 86 48 ce 3d 04 03 03 30 52 31 26 30 24 06 03 55 04 03 0c 1d 41 70 70 6c 65 20 41 70 70 20 41 74 74 65 73 74 61 74 69 6f 6e 20 52 6f 6f 74 20 43 41 31 13 30 11 06 03 55 04 0a 0c 0a 41 70 70 6c 65 20 49 6e ... 360 more bytes>,
    byteLength: 460
  },
  serialNumber: '09bac5e1bc401ad9d45395bc381a0854',
  subjectName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(3) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  subject: 'CN=Apple App Attestation CA 1, O=Apple Inc., ST=California',
  issuerName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(3) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  issuer: 'CN=Apple App Attestation Root CA, O=Apple Inc., ST=California',
  signatureAlgorithm: { name: 'ECDSA', hash: { name: 'SHA-384' } },
  signature: Uint8Array(104) [
     48, 102,   2,  49,   0, 187, 190, 136, 141, 115, 141,   5,
      2, 207, 188, 253, 102, 109,   9,  87,  80,  53, 188, 214,
    135,  44,  63, 132,  48,  73,  38,  41, 237, 209, 249,  20,
    232, 121, 153,  28, 154, 232, 181, 174, 248, 211, 168,  84,
     51, 247, 182,  13,   6,   2,  49,   0, 171,  56, 237, 208,
    204, 129, 237,   0, 164,  82, 195, 186,  68, 249, 147,  99,
    101,  83, 254, 204,  41, 127,  46, 180, 223, 159,  94, 190,
     90,  74, 202, 182, 153,  92,  75, 130,  13, 249,   4,  56,
    111, 120,   7, 187,
    ... 4 more items
  ],
  notBefore: 2020-03-18T18:39:55.000Z,
  notAfter: 2030-03-13T00:00:00.000Z,
  extensions: Extensions(4) [
    BasicConstraintsExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.19',
      critical: true,
      value: [ArrayBuffer],
      ca: true,
      pathLength: 0
    },
    AuthorityKeyIdentifierExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.35',
      critical: false,
      value: [ArrayBuffer],
      keyId: 'ac91105333bdbe6841ffa70ca9e5faeae5e58aa1'
    },
    SubjectKeyIdentifierExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.14',
      critical: false,
      value: [ArrayBuffer],
      keyId: '3ee35d1c0419a9c9b431f88474d6e1e15772e39b'
    },
    KeyUsagesExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.15',
      critical: true,
      value: [ArrayBuffer],
      usages: 96
    }
  ],
  publicKey: PublicKey {
    rawData: ArrayBuffer {
      [Uint8Contents]: <30 76 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 00 22 03 62 00 04 ae 5b 37 a0 77 4d 79 b2 35 8f 40 e7 d1 f2 26 26 f1 c2 5f ef 17 80 2d ea b3 82 6a 59 87 4f f8 d2 ad 15 25 78 9a a2 66 04 19 12 48 b6 3c b9 67 06 9e 98 d3 63 bd 5e 37 0f bf a0 8e 32 9e 80 73 a9 85 e7 74 6e a3 59 a2 f6 6f 29 db 32 ... 20 more bytes>,
      byteLength: 120
    },
    algorithm: { name: 'ECDSA', namedCurve: 'P-384' },
    tag: 'PUBLIC KEY'
  },
  tag: 'CERTIFICATE'
}

X509Certificate {
  rawData: ArrayBuffer {
    [Uint8Contents]: <30 82 02 21 30 82 01 a7 a0 03 02 01 02 02 10 0b f3 be 0e f1 cd d2 e0 fb 8c 6e 72 1f 62 17 98 30 0a 06 08 2a 86 48 ce 3d 04 03 03 30 52 31 26 30 24 06 03 55 04 03 0c 1d 41 70 70 6c 65 20 41 70 70 20 41 74 74 65 73 74 61 74 69 6f 6e 20 52 6f 6f 74 20 43 41 31 13 30 11 06 03 55 04 0a 0c 0a 41 70 70 6c ... 449 more bytes>,
    byteLength: 549
  },
  tbs: ArrayBuffer {
    [Uint8Contents]: <30 82 01 a7 a0 03 02 01 02 02 10 0b f3 be 0e f1 cd d2 e0 fb 8c 6e 72 1f 62 17 98 30 0a 06 08 2a 86 48 ce 3d 04 03 03 30 52 31 26 30 24 06 03 55 04 03 0c 1d 41 70 70 6c 65 20 41 70 70 20 41 74 74 65 73 74 61 74 69 6f 6e 20 52 6f 6f 74 20 43 41 31 13 30 11 06 03 55 04 0a 0c 0a 41 70 70 6c 65 20 49 6e ... 327 more bytes>,
    byteLength: 427
  },
  serialNumber: '0bf3be0ef1cdd2e0fb8c6e721f621798',
  subjectName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(3) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  subject: 'CN=Apple App Attestation Root CA, O=Apple Inc., ST=California',
  issuerName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(3) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  issuer: 'CN=Apple App Attestation Root CA, O=Apple Inc., ST=California',
  signatureAlgorithm: { name: 'ECDSA', hash: { name: 'SHA-384' } },
  signature: Uint8Array(103) [
     48, 101,   2,  48,  66,   1,  70, 156,  28, 175, 178,  37,
     91, 165,  50, 176,  74,   6, 180, 144, 253,  30, 240,  71,
    131,  75, 143, 172,  66, 100, 239, 111, 187, 231, 231, 115,
    185, 248,  84,  87, 129, 226, 225, 164, 157,  58, 202, 192,
    185,  62, 179, 178,   2,  49,   0, 167, 149,  56, 196,  56,
      4, 130,  89,  69, 236,  73, 247,  85, 193,  55, 137, 236,
     89, 102, 210, 158,  98, 122, 106, 182,  40, 213, 163,  33,
    107, 105, 101,  72, 201, 223, 221, 129, 169, 230, 173, 219,
    130, 213, 185, 147,
    ... 3 more items
  ],
  notBefore: 2020-03-18T18:32:53.000Z,
  notAfter: 2045-03-15T00:00:00.000Z,
  extensions: Extensions(3) [
    BasicConstraintsExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.19',
      critical: true,
      value: [ArrayBuffer],
      ca: true,
      pathLength: undefined
    },
    SubjectKeyIdentifierExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.14',
      critical: false,
      value: [ArrayBuffer],
      keyId: 'ac91105333bdbe6841ffa70ca9e5faeae5e58aa1'
    },
    KeyUsagesExtension {
      rawData: [ArrayBuffer],
      type: '2.5.29.15',
      critical: true,
      value: [ArrayBuffer],
      usages: 96
    }
  ],
  publicKey: PublicKey {
    rawData: ArrayBuffer {
      [Uint8Contents]: <30 76 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 00 22 03 62 00 04 45 31 e1 98 b5 b4 ec 04 da 15 02 04 57 04 ed 4f 87 72 72 d7 61 35 b2 61 16 cf c8 8b 61 5d 0a 00 07 19 ba 69 85 8d fe 77 ca a3 b8 39 e0 20 dd d6 56 14 14 04 70 28 31 e4 3f 70 b8 8f d6 c3 94 b6 08 ea 2b d6 ae 61 e9 f5 98 c1 2f 46 ... 20 more bytes>,
      byteLength: 120
    },
    algorithm: { name: 'ECDSA', namedCurve: 'P-384' },
    tag: 'PUBLIC KEY'
  },
  tag: 'CERTIFICATE'
}

Examples of using X509Crl and X509CrlGenerator

Foremost, thank you for this great library!

As part of needing to implement various CRL functionality, I noticed related PRs merged here and here but couldn't find any examples in the repo of using these structures.

I wonder if it'd be possible for you to provide some minimal example of usingX509CrlEntry, X509CrlGenerator, and any related structures; this would be extremely helpful (something similar to the example showed here should be great).

Note that upon trying to create a minimal instance of X509CrlEntry with the following:

const serialNumber = crypto.randomBytes(16).toString("hex");
const crlEntry = new x509.X509CrlEntry(serialNumber, new Date(), []);

as per the constructor:

constructor(serialNumber: string, revocationDate: Date, extensions: Extension[]);

I'm getting the following error:

dev-api              |     err: {
dev-api              |       "type": "TypeError",
dev-api              |       "message": "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'",
dev-api              |       "stack":
dev-api              |           TypeError: The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
dev-api              |               at BufferSourceConverter.toView (/app/node_modules/pvtsutils/build/index.js:60:15)
dev-api              |               at BufferSourceConverter.toUint8Array (/app/node_modules/pvtsutils/build/index.js:48:21)
dev-api              |               at new Some (/app/node_modules/asn1js/build/index.js:146:98)
dev-api              |               at new LocalIntegerValueBlock (/app/node_modules/asn1js/build/index.js:1472:9)
dev-api              |               at new BaseBlock (/app/node_modules/asn1js/build/index.js:513:44)
dev-api              |               at new Integer (/app/node_modules/asn1js/build/index.js:1612:9)
dev-api              |               at Object.toASN (/app/node_modules/@peculiar/asn1-schema/build/cjs/converters.js:32:23)
dev-api              |               at AsnSerializer.toAsnItem (/app/node_modules/@peculiar/asn1-schema/build/cjs/serializer.js:135:38)
dev-api              |               at AsnSerializer.toASN (/app/node_modules/@peculiar/asn1-schema/build/cjs/serializer.js:52:48)
dev-api              |               at AsnSerializer.serialize (/app/node_modules/@peculiar/asn1-schema/build/cjs/serializer.js:14:21)
dev-api              |     }

Overall, it would be great to get a concrete example of how to use the structure.

Library does not work in Safari web browser (desktop and iOS)

Safari (desktop and iOS) does not support a negative look behind in the regular expression (caniuse). While such a feature is used in the next regular expression: https://github.com/PeculiarVentures/x509/blob/master/src/name.ts#L162.
UPD: one more https://github.com/PeculiarVentures/x509/blob/master/src/name.ts#L186.

Safari throws a runtime error: SyntaxError: Invalid regular expression: invalid group specifier name.

Steps to reproduce:

  1. Create HTML with <script src="https://unpkg.com/@peculiar/x509"></script>
  2. Open it in the Safari browser (Version 14.0.2 (16610.3.7.1.9).
  3. See an error in the console, x509 object doesn't add to the global scope.

X509Certificate.verify() method does not use the crypto argument

I am experiencing the following problem

// self-signed certificate verification
const certificate = new X509Certificate(buffer);
await certificate.verify({}, new Crypto()); // => true
await certificate.verify({ publicKey: certificate.publicKey }, new Crypto()); // => false

So, as a result of checking the X509Certificate.verify() source code, it seems that the default value is used without using the parameter crypto.

if (!paramsKey) {
  // self-signed
  keyAlgorithm = { ...this.publicKey.algorithm, ...this.signatureAlgorithm };
  publicKey = await this.publicKey.export(keyAlgorithm, ["verify"], crypto);
} else if ("publicKey" in paramsKey) {
  // IPublicKeyContainer
  keyAlgorithm = { ...paramsKey.publicKey.algorithm, ...this.signatureAlgorithm };
  publicKey = await paramsKey.publicKey.export(keyAlgorithm, ["verify"]); // this
} else if (paramsKey instanceof PublicKey) {
  // PublicKey
  keyAlgorithm = { ...paramsKey.algorithm, ...this.signatureAlgorithm };
  publicKey = await paramsKey.export(keyAlgorithm, ["verify"]); // this
} else if (BufferSourceConverter.isBufferSource(paramsKey)) {
  const key = new PublicKey(paramsKey);
  keyAlgorithm = { ...key.algorithm, ...this.signatureAlgorithm };
  publicKey = await key.export(keyAlgorithm, ["verify"]); // this
} else {
  // CryptoKey
  keyAlgorithm = { ...paramsKey.algorithm, ...this.signatureAlgorithm };
  publicKey = paramsKey;
}

There is a workaround as follows, but it seems like a fix is needed.

cryptoProvider.set(new Crypto());
await certificate.verify({ publicKey: certificate.publicKey });

Parsing an x509 returns 'Cannot convert ASN.1 algorithm to WebCrypto algorithm'

I'm hoping to get some info on an error I'm running into when parsing a certificate using new x509.X509Certificate(). When I try to parse the below cert it throws an error Cannot convert ASN.1 algorithm to WebCrypto algorithm' - per the code this looks expected. but I was able to parse this with pkijs and `asn1js' I'm just wondering if this is intended or if there's a workaround.

cert:

-----BEGIN CERTIFICATE-----
MIICpzCCAhACAg4AMA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG
A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE
MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl
YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw
ODIyMDcyNjQzWhcNMTcwODIxMDcyNjQzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE
CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs
ZS5jb20wgfAwgagGByqGSM44BAEwgZwCQQDKVt7ZYtFRCzrm2/NTjl45YtMgVctQ
pLadAowFRydY13uhGw+JXyM+qmngfQkXImQpoYdIe+A8DWG2vaO3wKQ3AhUAxx6d
eaDs+XNHcbsiVQ1osvxrG8sCQHQYZDlSy/A5AFXrWXUNlTJbNhWDnitiG/95qYCe
FGnwYPp/WyhX+/lbDmQujkrbd4wYStudZM0cc4iDAWeOHQ0DQwACQDtK/S6POMQE
8aI+skBdNQn+Ch76kNDhztC/suOr9FbCSxnZ/CfhSgE1phOJyEkdR2jgErl3uh51
lo+7to76LLUwDQYJKoZIhvcNAQEFBQADgYEAnrmxZ3HB0LmVoFYdBJWxNBkRaFyn
jBmRsSJp2xvFg2nyAF77AOqBuFOFqOxg04eDxH8TGLQOWjqdyCFCY79AQlmkdB+8
Z5SWqPEwLJHVLd91O9avQwwRQT5TAxGXFkHTlQxOoaGfTsVQFqSDnlYC4mFjspA7
W+K8+llxOFmtVzU=
-----END CERTIFICATE-----

Intermittent ERR_OSSL_ASN1_ILLEGAL_PADDING error decoding certificates

I'm not quite sure what's going on here, but sometimes I can generate a self-signed x509 certificate that node's TLSSocket rejects with an ERR_OSSL_ASN1_ILLEGAL_PADDING error.

I think it's related to the serial number field, some values seem to not work

Here are some example certificates:

Serial number 80048117884272

-----BEGIN CERTIFICATE-----
MIIBdDCCARugAwIBAgIIN0FJVxNJMTcwCgYIKoZIzj0EAwIwADAeFw0yNDAzMjkw
NzAwNDlaFw0zNDAzMjcwODAwNDlaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AAQ1arYefooX/Y3HZBUmx0Z+rlpnR5u+N82aO+77a+qGk5jVArQteAlP9ejlnw/g
DT30Tf9CS5ZKQywIxLe+UBkoo38wfTB7BgorBgEEAYOiWgEBAQH/BGowaAQkCAES
ILTRpIzei9gsWWw9jlM8HUh52s5bJ6f1fIUT10JO0qFCBECXmPerzoBc9288gwRq
gOUaP7XWM7ptrzVyuk2ojUEJrId0gt+WBHL6451bBK65SPw+NS00Lh0MAIT72vrF
+FMAMAoGCCqGSM49BAMCA0cAMEQCIBmkSvawNuaQVbjHDvLAaJZhyXS+yKpOsjBf
o6Q6qCcUAiB6yQxYTQEiWTkjaRaeFTqwmg3WLLjMcL7TU4jx9JyTPg==
-----END CERTIFICATE-----

Serial number 80284629184668

-----BEGIN CERTIFICATE-----
MIIBdTCCARqgAwIBAgIHAChGKRhGaDAKBggqhkjOPQQDAjAAMB4XDTI0MDMyOTA4
MTA0MloXDTM0MDMyNzA5MTA0MlowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
BM+9jxG60ibilrh/tMckZnZlRy+YKr4wIsdCEVhrkohX3CzQGforRUWj3Gd+fgxC
sjzCqlE1+wEacO8WcG5BQXujfzB9MHsGCisGAQQBg6JaAQEBAf8EajBoBCQIARIg
+Tf7EqCTFrZpts0JeNgacbEtTw9i5eN8PXtE1X5Hi+wEQIAYNPej0VxkWcLPfU29
TUX0OF3binlvEDOXKbSgOgtRh84nMH7ZzxDkP+2nx11azPBG7fe+kfyHzStFer2W
gwAwCgYIKoZIzj0EAwIDSQAwRgIhALA1mqPIpAfV5F5Lbi5fziHlF8ZavyoGZOp2
uUXQW9xxAiEApLXWM+TKI5KkWBZ11yodUR++nao+013OeY1SoEi4YuQ=
-----END CERTIFICATE-----

Serial number 80290967596123

-----BEGIN CERTIFICATE-----
MIIBdDCCARqgAwIBAgIHACkJZ1lhIzAKBggqhkjOPQQDAjAAMB4XDTI0MDMyOTA5
Mzk0MFoXDTM0MDMyNzEwMzk0MFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
BJwqZR4Mm9VX9qAp7vLRqXixbrN5ijc59+dAY2kKZZ/73mr1TKymgpmtB1s4/kDr
MxhpCsanxaLTkQmHEK9EwAijfzB9MHsGCisGAQQBg6JaAQEBAf8EajBoBCQIARIg
qNcdQX5sb5EnD38brTMGBxtMJ8ySzQcy85P1CYgjpycEQIsgOFHnDyFMpfqybhrO
7akur71S94GtSeyspn7ye1I6qumJffhVKZbJxf0dbhwu8z5ca22gPZt4UuOh4ayt
UAgwCgYIKoZIzj0EAwIDSAAwRQIgGvZQf/hL0mPGVzw8vC2KxItclYXIOnsU/aka
iYdwuY0CIQDEBHE84znRpGCwzAhmMnhwM5JvTyMi1jBijZhJ5KvPMA==
-----END CERTIFICATE-----

Serial number 8070459553297620

-----BEGIN CERTIFICATE-----
MIIBdjCCARugAwIBAgIIAHBFlVMpdiAwCgYIKoZIzj0EAwIwADAeFw0yNDAzMjkx
MDQ5MDJaFw0zNDAzMjcxMTQ5MDJaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AARccRhCabh1rQMNXr1zz/jB4Hot/n+VIL4IysnGaBoaEZLo2vqpoWhXgH2Sz7/5
FzvRofDNlDtK5Jp8su+1OoWeo38wfTB7BgorBgEEAYOiWgEBAQH/BGowaAQkCAES
IMZ/hZH1YUoiVmUjNMmxKxp7rzkP1K/cCTPOT481M7xLBEB8eUQ79y7vUgwggOG4
J0pEqTRVi1rh0XzcFUZuwhdA3/TQ/He/1KNSQ2z2x+hxCKnoWzt68jPA2gwNuWRu
qMUEMAoGCCqGSM49BAMCA0kAMEYCIQCQbANLI26dzckKMIWaYIjme6/tBMNAtLXo
3oWWyt3EfgIhAKD8K6As+3sZ0p1SjiLi/BX8x+wAU6NI42SW8ua63doP
-----END CERTIFICATE-----

Weirdly they all begin with 80, I don't know if that means anything or it's just a coincidence.

Serial number 801234

-----BEGIN CERTIFICATE-----
MIIBMjCB2aADAgECAgMAEjQwCgYIKoZIzj0EAwIwIDENMAsGA1UEAxMEVGVzdDEP
MA0GA1UECgwG0JTQvtC8MB4XDTIwMDEwMTAwMDAwMFoXDTIwMDEwMjAwMDAwMFow
IDENMAsGA1UEAxMEVGVzdDEPMA0GA1UECgwG0JTQvtC8MFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEUwBK1GpQJ4VxwktgsRBbl+xcy3Pk6vCKxUDTEuaz49tA1mR/
1JFIh4t0KTrmuLuX0nA8+gc/VKIyDH1APccz4aMCMAAwCgYIKoZIzj0EAwIDSAAw
RQIhAPf7yZpUymNFDOT/SPdtUg9VSXDe5UE6PlxAxyxVXnkfAiBCEV2SOWFKSmPS
NhUpJtcMgxHupLeCWrFon6SSGUDJRw==
-----END CERTIFICATE-----

Here's a reproduction. The serial numbers from above cause new TLSSocket to throw Error: error:068000DD:asn1 encoding routines::illegal padding, the default serial number of "01" does not.

import * as x509 from '@peculiar/x509'
import { Crypto } from '@peculiar/webcrypto'
import { Socket } from 'net'
import { TLSSocket } from 'tls'

const crypto = new Crypto()
x509.cryptoProvider.set(crypto)

const keys = await crypto.subtle.generateKey({
  name: 'ECDSA',
  namedCurve: 'P-256',
}, true, ['sign'])

const cert = await x509.X509CertificateGenerator.createSelfSigned({
  // will throw
  serialNumber: '80048117884272',

  // does not throw
  //serialNumber: '01',

  // ...other certificate parameters
  name: 'CN=Test, O=ะ”ะพะผ',
  notBefore: new Date('2020/01/01'),
  notAfter: new Date('2020/01/02'),
  signingAlgorithm: {
    name: 'ECDSA',
    hash: 'SHA-256',
  },
  keys: keys
})

// throws with certain serial numbers
new TLSSocket(new Socket(), {
  cert: cert.toString(),
  key: await privateKeyToPEM(keys)
})

// helper to transform a private key to PEM format
async function privateKeyToPEM (keys) {
  const arrayBuffer = await crypto.subtle.exportKey('spki', keys.privateKey)
  let str = Buffer.from(arrayBuffer).toString('base64')
  let finalString = '-----BEGIN PRIVATE KEY-----\n'

  while (str.length > 0) {
    finalString += str.substring(0, 64) + '\n'
    str = str.substring(64)
  }

  finalString = finalString + '-----END PRIVATE KEY-----'

  return finalString
}

Incorrect time format for expiry dates after 2050?

I'm creating certificates with expiry dates in the far future.

import * as x509 from '@peculiar/x509'

const selfCert = await x509.X509CertificateGenerator.createSelfSigned({
  notBefore: new Date(now - CERT_VALIDITY_PERIOD_FROM),
  notAfter: new Date(now + CERT_VALIDITY_PERIOD_TO),
  //... other params
})

Trying to use them over TLS results in errors on the remote like:

failed to negotiate security protocol: tls: failed to parse certificate from server: x509: malformed GeneralizedTime

From what I understand X.509 has two time representations, UTCTime if the date is 2049 or earlier, and GeneralizedTime if the date is 2050 or later.

If I make the expiry date before 2049 everything works as expected.

Could this module be representing everything as UTCTime?

Find extension by OID/type

Hello! Thank you for this library. ๐Ÿ™‡๐Ÿผโ€โ™€๏ธ

Do we have support for finding a certificate extension given a oid (in the case of Extension class, that would be type)?
I would like to create a util for it if it does not exist yet. Thank you

createSelfSigned method causes out of memory crash in Node 16.x

Values taken from browser example:

 const algorithm = {
        name: "RSASSA-PKCS1-v1_5",
        hash: "SHA-256",
        modulusLength: 2048,
        publicExponent: new Uint8Array([1, 0, 1])
    }
 
 const caKeys = await generateKey(algorithm, true, ["sign", "verify"]);

 const caCert = await x509.X509CertificateGenerator.createSelfSigned({
        serialNumber: "01",
        subject: "CN=Test certificate, [email protected]",
        notBefore: new Date("2020/01/01"),
        notAfter: new Date("2022/01/02"),
        signingAlgorithm: {
            "name": "RSASSA-PKCS1-v1_5",
            "hash": "SHA-256",
            "modulusLength": 2048,
            "publicExponent": {
                "0": 1,
                "1": 0,
                "2": 1
            }
        },
        keys: caKeys,
        extensions: [
            {
                "rawData": {},
                "type": "2.5.29.19",
                "critical": true,
                "value": {},
                "ca": false
            },
            {
                "rawData": {},
                "type": "2.5.29.15",
                "critical": true,
                "value": {},
                "usages": 9
            },
            {
                "rawData": {},
                "type": "2.5.29.14",
                "critical": false,
                "value": {},
                "keyId": "84cb7d152ef9ebb1eef48a300b9e4b8470590b4a"
            }
        ]
    });

challengePassword attribute issue?

Hey! ๐Ÿ‘‹๐Ÿป

I'm working on switching to this library from Forge for CSR generation, and I'm running into a subtle difference with the challengePassword attribute that I think is causing the CSR to be rejected.

If you look at the output below, you'll see that they're almost identical (UID and challengePassword values are different on purpose), except for how OpenSSL outputs the challengePassword attribute. Like I said, my best guess at the moment is that this is causing the CSR to be rejected.

Can you help? Thanks!

CSR from Forge

When I run the CSR through OpenSSL (openssl req -noout -text -in csr.pem), I get this:

Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: CN=Truepic Lens SDK v0.1.0 in Lens Demo v1.0.0, OU=Development Org Unit, O=Development Org, C=US/UID=cs-cf338b81e63274f810cec6f9aa61675b
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c5:21:6d:96:51:78:b6:8a:7b:d4:13:c2:85:fe:
                    99:0e:4f:b0:2b:72:0b:35:f4:bb:64:c0:af:89:91:
                    ae:c5:8f:d0:dc:51:63:94:80:32:bb:c9:a9:35:eb:
                    6d:6f:47:fd:cb:33:dc:2d:95:87:9b:f0:7d:65:1a:
                    c9:fa:18:cf:e1:c4:55:aa:8c:82:6b:a4:c3:04:67:
                    22:4b:b7:e0:10:ac:c0:03:53:f4:77:87:74:11:29:
                    48:39:84:60:87:fa:2f:be:35:58:aa:2a:88:a1:06:
                    29:28:f7:5d:cb:71:5d:f3:98:0a:91:b5:56:02:a0:
                    95:4d:a5:a2:3e:cb:90:02:12:a6:a7:39:d8:b2:91:
                    56:9a:6f:0b:d4:5a:99:91:a6:30:d4:fa:ab:22:26:
                    fa:51:19:56:40:63:7d:44:e0:fe:c0:33:2b:cf:5b:
                    07:8c:ca:1c:51:54:36:69:69:56:a7:69:aa:85:55:
                    8c:85:e6:e2:1a:f6:b6:0d:86:48:2a:98:f7:b1:36:
                    3f:20:a4:70:c6:7d:8f:31:97:12:71:e4:7b:6e:44:
                    2f:dd:50:1e:ce:87:1a:a6:1a:8a:bf:ec:f8:42:ae:
                    df:c8:c0:19:da:69:db:fb:58:97:01:54:0b:43:33:
                    cd:ad:bc:eb:28:69:88:1d:e8:6c:20:ee:d4:c9:6c:
                    c6:03
                Exponent: 65537 (0x10001)
        Attributes:
            challengePassword        :unable to print attribute
    Signature Algorithm: sha384WithRSAEncryption
         98:58:bb:2d:d6:ba:54:87:f8:9a:b2:ea:1b:4d:f0:89:0b:ab:
         27:25:d7:0d:93:0e:7a:e9:d9:0a:a6:cb:e4:84:30:6a:dc:b5:
         7e:c6:7b:05:94:0d:03:14:af:aa:ba:89:2a:06:ae:3d:ee:12:
         ea:1f:1e:54:96:37:5f:91:38:a3:41:b7:d1:e5:45:3f:6d:3a:
         d8:3a:39:e5:e6:e8:9e:d5:ae:0a:ad:4e:91:95:f2:29:b3:31:
         a0:de:4b:9c:45:9a:44:02:f3:e5:ba:8c:3a:89:e4:47:c4:7b:
         a5:4d:0a:7e:d7:7a:d4:05:26:f0:d4:53:0a:80:1f:1e:36:1c:
         7e:68:09:cf:ae:7c:79:a9:53:f3:85:55:65:f5:df:01:68:a2:
         e1:df:c8:35:2e:7d:72:64:3e:b0:b2:98:be:5b:71:3a:27:fb:
         9a:8d:2e:36:15:34:51:5d:b4:d7:f5:c4:8e:ea:a8:5e:07:f0:
         91:49:4e:19:20:eb:03:63:76:f7:28:74:e4:ae:04:98:4e:bd:
         eb:7c:3f:1c:3f:68:9b:c2:88:08:8c:93:be:7a:9f:26:84:88:
         54:64:18:d5:5b:c1:58:56:35:d9:b4:6a:fb:62:e2:1c:16:ad:
         94:b7:9d:14:90:27:03:7c:70:4d:97:4c:ea:1b:d1:dc:44:76:
         c5:2f:07:5d

Here's how this library parses it:

Pkcs10CertificateRequest {
  rawData: ArrayBuffer {
    [Uint8Contents]: <30 82 03 28 30 82 02 10 02 01 00 30 81 b1 31 34 30 32 06 03 55 04 03 13 2b 54 72 75 65 70 69 63 20 4c 65 6e 73 20 53 44 4b 20 76 30 2e 31 2e 30 20 69 6e 20 4c 65 6e 73 20 44 65 6d 6f 20 76 31 2e 30 2e 30 31 1d 30 1b 06 03 55 04 0b 13 14 44 65 76 65 6c 6f 70 6d 65 6e 74 20 4f 72 67 20 55 6e 69 74 31 ... 712 more bytes>,
    byteLength: 812
  },
  tbs: ArrayBuffer {
    [Uint8Contents]: <30 82 02 10 02 01 00 30 81 b1 31 34 30 32 06 03 55 04 03 13 2b 54 72 75 65 70 69 63 20 4c 65 6e 73 20 53 44 4b 20 76 30 2e 31 2e 30 20 69 6e 20 4c 65 6e 73 20 44 65 6d 6f 20 76 31 2e 30 2e 30 31 1d 30 1b 06 03 55 04 0b 13 14 44 65 76 65 6c 6f 70 6d 65 6e 74 20 4f 72 67 20 55 6e 69 74 31 18 30 16 06 ... 432 more bytes>,
    byteLength: 532
  },
  publicKey: PublicKey {
    rawData: ArrayBuffer {
      [Uint8Contents]: <30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c9 73 75 99 76 4f 66 9e be 81 d9 d7 2e 6e 64 3c 2d 99 f1 a6 90 ee 8d b6 40 9b 7b 72 62 5c b4 5e 8d 98 0a b8 22 0b dc 21 a7 ba 5f ac c4 74 9d cf 73 6e b2 a7 3c 00 ce 41 f6 8b d0 1c 27 72 36 c0 64 75 5c ... 194 more bytes>,
      byteLength: 294
    },
    algorithm: {
      name: 'RSASSA-PKCS1-v1_5',
      publicExponent: [Uint8Array],
      modulusLength: 2048
    },
    tag: 'PUBLIC KEY'
  },
  signatureAlgorithm: { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-384' } },
  signature: ArrayBuffer {
    [Uint8Contents]: <06 40 7e 70 1f 41 10 c2 e9 29 2c da 9e 48 ed f7 2f e8 cb 67 26 f8 0a 91 d0 51 aa cd 55 de a4 ec 5a c0 a7 7d 49 8d 50 af f2 01 ed 5a 8e a7 9a bc 46 69 0f 66 95 71 9d a9 2f 55 20 bb 10 dc d7 97 bc 25 62 89 56 9b 2a ee 5d 7e 4e 8d cf 21 1a d8 ed b0 a9 6d 61 c4 38 05 6f f6 2a 11 90 6e ae f7 93 08 8b c8 ... 156 more bytes>,
    byteLength: 256
  },
  attributes: Attributes(1) [
    ChallengePasswordAttribute {
      rawData: [ArrayBuffer],
      type: '1.2.840.113549.1.9.7',
      values: [Array],
      password: '559b06e9b9f0a2e157a31d7ff8671d45'
    }
  ],
  extensions: [],
  subjectName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(5) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  subject: 'CN=Truepic Lens SDK v0.1.0 in Lens Demo v1.0.0, OU=Development Org Unit, O=Development Org, C=US, 0.9.2342.19200300.100.1.1=cs-cb2500d6dc56d7e69bfe0720de317c1b',
  tag: 'CERTIFICATE REQUEST'
}

CSR from this library

When I run the CSR through OpenSSL (openssl req -noout -text -in csr.pem), I get this:

Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: CN=Truepic Lens SDK v0.1.0 in Lens Demo v1.0.0, OU=Development Org Unit, O=Development Org, C=US/UID=cs-ee1b2a5a06878bca6a230f648ee44e9f
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:bf:9e:6d:fe:fc:9b:c2:de:c2:c2:18:c2:17:84:
                    a2:a9:40:07:73:5f:da:9f:c9:f2:86:af:58:d2:53:
                    8d:55:95:87:37:5d:71:e8:66:32:e3:63:a6:2b:3d:
                    7b:64:90:4f:33:81:fa:b9:9b:0a:a9:47:38:c2:1c:
                    bc:81:3c:01:c5:af:b9:1f:88:f1:0a:e6:c4:20:4a:
                    e8:38:36:ce:75:15:f1:e4:3b:43:a1:a7:9c:4d:f9:
                    4c:9d:96:b6:bc:3c:38:a0:ae:40:1a:1d:50:f3:a9:
                    cd:b3:aa:ef:f0:99:8c:ae:74:68:93:3a:45:34:53:
                    d1:80:45:88:f3:97:74:c6:8a:13:ac:a6:e4:0c:cc:
                    52:3f:0f:69:95:4a:f0:f7:e2:64:75:47:13:f3:fe:
                    c7:3d:bc:e4:a9:e5:2c:de:ad:17:31:d9:15:62:eb:
                    16:3a:a1:25:80:85:ab:3d:94:3b:b8:07:67:aa:42:
                    33:d7:ec:c9:07:7a:af:73:59:1a:e0:50:1e:07:97:
                    59:3f:b0:25:ed:b0:29:50:79:b1:57:40:6a:01:09:
                    ef:d4:14:5f:ee:9e:3c:6c:be:9e:03:b1:98:72:a6:
                    1c:a3:e3:15:b1:96:91:d7:81:53:b8:b4:e4:95:47:
                    e4:bf:a3:e1:fe:84:dd:48:ec:cb:e2:4f:8b:ef:43:
                    dc:f3
                Exponent: 65537 (0x10001)
        Attributes:
            challengePassword        :87d9ea7672cd0551e0e5e297ddf01232
    Signature Algorithm: sha384WithRSAEncryption
         a5:a6:74:09:d1:3f:39:8b:63:ab:64:b8:e4:d6:7f:63:23:ec:
         8c:47:c1:b2:b9:ce:bb:d8:b7:00:d1:51:d6:48:59:48:1a:bc:
         e2:60:8a:b7:d2:8c:11:2e:6e:b4:bb:b2:a2:41:17:98:77:93:
         10:71:fd:2d:d1:87:c2:d2:db:6b:60:04:c1:39:c6:49:c8:70:
         22:85:c4:08:9a:dd:f5:29:03:ba:52:e7:2f:3e:14:f5:7f:02:
         62:df:a4:c1:bc:18:c5:9a:a6:8e:b6:06:bd:01:f4:66:b9:30:
         43:a2:c5:81:b5:b7:49:8d:04:19:7e:ff:46:9a:ad:d7:a3:e4:
         9f:ed:eb:dc:73:b9:00:19:61:1b:85:81:b0:f4:f4:41:27:c9:
         05:92:6b:8d:c1:12:96:3f:0f:d9:9e:98:47:8f:cb:ad:81:7e:
         a1:9e:61:68:2a:cb:4f:0a:fa:00:1b:d1:70:3d:84:95:59:17:
         bb:b4:b9:1e:9b:c1:11:36:8d:f4:b9:8a:f2:b8:4b:17:b3:06:
         d6:a7:b4:30:f6:fc:db:0f:89:29:f0:09:bf:91:85:f0:fe:82:
         41:66:7b:4c:45:80:cb:fb:fc:4d:fc:a7:40:78:ba:96:0f:f4:
         10:46:4f:5b:3d:cb:4a:2d:d6:f4:b5:0b:fe:2d:3d:56:e7:ea:
         8b:86:d5:07

Here's the CSR object from this library:

Pkcs10CertificateRequest {
  rawData: ArrayBuffer {
    [Uint8Contents]: <30 82 03 1b 30 82 02 03 02 01 00 30 81 a4 31 34 30 32 06 03 55 04 03 13 2b 54 72 75 65 70 69 63 20 4c 65 6e 73 20 53 44 4b 20 76 30 2e 31 2e 30 20 69 6e 20 4c 65 6e 73 20 44 65 6d 6f 20 76 31 2e 30 2e 30 31 1d 30 1b 06 03 55 04 0b 13 14 44 65 76 65 6c 6f 70 6d 65 6e 74 20 4f 72 67 20 55 6e 69 74 31 ... 699 more bytes>,
    byteLength: 799
  },
  tbs: ArrayBuffer {
    [Uint8Contents]: <30 82 02 03 02 01 00 30 81 a4 31 34 30 32 06 03 55 04 03 13 2b 54 72 75 65 70 69 63 20 4c 65 6e 73 20 53 44 4b 20 76 30 2e 31 2e 30 20 69 6e 20 4c 65 6e 73 20 44 65 6d 6f 20 76 31 2e 30 2e 30 31 1d 30 1b 06 03 55 04 0b 13 14 44 65 76 65 6c 6f 70 6d 65 6e 74 20 4f 72 67 20 55 6e 69 74 31 18 30 16 06 ... 419 more bytes>,
    byteLength: 519
  },
  publicKey: PublicKey {
    rawData: ArrayBuffer {
      [Uint8Contents]: <30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 e0 04 91 d4 07 22 69 3a 9f 57 cb 5b 27 fc 3b 6d 10 d9 e2 7e 29 ca 4b 80 39 c9 76 7c 88 0b 3f 7b ff f8 2a 35 e4 19 9f ec 52 0c df 8a 2a d6 22 6f 48 92 97 51 34 4a 48 2c 5d 72 74 52 fd 65 be 71 20 1d 53 ... 194 more bytes>,
      byteLength: 294
    },
    algorithm: {
      name: 'RSASSA-PKCS1-v1_5',
      publicExponent: [Uint8Array],
      modulusLength: 2048
    },
    tag: 'PUBLIC KEY'
  },
  signatureAlgorithm: { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-384' } },
  signature: ArrayBuffer {
    [Uint8Contents]: <0f d3 c3 80 81 9b ce 17 70 c7 9d a1 a3 4e 1f 60 e4 6d ac 57 39 86 ba 9f b7 c1 de 74 1c 1c cc 95 92 80 b6 61 22 fd 2d 35 0f 97 c1 44 0b b9 c1 bc d5 c9 75 8f ae cf 78 b6 7f 54 db 4d 4c ac 3d d1 fa 11 84 e3 0e 97 cb a5 2b c3 5e 84 01 28 4f 7d 37 98 b2 c7 d6 c4 6b 5e 2a df f7 b7 fe 00 ac 59 2c 23 b9 cc ... 156 more bytes>,
    byteLength: 256
  },
  attributes: Attributes(1) [
    ChallengePasswordAttribute {
      rawData: [ArrayBuffer],
      type: '1.2.840.113549.1.9.7',
      values: [Array],
      password: '87d9ea7672cd0551e0e5e297ddf01232'
    }
  ],
  extensions: [],
  subjectName: Name {
    extraNames: NameIdentifier { items: {} },
    asn: Name(4) [
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName],
      [RelativeDistinguishedName]
    ]
  },
  subject: 'CN=Truepic Lens SDK v0.1.0 in Lens Demo v1.0.0, OU=Development Org Unit, O=Development Org, C=US/UID=cs-ee1b2a5a06878bca6a230f648ee44e9f',
  tag: 'CERTIFICATE REQUEST'
}

`RsaAlgorithm.toAsnAlgorithm` throws error for string type `hash` algorithm

Describe the bug:
The method RsaAlgorithm.toAsnAlgorithm in rsa_algorithm.ts throws an error when the hash property in the algorithm object is a string, rather than an AlgorithmIdentifier.

To Reproduce:

  1. Set the algorithm object as:
const algorithm = {
  name: "RSASSA-PKCS1-v1_5",
  hash: "SHA-256",
};
  1. Use the RsaAlgorithm.toAsnAlgorithm method.
  2. An error is thrown.

Expected behavior:
The method should handle string values for hash.

Reference:

switch (alg.hash.name.toLowerCase()) {

Additional context:
Allowing string types for the hash property could improve the user experience and reduce confusion.

ReferenceError: Buffer is not defined

Hello,

I get this error message ReferenceError: Buffer is not <anonymous> webcrypto.es.js:244. This is my example code.

import * as x509 from "@peculiar/x509";
import { Crypto } from "@peculiar/webcrypto";

const crypto = new Crypto();
x509.cryptoProvider.set(crypto);

const algorithm = {
  name: "RSASSA-PKCS1-v1_5",
  hash: "SHA-256",
  publicExponent: new Uint8Array([1, 0, 1]),
  modulusLength: 2048,
 };
 const keys = crypto.subtle.generateKey(algorithm, false, ["sign", "verify"]);
 console.log(keys)

This are my package.json file

 "dependencies": {
   "@peculiar/webcrypto": "^1.4.3",
   "@peculiar/x509": "^1.9.5",
   "vue": "^3.3.4",
   ...
   },
   "devDependencies": {
     "vite": "4.4.9",
   },

The error is already thrown with the line const crypto = new Crypto();.

Example for validating a certificate chain

@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.

I would like to check whether a given x.509 certificate has been signed by a known root certificate (CA). Can this library be used for that?

Package import is empty

Package version

Usage example

import * as x509 from '@peculiar/x509';

console.log(x509); // -> {default: {}}

Usage environment

Project created using https://create-react-app.dev/.

Problem description

If your module is meant to be used client-side the browser field should be used instead of the main field. This is helpful to hint users that it might rely on primitives that aren't available in Node.js modules. (e.g. window)

Based on https://docs.npmjs.com/cli/v7/configuring-npm/package-json#browser.

Solution

Use:

"unpkg": "build/x509.js",

Instead of:

"browser": "build/x509.js",

https://github.com/PeculiarVentures/x509/blob/master/package.json#L7

Seemingly invalid AlgorithmIdentifier ASN.1 encoding

Hello there,

I was trying to use the ecdsa-with-SHA256 algorithm to sign my certificates, but it seems the ASN.1 encoding generated includes a NULL attribute. For example here is the beginning of a certificate as generated by this library:

    0:d=0  hl=4 l= 449 cons: SEQUENCE
    4:d=1  hl=4 l= 357 cons:  SEQUENCE
    8:d=2  hl=2 l=   3 cons:   cont [ 0 ]
   10:d=3  hl=2 l=   1 prim:    INTEGER           :02
   13:d=2  hl=2 l=   8 prim:   INTEGER           :19F2B75E93D0686D
   23:d=2  hl=2 l=  12 cons:   SEQUENCE
   25:d=3  hl=2 l=   8 prim:    OBJECT            :ecdsa-with-SHA256
   35:d=3  hl=2 l=   0 prim:    NULL
   37:d=2  hl=2 l=  34 cons:   SEQUENCE

I generated another certificate through another source and that one doesn't have the NULL attribute:

    0:d=0  hl=4 l= 480 cons: SEQUENCE
    4:d=1  hl=4 l= 390 cons:  SEQUENCE
    8:d=2  hl=2 l=   3 cons:   cont [ 0 ]
   10:d=3  hl=2 l=   1 prim:    INTEGER           :02
   13:d=2  hl=2 l=   8 prim:   INTEGER           :19F2B75E93D0686D
   23:d=2  hl=2 l=  10 cons:   SEQUENCE
   25:d=3  hl=2 l=   8 prim:    OBJECT            :ecdsa-with-SHA256
   35:d=2  hl=2 l=  34 cons:   SEQUENCE

Reading RFC3279, it seems the parameters field must not be present. I modified

x509/src/ec_algorithm.ts

Lines 21 to 29 in 69ee3ff

switch (hash.toLowerCase()) {
case "sha-1":
return new AlgorithmIdentifier({ algorithm: asn1Ecc.id_ecdsaWithSHA1, parameters: null });
case "sha-256":
return new AlgorithmIdentifier({ algorithm: asn1Ecc.id_ecdsaWithSHA256, parameters: null });
case "sha-384":
return new AlgorithmIdentifier({ algorithm: asn1Ecc.id_ecdsaWithSHA384, parameters: null });
case "sha-512":
return new AlgorithmIdentifier({ algorithm: asn1Ecc.id_ecdsaWithSHA512, parameters: null });
locally by using undefined instead of null and it seems to remove the NULL field.

Is it something you'd consider accepting in the library? If not, is there a way to overrides the default algorithm mapping ?

Thanks !

PEM converter should allow DOS line endings

Hello,

I have a PEM file with DOS/Windows line endings that is rejected by the PEM Converter.
Could you please consider accepting \r\n line endings as well?

Thanks,
Gregor

How to define new OID

How do I define this OID:
MatterRCACId = 1.3.6.1.4.1.37244.1.4

So that I can use it in the name field like this:
name: "MatterRCACId = CACACACA00000001",

I see asn1X509.RelativeDistinguishedName() in the test case, but how do I get it into the name field?

image

Support comments for PEM format

Subject: CN=Some
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

or

-----BEGIN CERTIFICATE-----
Subject: CN=Some
...
-----END CERTIFICATE-----

X509Certificate fingerprint

Hiow can i get formatted fingerprint of certificate ?

Foe example i have following certificate:

-----BEGIN CERTIFICATE-----
MIICxDCCAaygAwIBAgIIBKsgcOucPtIwDQYJKoZIhvcNAQELBQAwIjEgMB4GA1UE
AxMXUG9ydG5veCBTU08gQ2VydGlmaWNhdGUwHhcNMjQwNjExMTY1MjIzWhcNMjcw
NjExMTY1MjIzWjAiMSAwHgYDVQQDExdQb3J0bm94IFNTTyBDZXJ0aWZpY2F0ZTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMNqit0JSoscHlrO+ggUpww1
bmv1XF7Qz2y/VFxPNSKE9WMAYbdYlV9m5ZF0dpPdozvbDYYr60H6s0FJYt42951r
NGFKI+EwQs7kTv+LgIvXPTvI0CKnO+ULzDtjeE618ouKKLmULJBilr5THnngrjdd
jTLd1jYUlVa2w9C7A1RZeBwJVxG8jTbfVzZReVir7Iu0k1nxxjiOuvSj1oqfH+NT
mLthwXBuYpSH0wteU2TcXyJYOCkmYklWVB5d4z0W6t8G8i+GmIpnoA9db7NYDmbj
iMyxgv0DSWLWHdtbDyDgP/8n9X5uTxdvcADK1iE1vAlK1dbibf9Pw2bt7yLKUZcC
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAocTLQoE+ERTNES36lykJXq05ryiBMDda
xlEvSsOzbRWxRPPZgBHlMYlv+CKQGyM3nv2VClGVMbx5TdVB7NkrxbmFJoNxjjyk
HkZa0lvyYxDgJVsCj2l9az1jFCU9KGJ++LAeCVwgrTBgFODAUYBA3ErdDgYKI2P9
C4HHf2ytQpBX1201NCHULZG+bduJIiKW2ourCfulppbM1M4AwSbsDw+efyKd9Gn7
4wJiQFiYXsgKkWnb39Cs9WKJi6DGyJV2/cQ9pR8y3KBz4WIddRmj6VAUBrvKvOR5
vpM5dy8b/U5Bsb4SO5S0Idx69+/tko/WUgxIUyhIXPHFh4+AE3+uaA==
-----END CERTIFICATE-----

I try to use some online services that detect certificate fingerprint, and what i have with mentioned above certificate:
sha-1: eab12636cfe4526d0c2c3a615ac73fc0689e2d8d.

I'm just asking how can i do the same with @peculiar/x509 library.

Example of how to create a custom x509 extension

I'm coming from using node-forge, where it was relatively easy to create custom extensions.

  extensions.push({
    name: 'nodeSignature',
    id: config.oids.extensions.nodeSignature,
    critical: true,
    value: asn1.create(
      asn1.Class.APPLICATION,
      asn1.Type.OCTETSTRING, // or asn1.type.IA5STRING
      false,
      nodeSignature,
    ),
  });

I'm looking for a way to create a custom extension with this library.

I found that most of the extensions classes extend the Extension class.

However they all seem to take values that are already encoded ASN1 objects as buffer sources.

The @peculiar/asn1-schema doesn't have a lot of documentation. The only example is KeyUsage which ends up using @peculiar/asn1-x509 https://github.com/PeculiarVentures/asn1-schema/blob/master/packages/x509/src/extensions/key_usage.ts. But it's not clear if BitString is supposed to be a raw byte string that is already encoded.

Is there an ASN1 builder available to be used here that can do the above?

Constructing certificates with a Buffer bypasses PEM parsing

Stack trace:

Error: Constructed encoding used for primitive type
    at Function.parse (/Users/xyz/repos/project/node_modules/@peculiar/asn1-schema/build/cjs/parser.js:27:19)
    at Function.parse (/Users/xyz/repos/project/node_modules/@peculiar/asn1-schema/build/cjs/convert.js:13:35)
    at new AsnData (/Users/xyz/repos/project/node_modules/@peculiar/x509/build/x509.cjs.js:73:47)
    at new PemData (/Users/xyz/repos/project/node_modules/@peculiar/x509/build/x509.cjs.js:277:13)
    at new X509Certificate (/Users/xyz/repos/project/node_modules/@peculiar/x509/build/x509.cjs.js:588:13)

Expected result: When I construct a new X509Certificate with a Buffer containing a PEM file, I get back an X509Certificate object
Actual result: Execution fails with the above stack trace.

I think I've tracked this down to a call to toArrayBuffer in PemData (https://github.com/PeculiarVentures/x509/blob/master/src/pem_data.ts#L20), where the input is checked to see if it is a string. Only strings then get checked to see if they are a PEM file.

The workaround is to call .toString() on the Buffer before constructing an X509Certificate object.

Output of certificate cannot be used in `tls.checkServerIdentity()`

I have a certificate I would like to verify against the root nodejs store. The certificate is in PEM format.

I attempted to use the built in nodejs function tls.checkServerIdentity() after I used the x509 parser, but I get the following error:

Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: messageverificationcerts.sandbox.paypal.com. is not cert's CN: undefined

Source code:

  const x509 = require('@peculiar/x509');
  const cert = new x509.X509Certificate(relevantCert);
  //console.log(util.inspect(cert,false,6)) // appears to output a correct cert
  const tlsResult = tls.checkServerIdentity('messageverificationcerts.sandbox.paypal.com',  cert)
  console.log(tlsResult);

Can I create a set of certificates?

Hey :)

I'm trying to create a set of certificates, which can be drawn as below:

  • RootCert
  • Child cert, signed by root cert
    

I'm struggling with that...

For RootCert, I'm building it like that:

const alg = {
    name: 'RSASSA-PKCS1-v1_5',
    hash: 'SHA-256',
    publicExponent: new Uint8Array([1, 0, 1]),
    modulusLength: 4096,
};

const keys = await crypto.subtle.generateKey(alg, true, ['sign', 'verify']);

const cert = await x509.X509CertificateGenerator.createSelfSigned({
        serialNumber: '01',
        name: 'CN=Test',
        notBefore: new Date('2020/01/01'),
        notAfter: new Date('2020/01/02'),
        signingAlgorithm: alg,
        keys,
        extensions: [
            new x509.BasicConstraintsExtension(true, 2, true),
            new x509.ExtendedKeyUsageExtension(
                ['1.2.3.4.5.6.7', '2.3.4.5.6.7.8'],
                true
            ),
            new x509.KeyUsagesExtension(
                x509.KeyUsageFlags.digitalSignature |
                    x509.KeyUsageFlags.keyEncipherment |
                    x509.KeyUsageFlags.cRLSign |
                    x509.KeyUsageFlags.keyCertSign,
                true
            ),
            await x509.SubjectKeyIdentifierExtension.create(keys.publicKey),
        ],
    });

When it comes to create a child cert, I'm trying to do the following:

const keys2 = await crypto.subtle.generateKey(alg, false, ['decrypt']);

const c2 = await x509.X509CertificateGenerator.create({
        publicKey: keys2.publicKey,
        serialNumber: '01',
        signingKey: cert.privateKey!,
        notBefore: new Date('2020/01/01'),
        notAfter: new Date('2020/01/02'),
        signingAlgorithm: alg,
        extensions: [
            await x509.SubjectKeyIdentifierExtension.create(keys2.publicKey),
        ],
    });

But.. I couldn't say I've been successful!

Is it possible to achieve what I'm trying to do? If so, what am I missing?

Easier way to look up name attributes

Right now looking up name attributes requires going through the JSON structure and finding it. There should be an easier way to do this with something like cert.subject.getField.

Also the name system seems really flexible. You can have duplicate CN properties. I'm wondering if x509 really allows this, and perhaps by constraining the Name a bit, it would be easier to look up attributes in the subject and issuer names.

PrintableString, UTF8String & OCSP

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?

can not set expiry of a Self signed certificate after 2049-12-31T23:59:59Z

If I create a certificate with the following code snippet and check the notAfter of the resulting publicKey it returns 'Bad time value'.

So the max notAfter is new Date('2049-12-31T23:59:59Z')

const cert = await x509.X509CertificateGenerator.createSelfSigned({
    serialNumber: '01',
    notBefore: new Date(),
    notAfter: new Date('2149-12-31T23:59:59Z'),
    signingAlgorithm: alg,
    keys: keys,
    extensions,
  });

can we make this work for dates after 2049-12-31T23:59:59Z

Can't use version 1.5.1 NPM package for browser mode

Hello,

We were just updating from 1.4 to 1.5 and it selected 1.5.1 but it breaks the build:

ERROR in ../jslib/node_modules/@peculiar/x509/build/x509.es.js 36:0-38
Module not found: Error: Can't resolve 'webcrypto-core' in '/jslib/node_modules/@peculiar/x509/build'
resolve 'webcrypto-core' in '/jslib/node_modules/@peculiar/x509/build'

Digging into recent commits, sounds like updating webcrypto-core dev-dependency (90bce88) and using it later on (db1917e) are the cause of this break.

Edit: forcing dependency to 1.5.0 works well

Add support for RSA-PSS

The RSA algorithm provider currently does not support RSA-PSS. This leads to incorrect behaviour, e.g. verification of a certificate signed with RSA-PSS fails

signature verification failing for x509 certificate with ECDSA signature algorithm

signature verification is failing for x509 certificate.

Current Behaviour:- isSame is coming false
Expected Behaviour:- isSame should be true

code

     import { writeFileSync, readFileSync } from 'fs';
     import { join } from 'path';
     import * as x509 from '@peculiar/x509';
     import { Crypto } from '@peculiar/webcrypto';
     
     const crypto = new Crypto();
     x509.cryptoProvider.set(crypto);
     
     const chain = new x509.X509Certificate(
      readFileSync(join('chain.pem')).toString()
    );
    const cert = new x509.X509Certificate(
      readFileSync(join('cert.pem')).toString()
    );
    const publicKey = await chain.publicKey.export();

    const isSame = await crypt.subtle.verify(
      cert.signatureAlgorithm,
      publicKey,
      cert.signature, // BufferSource
      cert.tbs
    );

cert.pem:

-----BEGIN CERTIFICATE-----
MIICgzCCAimgAwIBAgIUU3QVClDwfllZDosbQuijZEqeqN0wCgYIKoZIzj0EAwIw
fTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQH
EwdSYWxlaWdoMQ0wCwYDVQQKEwRFREpYMRUwEwYDVQQLEwxFREpYIFJvb3QgQ0Ex
HTAbBgNVBAMTFEVESlggSW50ZXJtZWRpYXRlIENBMB4XDTIxMDEwNzA3MTYwMFoX
DTIyMDEwNzA3MTYwMFowaTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5DMRAwDgYD
VQQHEwdSZWxlaWdoMQ0wCwYDVQQKEwRFREpYMRQwEgYDVQQLEwtFbmdpbmVlcmlu
ZzEWMBQGA1UEAxMNYXBpLmVkangudGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABKZwl6VQ9VZHpelF+ZhBkKY3N7f7qXvvIwDBIDpV18+iYs1r01Qoo2TwJh3/
j/n87sqtm4nfuOBvjL8M/RUdDRWjgZowgZcwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBQjjloUw1goFcY26GEIxnJlckH+JTAfBgNVHSMEGDAWgBSQcYOQauj7nFQP4ZWK
p59JtGVZajAYBgNVHREEETAPgg1hcGkuZWRqeC50ZXN0MAoGCCqGSM49BAMCA0gA
MEUCIFYIeb08ZaNS6iFPrOdhCAZW10XF4C2oT9GwOMJ8tKp8AiEA25uqFuewgI0S
uLTWX6MGa1pd9z0p2Ks+8/+1pY9PUGc=
-----END CERTIFICATE-----

chain.pem

-----BEGIN CERTIFICATE-----
MIICVzCCAf2gAwIBAgIUH5dl9HNmcklznEOCTZxFIN65ugYwCgYIKoZIzj0EAwIw
dTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQH
EwdSYWxlaWdoMQ0wCwYDVQQKEwRFREpYMRUwEwYDVQQLEwxFREpYIFJvb3QgQ0Ex
FTATBgNVBAMTDEVESlggUm9vdCBDQTAeFw0yMTAxMDcwNzE2MDBaFw0yNDAxMDcw
NzE2MDBaMH0xCzAJBgNVBAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEQ
MA4GA1UEBxMHUmFsZWlnaDENMAsGA1UEChMERURKWDEVMBMGA1UECxMMRURKWCBS
b290IENBMR0wGwYDVQQDExRFREpYIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABCDz/PG5Nw0Q45Vafk3p3RABsaiGgv5FPFEl+jfVVxw6
o084dU+GibHJqL9JOk9t7zaXdc04oQSy0dkviV40HQGjYzBhMA4GA1UdDwEB/wQE
AwICBDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQcYOQauj7nFQP4ZWKp59J
tGVZajAfBgNVHSMEGDAWgBQU/uYHGGZLCS8dm9tCcaSBodnGnTAKBggqhkjOPQQD
AgNIADBFAiEAgiDUpS14LEn9p/2u93L8+PsfvUHVD2WItO6cPfTT01cCIDr/RT6K
5CmGnGgmFRrn/hZyGe3CvpzKfZqMX63UeoQQ
-----END CERTIFICATE-----

tsyringe requires a reflect polyfill.

Hello. I've been using this library on Bun for around 6 months without a single issue, however last night I did an upgrade to Bun's binary and my app stopped launching. Error :

1 | if (typeof Reflect === "undefined" || !Reflect.getMetadata) { 2 | throw new Error("tsyringe requires a reflect polyfill. Please add 'import \"reflect-metadata\"' to the top of your entry point."); ^ error: tsyringe requires a reflect polyfill. Please add 'import "reflect-metadata"' to the top of your entry point. at /srv/bun/iSAIM/node_modules/tsyringe/dist/esm5/index.js:2:10 at /srv/bun/iSAIM/node_modules/@peculiar/x509/build/x509.es.js:36:0 at /srv/bun/iSAIM/src/server.js:5:0

Following the error's message suggestion of adding import reflect-metadata to the top of my script doesn't work. Any idea what is wrong? Thanks.

Construction of certificate failed

Hi,

I'm trying to load ECDSA certificates to verify auth challenges.
I've seen some work, and others don't, and I don't know why

// This breaks, this one is using brainpool256r1 curve, but even some P-256 break
// This one was constructed with openssl
let certificate = `-----BEGIN CERTIFICATE-----
MIIC4jCCAomgAwIBAgIUIuaOxkNl6SL494+979nBRHn0MMswCgYIKoZIzj0EAwIw
WDELMAkGA1UEBhMCTFUxEzARBgNVBAgMCkx1eGVtYm91cmcxEzARBgNVBAcMCkx1
eGVtYm91cmcxEDAOBgNVBAoMB1NhdG9yaXMxDTALBgNVBAMMBEdyZWcwIBcNMjEw
OTIzMDkyNDM1WhgPNDQ4NTExMDQwOTI0MzVaMFgxCzAJBgNVBAYTAkxVMRMwEQYD
VQQIDApMdXhlbWJvdXJnMRMwEQYDVQQHDApMdXhlbWJvdXJnMRAwDgYDVQQKDAdT
YXRvcmlzMQ0wCwYDVQQDDARHcmVnMIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcq
hkjOPQEBAiEAqftX26Huqbw+ZgqQnYONcm479iPVJiAoIBNIHR9uU3cwRAQgfVoJ
dfwsMFfu9nUwQXr/5/uAVcEm3Fxs6UpLRPMwtdkEICbcXGzpSktE8zC12bvXfL+V
hBYpXPfhzmvM3Bj/jAe2BEEEi9Kuuct+V8ssS0gv/IG3r7neJ+HjvSPCOkRTvZrO
MmJUfvg1w9rE/Zf4RhoUYR3JwndFEy3tjlRcHVTHLwRplwIhAKn7V9uh7qm8PmYK
kJ2DjXGMOXqjtWGm95AeDoKXSFanAgEBA0IABJBZdHpArFYI3Q3uoUjgpdBAh6UV
ZBC1hQRixsybOBraTFKmCUi1PGL5Kz6f0qbrsXpFAfe1OnI4ITWw8C65iqujUzBR
MB0GA1UdDgQWBBQ/oJyZ2VZ048roWL5OinfKiKBbozAfBgNVHSMEGDAWgBQ/oJyZ
2VZ048roWL5OinfKiKBbozAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0cA
MEQCIFJNL8kKKHCQYDP1NII5OyBrD3Yqe1w4oInKWrKi02qtAiAIexDPePqnLSjK
g209BfzbYa3JpzI3TfnPbAeMQ2BjLg==
-----END CERTIFICATE-----`;

// This one can be loaded
certificate = `-----BEGIN CERTIFICATE-----
MIICgzCCAimgAwIBAgIUU3QVClDwfllZDosbQuijZEqeqN0wCgYIKoZIzj0EAwIw
fTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQH
EwdSYWxlaWdoMQ0wCwYDVQQKEwRFREpYMRUwEwYDVQQLEwxFREpYIFJvb3QgQ0Ex
HTAbBgNVBAMTFEVESlggSW50ZXJtZWRpYXRlIENBMB4XDTIxMDEwNzA3MTYwMFoX
DTIyMDEwNzA3MTYwMFowaTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5DMRAwDgYD
VQQHEwdSZWxlaWdoMQ0wCwYDVQQKEwRFREpYMRQwEgYDVQQLEwtFbmdpbmVlcmlu
ZzEWMBQGA1UEAxMNYXBpLmVkangudGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABKZwl6VQ9VZHpelF+ZhBkKY3N7f7qXvvIwDBIDpV18+iYs1r01Qoo2TwJh3/
j/n87sqtm4nfuOBvjL8M/RUdDRWjgZowgZcwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBQjjloUw1goFcY26GEIxnJlckH+JTAfBgNVHSMEGDAWgBSQcYOQauj7nFQP4ZWK
p59JtGVZajAYBgNVHREEETAPgg1hcGkuZWRqeC50ZXN0MAoGCCqGSM49BAMCA0gA
MEUCIFYIeb08ZaNS6iFPrOdhCAZW10XF4C2oT9GwOMJ8tKp8AiEA25uqFuewgI0S
uLTWX6MGa1pd9z0p2Ks+8/+1pY9PUGc=
-----END CERTIFICATE-----`

console.log(new x509.X509Certificate(certificate));

Can you guide me on what is hapenning?

Incorporate extended key usage OIDs

Since KeyUsagesExtension has all the flags already built in, why not add in the basic OIDs for extended key usage?

  const serverAuth = '1.3.6.1.5.5.7.3.1';
  const clientAuth = '1.3.6.1.5.5.7.3.2';
  const codeSigning = '1.3.6.1.5.5.7.3.3';
  const emailProtection = '1.3.6.1.5.5.7.3.4';
  const timeStamping = '1.3.6.1.5.5.7.3.8';
  const ocspSigning = '1.3.6.1.5.5.7.3.9';

      new x509.ExtendedKeyUsageExtension(
        [
          serverAuth,
          clientAuth,
          codeSigning,
          emailProtection,
          timeStamping,
          ocspSigning
        ]
      );

certificates and CSR cannot always be verified by openssl

Hello there,

I'm trying to use this library to issue certificates (using ECDSA P-256 algorithm). I though I got something working, but then the generated CSR and leaf certificates can not always be verified by openssl. I tried to find a common cause, but to me it seems the signature is randomly invalid (at least from openssl point of view).

I have put together a reproduction script (should only require npm i @peculiar/x509 @peculiar/webcrypto, typescript and openssl in the path) to be executable. It can be found in this gist: https://gist.github.com/fmonniot/e94ed40e3902415ccb9ca5a1d932297e.

Running this script multiple times yields different results: either the csr is valid, or the end cert is valid, or both, or neither :(

Is there a known format issue with openssl req -verify and x509 -verify commands?

And last but not least, Thank you for writing this library !

X509Certificates export format missing eContentType value

In the certs-only CMS Signed Data structure exported by the X509Certificates class, the eContentType field is left as the default of an empty OID. However, the CMS RFC 5652 section 5.2 states:

In the degenerate case where there are no signers, the EncapsulatedContentInfo value being "signed" is irrelevant. In this case, the content type within the EncapsulatedContentInfo value being "signed" MUST be id-data (as defined in Section 4), and the content field of the EncapsulatedContentInfo value MUST be omitted.

In particular, the OpenSSL pkcs7 command complains about the empty OID and considers the data malformed:

unable to load PKCS7 object
4402050560:error:0D0C40D8:asn1 encoding routines:c2i_ASN1_OBJECT:invalid object encoding:crypto/asn1/a_object.c:254:
4402050560:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:646:Field=type, Type=PKCS7
4402050560:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:646:Field=contents, Type=PKCS7_SIGNED
4402050560:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:646:
4402050560:error:0D08403A:asn1 encoding routines:asn1_template_ex_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:496:Field=d.sign, Type=PKCS7
4402050560:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:crypto/pem/pem_oth.c:33:

Setting the eContentType field to id_data allows OpenSSL to parse the structure and complies with the standard. I can open a PR with this change for your review.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.