Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for additional extensions #4

Open
voltone opened this issue Jul 4, 2018 · 11 comments
Open

Add support for additional extensions #4

voltone opened this issue Jul 4, 2018 · 11 comments

Comments

@voltone
Copy link
Owner

voltone commented Jul 4, 2018

Such as cRLDistributionPoints, authorityInfoAccess (AIA) and certificatePolicies...

@GalaxyGorilla
Copy link

@voltone Have you ever figured out how to define custom extensions with Erlang's certificate support?

@voltone
Copy link
Owner Author

voltone commented Sep 12, 2021

Some extensions are already decoded by Erlang's :public_key, such as those mentioned at the top of this ticket, I just haven't added convenience functions to X509.Certificate.Extension for them yet. You can still look them up and work with the Erlang record directly, e.g.:

iex(3)> aia = {1, 3, 6, 1, 5, 5, 7, 1, 1}
iex(4)> pem |> X509.Certificate.from_pem!() |> X509.Certificate.extension(aia)
{:Extension, {1, 3, 6, 1, 5, 5, 7, 1, 1}, false,
 [
   {:AccessDescription, {1, 3, 6, 1, 5, 5, 7, 48, 1},
    {:uniformResourceIdentifier, 'http://ocsp.starfieldtech.com/'}},
   {:AccessDescription, {1, 3, 6, 1, 5, 5, 7, 48, 2},
    {:uniformResourceIdentifier,
     'http://certificates.starfieldtech.com/repository/sfig2.crt'}}
 ]}

Same thing for encoding certificates with additional extensions.

Any unknown extensions are represented as DER binaries, which you can encode/decode manually with a suitable ASN.1 encoder.

Is there any particular extension you are interested in @GalaxyGorilla?

@GalaxyGorilla
Copy link

GalaxyGorilla commented Sep 12, 2021

@voltone thanks for the answer! However, I meant custom extensions, e.g. not standard extensions. I want to create my own extensions for client certificates. I'll test around throughout the week and check what's possible.

Unfortunately I'm developing in Erlang, so I can't use your lib at all :(.

@voltone
Copy link
Owner Author

voltone commented Sep 12, 2021

@voltone thanks for the answer! However, I meant custom extensions, e.g. not standard extensions. I want to create my own extensions for client certificates. I'll test around throughout the week and check what's possible.

You basically just need an OID and a blob of DER-encoded data. You can mint your own OIDs within the enterprise namespace if you have an OUI. Producing a DER version of some data structure may require defining some ASN.1 syntax and generating the encoder and decoder functions, along with a header file for the record. Or you can cheat by using the undocumented asn1rt_nif module, e.g. to wrap an arbitrary binary:

1> Hello = <<"Hello, world!">>.                      
<<"Hello, world!">>
2> asn1rt_nif:encode_ber_tlv({16, [{131072, Hello}]}).
<<48,15,128,13,72,101,108,108,111,44,32,119,111,114,108,
  100,33>>

Unfortunately I'm developing in Erlang, so I can't use your lib at all :(.

I actually have a branch somewhere that rewrites much of the code to Erlang, with the Elixir code becoming a thin wrapper and adapter to Elixir data types (e.g. DateTime). When pulled in from a Mix project it builds everything, but from Rebar3 it builds just the Erlang core. It's not quite done yet, I need some encouragement to allocate time to finish it...

@GalaxyGorilla
Copy link

You basically just need an OID and a blob of DER-encoded data. You can mint your own OIDs within the enterprise namespace if you have an OUI. Producing a DER version of some data structure may require defining some ASN.1 syntax and generating the encoder and decoder functions, along with a header file for the record. Or you can cheat by using the undocumented asn1rt_nif module, e.g. to wrap an arbitrary binary:

Thanks! Just being able to use a {OID, value} was exactly what I was hoping for, I did not know about asn1rt_nif :). If that really works out also using the public_key API then this would be sufficient for me. I only use it within my own SaaS system so there's no necessity for an official OUI.

I actually have a branch somewhere that rewrites much of the code to Erlang, with the Elixir code becoming a thin wrapper and adapter to Elixir data types (e.g. DateTime). When pulled in from a Mix project it builds everything, but from Rebar3 it builds just the Erlang core. It's not quite done yet, I need some encouragement to allocate time to finish it...

Sounds super cool and I think this would help the Erlang community a lot! I actually built my own little library for GRiSP as part of a larger PKI component, see https://github.com/GalaxyGorilla/grisp_cryptoauth. But that little library is not extensive for certificates at all, just tailored to our needs for GRiSP client certificates.

@voltone
Copy link
Owner Author

voltone commented Sep 13, 2021

If that really works out also using the public_key API then this would be sufficient for me.

It should. Let's say you have a macro that defines the OID value as a tuple and you want to support a single binary value, you can just wrap that in an Extension record and use that alongside other extension in a Certificate or OTPCertificate record:

my_extension(Value) ->
  %% ASN.1 universal tag value 4: Octet String
  DER = asn1rt_nif:encode_ber_tlv({16, [{4, Value}]}),
  #'Extension'{extnID = ?my_extension_oid, extnValue = DER}.

Encoding non-binary values, such as integers, is a bit harder as encode_ber_tlv does not take care of the type-specific conversion.

@GalaxyGorilla
Copy link

my_extension(Value) ->
  %% ASN.1 universal tag value 4: Octet String
  DER = asn1rt_nif:encode_ber_tlv({16, [{4, Value}]}),
  #'Extension'{extnID = ?my_extension_oid, extnValue = DER}.

What does {16, [{4, Value}]} actually represent? Which is the ASN.1 tag number?

Encoding non-binary values, such as integers, is a bit harder as encode_ber_tlv does not take care of the type-specific conversion.

That's exactly what I need for e.g. device serial numbers. I found the OTP-PUB-KEY module helpful here, but it also doesn't provide support for the basic types (like Integer). But you can just use types which are directly derived from those like this:

3> 'OTP-PUB-KEY':encode('CertificateSerialNumber', 128).
{ok,<<2,2,0,128>>}

@voltone
Copy link
Owner Author

voltone commented Sep 13, 2021

What does {16, [{4, Value}]} actually represent? Which is the ASN.1 tag number?

This is basically a Sequence (tag value 16) with a single element of type Octet String (tag value 4). I think the top-level type should always be a sequence, even if you only want to encode a single value.

@GalaxyGorilla
Copy link

Hmmm are you sure about the top level type being always a sequence? What I can find just always speaks about 'DER encoded'.

@voltone
Copy link
Owner Author

voltone commented Sep 13, 2021

Hmmm are you sure about the top level type being always a sequence? What I can find just always speaks about 'DER encoded'.

I thought it was a convention among standard extensions at least, but I now noticed that SubjectKeyIdentifier is defined as a plain octet string, without a sequence. So feel free to drop the sequence :)

@GalaxyGorilla
Copy link

Haha, I also looked on the SubjectKeyIdentifier, that got me suspicious ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants