Skip to content

Commit

Permalink
Support 'engine' private keys (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
voltone authored Jun 7, 2021
1 parent 4a29228 commit 33ddd87
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 9 deletions.
39 changes: 31 additions & 8 deletions lib/x509/csr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ defmodule X509.CSR do
Returns a `:CertificationRequest` record for the given key pair and subject.
Supports RSA and EC private keys. The public key is extracted from the
private key and encoded, together with the subject, in the CSR. The CSR is
then signed with the private key, using a configurable hash algorithm.
private key (unless overridden; see Options below) and encoded, together with
the subject, in the CSR. The CSR is then signed with the private key, using a
configurable hash algorithm.
The private key may be specified as an 'engine reference'. Please refer to
documentation for Erlang/OTP's `:crypto` application for further information
about engines.
The default hash algorithm is `:sha256`. An alternative algorithm can be
specified using the `:hash` option. Possible values include `:sha224`,
Expand All @@ -38,6 +43,10 @@ defmodule X509.CSR do
`:sha256`)
* `:extension_request` - a list of certificate extensions to be included as
an `extensionRequest` attribute (see `X509.Certificate.Extension`)
* `:public_key` - the public key to include in the CSR; by default the public
key is derived from the private key, but if that does not work (for certain
private keys stored in an 'engine') it can be useful to override the value
using this option (default: from private key)
## Example:
Expand All @@ -52,11 +61,20 @@ defmodule X509.CSR do
true
"""
@spec new(X509.PrivateKey.t(), String.t() | X509.RDNSequence.t(), Keyword.t()) :: t()
@spec new(
X509.PrivateKey.t() | :crypto.engine_key_ref(),
String.t() | X509.RDNSequence.t(),
Keyword.t()
) :: t()
def new(private_key, subject, opts \\ []) do
hash = Keyword.get(opts, :hash, :sha256)
extensions = Keyword.get(opts, :extension_request, [])

public_key =
Keyword.get_lazy(opts, :public_key, fn ->
X509.PublicKey.derive(private_key)
end)

algorithm =
X509.SignatureAlgorithm.new(hash, private_key, :CertificationRequest_signatureAlgorithm)

Expand Down Expand Up @@ -99,15 +117,20 @@ defmodule X509.CSR do
certification_request_info(
version: @version,
subject: subject_rdn_sequence,
subjectPKInfo:
private_key
|> X509.PublicKey.derive()
|> X509.PublicKey.wrap(:CertificationRequestInfo_subjectPKInfo),
subjectPKInfo: X509.PublicKey.wrap(public_key, :CertificationRequestInfo_subjectPKInfo),
attributes: attributes
)

info_der = :public_key.der_encode(:CertificationRequestInfo, info)
signature = :public_key.sign(info_der, hash, private_key)

signature =
case private_key do
%{algorithm: algorithm, engine: _} ->
:crypto.sign(algorithm, hash, info_der, private_key)

_ ->
:public_key.sign(info_der, hash, private_key)
end

certification_request(
certificationRequestInfo: info,
Expand Down
11 changes: 10 additions & 1 deletion lib/x509/public_key.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@ defmodule X509.PublicKey do

@doc """
Derives the public key from the given RSA or EC private key.
The private key may be specified as an 'engine reference'. Please refer to
documentation for Erlang/OTP's `:crypto` application for further information
about engines. However, please note that `:crypto` may not support this API
for all key types.
"""
@spec derive(X509.PrivateKey.t()) :: t()
@spec derive(X509.PrivateKey.t() | :crypto.engine_key_ref()) :: t()
def derive(%{algorithm: algorithm, engine: _} = private_key) do
:crypto.privkey_to_pubkey(algorithm, private_key)
end

def derive(rsa_private_key(modulus: m, publicExponent: e)) do
rsa_public_key(modulus: m, publicExponent: e)
end
Expand Down
4 changes: 4 additions & 0 deletions lib/x509/signature_algorithm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ defmodule X509.SignatureAlgorithm do

def new(hash, signature, type \\ :SignatureAlgorithm)

def new(hash, %{algorithm: algorithm, engine: _}, type) do
new(hash, algorithm, type)
end

def new(hash, rsa_private_key(), type) do
new(hash, :rsa, type)
end
Expand Down

0 comments on commit 33ddd87

Please sign in to comment.