changed
CHANGELOG.md
|
@@ -1,5 +1,18 @@
|
1
1
|
# Changes
|
2
2
|
|
3
|
+ ## v0.7.0
|
4
|
+
|
5
|
+ ### Enhancements
|
6
|
+
|
7
|
+ * [X509.Certificate.Template] Add OCSP responder template
|
8
|
+ * [X509.Certificate.Extension] Add support for Authority Information Access
|
9
|
+ and OCSP Nocheck extensions
|
10
|
+
|
11
|
+ ### Fixes
|
12
|
+
|
13
|
+ * [X509.RDNSequence] Handle `teletexString` encoding (7-bit only, for now)
|
14
|
+ * [X509.PrivateKey] Documentation fixes
|
15
|
+
|
3
16
|
## v0.6.0
|
4
17
|
|
5
18
|
### Enhancements
|
changed
README.md
|
@@ -90,7 +90,7 @@ Add `x509` to your list of dependencies in `mix.exs`:
|
90
90
|
```elixir
|
91
91
|
def deps do
|
92
92
|
[
|
93
|
- {:x509, "~> 0.5.4"}
|
93
|
+ {:x509, "~> 0.7.0"}
|
94
94
|
]
|
95
95
|
end
|
96
96
|
```
|
changed
hex_metadata.config
|
@@ -25,4 +25,4 @@
|
25
25
|
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/voltone/x509">>}]}.
|
26
26
|
{<<"name">>,<<"x509">>}.
|
27
27
|
{<<"requirements">>,[]}.
|
28
|
- {<<"version">>,<<"0.6.0">>}.
|
28
|
+ {<<"version">>,<<"0.7.0">>}.
|
changed
lib/x509/asn1.ex
|
@@ -43,6 +43,7 @@ defmodule X509.ASN1 do
|
43
43
|
extension: :Extension,
|
44
44
|
basic_constraints: :BasicConstraints,
|
45
45
|
authority_key_identifier: :AuthorityKeyIdentifier,
|
46
|
+ access_description: :AccessDescription,
|
46
47
|
|
47
48
|
# CRLs
|
48
49
|
certificate_list: :CertificateList,
|
changed
lib/x509/certificate/extension.ex
|
@@ -17,6 +17,8 @@ defmodule X509.Certificate.Extension do
|
17
17
|
| :authority_key_identifier
|
18
18
|
| :subject_alt_name
|
19
19
|
| :crl_distribution_point
|
20
|
+ | :authority_information_access
|
21
|
+ | :ocsp_nocheck
|
20
22
|
|
21
23
|
@typedoc "Supported values in the key usage extension"
|
22
24
|
@type key_usage_value ::
|
|
@@ -37,6 +39,12 @@ defmodule X509.Certificate.Extension do
|
37
39
|
"""
|
38
40
|
@type san_value :: String.t() | {atom(), charlist()}
|
39
41
|
|
42
|
+ @type aia_access_method ::
|
43
|
+ :ocsp
|
44
|
+ | :ca_issuers
|
45
|
+ | :time_stamping
|
46
|
+ | :ca_repository
|
47
|
+
|
40
48
|
@doc """
|
41
49
|
The basic constraints extension identifies whether the subject of the
|
42
50
|
certificate is a CA and the maximum depth of valid certification
|
|
@@ -299,6 +307,90 @@ defmodule X509.Certificate.Extension do
|
299
307
|
}
|
300
308
|
end
|
301
309
|
|
310
|
+ @doc """
|
311
|
+ The authority information access extension indicates how to access
|
312
|
+ information and services for the issuer of the certificate in which the
|
313
|
+ extension appears.
|
314
|
+
|
315
|
+ Information and services may include on-line validation services and CA
|
316
|
+ policy data. This extension may be included in end entity or CA certificates.
|
317
|
+
|
318
|
+ This extension is marked as non-critical.
|
319
|
+
|
320
|
+ Example:
|
321
|
+
|
322
|
+ iex> X509.Certificate.Extension.authority_info_access(
|
323
|
+ ...> ocsp: "http://ocsp.example.net/"
|
324
|
+ ...> )
|
325
|
+ {:Extension, {1, 3, 6, 1, 5, 5, 7, 1, 1}, false,
|
326
|
+ [
|
327
|
+ {:AccessDescription, {1, 3, 6, 1, 5, 5, 7, 48, 1},
|
328
|
+ {:uniformResourceIdentifier, "http://ocsp.example.net/"}}
|
329
|
+ ]}
|
330
|
+ """
|
331
|
+ # @doc since: "0.7.0"
|
332
|
+ @spec authority_info_access([{aia_access_method(), String.t()}]) :: t()
|
333
|
+ def authority_info_access(access_methods) do
|
334
|
+ extension(
|
335
|
+ extnID: oid(:"id-pe-authorityInfoAccess"),
|
336
|
+ critical: false,
|
337
|
+ extnValue: Enum.map(access_methods, &aia_access_method/1)
|
338
|
+ )
|
339
|
+ end
|
340
|
+
|
341
|
+ defp aia_access_method({:ocsp, uri}) do
|
342
|
+ access_description(
|
343
|
+ accessMethod: oid(:"id-ad-ocsp"),
|
344
|
+ accessLocation: {:uniformResourceIdentifier, uri}
|
345
|
+ )
|
346
|
+ end
|
347
|
+
|
348
|
+ defp aia_access_method({:ca_issuers, uri}) do
|
349
|
+ access_description(
|
350
|
+ accessMethod: oid(:"id-ad-caIssuers"),
|
351
|
+ accessLocation: {:uniformResourceIdentifier, uri}
|
352
|
+ )
|
353
|
+ end
|
354
|
+
|
355
|
+ defp aia_access_method({:time_stamping, uri}) do
|
356
|
+ access_description(
|
357
|
+ accessMethod: oid(:"id-ad-timeStamping"),
|
358
|
+ accessLocation: {:uniformResourceIdentifier, uri}
|
359
|
+ )
|
360
|
+ end
|
361
|
+
|
362
|
+ defp aia_access_method({:ca_repository, uri}) do
|
363
|
+ access_description(
|
364
|
+ accessMethod: oid(:"id-ad-caRepository"),
|
365
|
+ accessLocation: {:uniformResourceIdentifier, uri}
|
366
|
+ )
|
367
|
+ end
|
368
|
+
|
369
|
+ @doc """
|
370
|
+ The OCSP Nocheck extension indicates that the OCSP client can trust this
|
371
|
+ OCSP responder certificate for the lifetime of the certificate, and no
|
372
|
+ revocation checks are needed.
|
373
|
+
|
374
|
+ This extension has no value, it acts as a flag simply by being present in a
|
375
|
+ certificate's extension list.
|
376
|
+
|
377
|
+ This extension is marked as non-critical.
|
378
|
+
|
379
|
+ Example:
|
380
|
+
|
381
|
+ iex> X509.Certificate.Extension.ocsp_nocheck()
|
382
|
+ {:Extension, {1, 3, 6, 1, 5, 5, 7, 48, 1, 5}, false, <<5, 0>>}
|
383
|
+ """
|
384
|
+ # @doc since: "0.7.0"
|
385
|
+ @spec ocsp_nocheck() :: t()
|
386
|
+ def ocsp_nocheck() do
|
387
|
+ extension(
|
388
|
+ extnID: {1, 3, 6, 1, 5, 5, 7, 48, 1, 5},
|
389
|
+ critical: false,
|
390
|
+ extnValue: <<5, 0>>
|
391
|
+ )
|
392
|
+ end
|
393
|
+
|
302
394
|
@doc """
|
303
395
|
Looks up the value of a specific extension in a list.
|
304
396
|
|
|
@@ -313,6 +405,8 @@ defmodule X509.Certificate.Extension do
|
313
405
|
def find(list, :authority_key_identifier), do: find(list, oid(:"id-ce-authorityKeyIdentifier"))
|
314
406
|
def find(list, :subject_alt_name), do: find(list, oid(:"id-ce-subjectAltName"))
|
315
407
|
def find(list, :crl_distribution_points), do: find(list, oid(:"id-ce-cRLDistributionPoints"))
|
408
|
+ def find(list, :authority_info_access), do: find(list, oid(:"id-pe-authorityInfoAccess"))
|
409
|
+ def find(list, :ocsp_nocheck), do: find(list, {1, 3, 6, 1, 5, 5, 7, 48, 1, 5})
|
316
410
|
|
317
411
|
def find(list, extension_oid) do
|
318
412
|
Enum.find(list, &match?(extension(extnID: ^extension_oid), &1))
|
changed
lib/x509/certificate/template.ex
|
@@ -55,6 +55,20 @@ defmodule X509.Certificate.Template do
|
55
55
|
|
56
56
|
The default validity is 1 year, plus a 30 day grace period.
|
57
57
|
|
58
|
+ * `:ocsp_responder` - designated responder to sign OCSP responses on behalf
|
59
|
+ of the issuing CA.
|
60
|
+
|
61
|
+ The Extended Key Usage extension is set to allow OCSP signing only. Must
|
62
|
+ be issued directly from the CA certificate for which OCSP responses will
|
63
|
+ be signed.
|
64
|
+
|
65
|
+ The OCSP Nocheck extension is included, to disable revocation checks for
|
66
|
+ the OCSP responder certificate. To exclude this extension override it
|
67
|
+ with a value of `false` (e.g. `extensions: [ocsp_nocheck: false]`)
|
68
|
+
|
69
|
+ The default validity is just 30 days, due to the fact that revocation
|
70
|
+ is not possible when OCSP Nocheck is set.
|
71
|
+
|
58
72
|
All of the above templates generate a random 8 byte (64 bit) serial number,
|
59
73
|
which can be overriden through the `:serial` option (see below).
|
60
74
|
|
|
@@ -165,6 +179,23 @@ defmodule X509.Certificate.Template do
|
165
179
|
|> new(opts)
|
166
180
|
end
|
167
181
|
|
182
|
+ def new(:ocsp_responder, opts) do
|
183
|
+ %__MODULE__{
|
184
|
+ # 30 days
|
185
|
+ validity: 30,
|
186
|
+ hash: :sha256,
|
187
|
+ extensions: [
|
188
|
+ basic_constraints: basic_constraints(false),
|
189
|
+ key_usage: key_usage([:digitalSignature]),
|
190
|
+ ext_key_usage: ext_key_usage([:OCSPSigning]),
|
191
|
+ subject_key_identifier: true,
|
192
|
+ authority_key_identifier: true,
|
193
|
+ ocsp_nocheck: ocsp_nocheck()
|
194
|
+ ]
|
195
|
+ }
|
196
|
+ |> new(opts)
|
197
|
+ end
|
198
|
+
|
168
199
|
def new(template, opts) do
|
169
200
|
override =
|
170
201
|
opts
|
changed
lib/x509/private_key.ex
|
@@ -37,7 +37,7 @@ defmodule X509.PrivateKey do
|
37
37
|
@default_e 65537
|
38
38
|
|
39
39
|
@doc """
|
40
|
- Generates a new private RSA private key. To derive the public key, use
|
40
|
+ Generates a new RSA private key. To derive the public key, use
|
41
41
|
`X509.PublicKey.derive/1`.
|
42
42
|
|
43
43
|
The key length in bits must be specified as an integer (minimum 256 bits).
|
|
@@ -52,10 +52,10 @@ defmodule X509.PrivateKey do
|
52
52
|
end
|
53
53
|
|
54
54
|
@doc """
|
55
|
- Generates a new private EC private key. To derive the public key, use
|
55
|
+ Generates a new EC private key. To derive the public key, use
|
56
56
|
`X509.PublicKey.derive/1`.
|
57
57
|
|
58
|
- The second parameter must specify a named curve. The curve can be specified
|
58
|
+ The first parameter must specify a named curve. The curve can be specified
|
59
59
|
as an atom or an OID tuple.
|
60
60
|
|
61
61
|
Note that this function uses Erlang/OTP's `:public_key` application, which
|
changed
lib/x509/rdn_sequence.ex
|
@@ -280,6 +280,8 @@ defmodule X509.RDNSequence do
|
280
280
|
defp attr_value_to_string({:utf8String, value}), do: value
|
281
281
|
defp attr_value_to_string({:printableString, value}), do: List.to_string(value)
|
282
282
|
defp attr_value_to_string({:ia5String, value}), do: List.to_string(value)
|
283
|
+ # FIXME: for 8-bit teletexString this requires mapping (see RFC1345)
|
284
|
+ defp attr_value_to_string({:teletexString, value}), do: List.to_string(value)
|
283
285
|
defp attr_value_to_string(value), do: List.to_string(value)
|
284
286
|
|
285
287
|
# Splits an attribute in the form of "type=value" into a {type, value} tuple
|
changed
mix.exs
|
@@ -1,7 +1,7 @@
|
1
1
|
defmodule X509.MixProject do
|
2
2
|
use Mix.Project
|
3
3
|
|
4
|
- @version "0.6.0"
|
4
|
+ @version "0.7.0"
|
5
5
|
|
6
6
|
def project do
|
7
7
|
[
|