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

Intermittent ERR_OSSL_ASN1_ILLEGAL_PADDING error decoding certificates #74

Open achingbrain opened 2 months ago

achingbrain commented 2 months ago

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
}