voltone / x509

Elixir package for working with X.509 certificates, Certificate Signing Requests (CSRs), Certificate Revocation Lists (CRLs) and RSA/ECC key pairs
BSD 3-Clause "New" or "Revised" License
120 stars 28 forks source link

Add _ character to list of printable string #45

Closed ConnorRigby closed 2 years ago

ConnorRigby commented 2 years ago

I'm working with a certificate that has the following data in it:

X509.RDNSequence.new("/serialNumber=eui48_6827194B53C2", :otp)

This raises an error:

** (EXIT from #PID<0.111.0>) shell process exited with reason: an exception was raised:
    ** (ArgumentError) unsupported character(s) in `PrintableString` attribute
        (x509 0.8.3) lib/x509/rdn_sequence.ex:431: X509.RDNSequence.printableString/2
        (x509 0.8.3) lib/x509/rdn_sequence.ex:357: X509.RDNSequence.new_attr/1
        (x509 0.8.3) lib/x509/rdn_sequence.ex:137: anonymous fn/1 in X509.RDNSequence.new/2
        (elixir 1.12.2) lib/enum.ex:1582: Enum."-map/2-lists^map/1-0-"/2
        (x509 0.8.3) lib/x509/rdn_sequence.ex:137: X509.RDNSequence.new/2

Simply adding the _ character fixes it.

voltone commented 2 years ago

But underscore is not allowed in the ASN.1 definition of PrintableString. See https://en.wikipedia.org/wiki/PrintableString or https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ (under ‘Strings’)…

ConnorRigby commented 2 years ago

Hmm i was afraid of that. i suppose the certificate i have is wrong then? 🤔 the openssl commandline utility doesn't seem to care about the character in any way.

voltone commented 2 years ago

Hmm i was afraid of that. i suppose the certificate i have is wrong then? 🤔

It is technically in violation of the specifications. Such violations are quite common I'm afraid. The approach I tried to take in X.509 is to be strict in what we produce, to avoid surprises when sending certificates to other entities that might choke on invalid values.

the openssl commandline utility doesn't seem to care about the character in any way.

It might display the value, but does it permit such values as input, e.g. when generating a certificate or CSR?

ConnorRigby commented 2 years ago

It might display the value, but does it permit such values as input, e.g. when generating a certificate or CSR?

I don't have access to the private key of this cert directly. Maybe a little more information about my usecase will help:

i'm using an ATECCA608 hardware security module. It doesn't store der directly, you have to reassemble it by extracting and decompressing data. The certificate stored in the chip cannot be changed. We use the Template struct for to reconstruct the certificate bits with this library: https://github.com/nerves-hub/atecc508a

    alt_dirname = X509.RDNSequence.new(
      "/serialNumber=eui48_6827194B53C2", :otp
    )

    template = %ATECC508A.Certificate.Template{
      signer_id: signer_id,
      template_id: template_id,
      chain_id: 0,
      sn_source: :device_sn,
      device_sn: serial_number,
      extensions: [
        Extension.subject_alt_name(directoryName: alt_dirname),
        Extension.basic_constraints(false),
        Extension.key_usage([:digitalSignature, :keyAgreement]),
        Extension.subject_key_identifier(ski),
        Extension.authority_key_identifier(aki)
      ]
    }
    signer_id_hex_str = Integer.to_string(signer_id, 16)

    issuer_rdn =
      X509.RDNSequence.new(
        "/O=Microchip Technology Inc/CN=Crypto Authentication Signer #{signer_id_hex_str}",
        :otp
      )

    subject_rdn =
      X509.RDNSequence.new(
        "/O=Microchip Technology Inc/CN=sn" <> Base.encode16(serial_number),
        :otp
      )

    compressed =
      %ATECC508A.Certificate.Compressed{
        data: device_data,
        device_sn: device_sn,
        public_key: public_key_raw,
        template: template,
        issuer_rdn: issuer_rdn,
        subject_rdn: subject_rdn
      }
    {:ok, X509.Certificate.to_der(ATECC508A.Certificate.decompress(compressed))}
ConnorRigby commented 2 years ago

For what it's worth, i've worked around the issue for now. Here's what i did to anyone reading in the future:

https://github.com/nerves-hub/atecc508a/commit/4cdfabab6e391cb811961d77851e3a71f1029731#diff-0f3d742236908f253aa6c989e313c7d2fe2b0ed42e2181ff9af7bfcc9ff15b29R59