Acquire certificates for all subdomains of a host and include them in SAN

This commit is contained in:
Konstantinos Kallas 2017-08-22 13:36:34 +03:00
parent 37a54cd498
commit 25ca6e5582
1 changed files with 42 additions and 10 deletions

View File

@ -156,8 +156,12 @@ format_get_certificate({error, Domain, Reason}) ->
{'error', bitstring(), _}.
get_certificate(CAUrl, DomainName, PrivateKey) ->
try
{ok, _Authz} = create_new_authorization(CAUrl, DomainName, PrivateKey),
create_new_certificate(CAUrl, DomainName, PrivateKey)
AllSubDomains = find_all_sub_domains(DomainName),
lists:foreach(
fun(Domain) ->
{ok, _Authz} = create_new_authorization(CAUrl, Domain, PrivateKey)
end, [DomainName|AllSubDomains]),
create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey)
catch
throw:Throw ->
Throw;
@ -235,13 +239,14 @@ create_new_authorization(CAUrl, DomainName, PrivateKey) ->
throw({error, DomainName, authorization})
end.
-spec create_new_certificate(url(), bitstring(), jose_jwk:key()) ->
-spec create_new_certificate(url(), {bitstring(), [bitstring()]}, jose_jwk:key()) ->
{ok, bitstring(), pem()}.
create_new_certificate(CAUrl, DomainName, PrivateKey) ->
create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
try
{ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl),
CSRSubject = [{commonName, bitstring_to_list(DomainName)}],
{CSR, CSRKey} = make_csr(CSRSubject),
SANs = [{dNSName, SAN} || SAN <- AllSubDomains],
{CSR, CSRKey} = make_csr(CSRSubject, SANs),
{NotBefore, NotAfter} = not_before_not_after(),
Req =
[{<<"csr">>, CSR},
@ -572,8 +577,9 @@ certificate_exists(Host) ->
%% For now we accept only generating a key of
%% specific type for signing the csr
-spec make_csr(proplist()) -> {binary(), jose_jwk:key()}.
make_csr(Attributes) ->
-spec make_csr(proplist(), [{dNSName, bitstring()}])
-> {binary(), jose_jwk:key()}.
make_csr(Attributes, SANs) ->
Key = generate_key(),
{_, KeyKey} = jose_jwk:to_key(Key),
KeyPub = to_public(Key),
@ -582,7 +588,8 @@ make_csr(Attributes) ->
{ok, RawBinPubKey} = raw_binary_public_key(KeyPub),
SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey),
{ok, Subject} = attributes_from_list(Attributes),
CRI = certificate_request_info(SubPKInfo, Subject),
ExtensionRequest = extension_request(SANs),
CRI = certificate_request_info(SubPKInfo, Subject, ExtensionRequest),
{ok, EncodedCRI} = der_encode(
'CertificationRequestInfo',
CRI),
@ -617,12 +624,27 @@ subject_pk_info(Algo, RawBinPubKey) ->
subjectPublicKey = RawBinPubKey
}.
certificate_request_info(SubPKInfo, Subject) ->
extension(SANs) ->
#'Extension'{
extnID = attribute_oid(subjectAltName),
critical = false,
extnValue = public_key:der_encode('SubjectAltName', SANs)}.
extension_request(SANs) ->
#'AttributePKCS-10'{
type = ?'pkcs-9-at-extensionRequest',
values = [{'asn1_OPENTYPE',
public_key:der_encode(
'ExtensionRequest',
[extension(SANs)])}]
}.
certificate_request_info(SubPKInfo, Subject, ExtensionRequest) ->
#'CertificationRequestInfo'{
version = 0,
subject = Subject,
subjectPKInfo = SubPKInfo,
attributes = []
attributes = [ExtensionRequest]
}.
signature_algo(_Key, _Hash) ->
@ -693,6 +715,7 @@ attribute_oid(countryName) -> ?'id-at-countryName';
attribute_oid(stateOrProvinceName) -> ?'id-at-stateOrProvinceName';
attribute_oid(localityName) -> ?'id-at-localityName';
attribute_oid(organizationName) -> ?'id-at-organizationName';
attribute_oid(subjectAltName) -> ?'id-ce-subjectAltName';
attribute_oid(_) -> error(bad_attributes).
@ -793,6 +816,15 @@ private_key_types() ->
'DSAPrivateKey',
'ECPrivateKey'].
-spec find_all_sub_domains(bitstring()) -> [bitstring()].
find_all_sub_domains(DomainName) ->
AllRoutes = ejabberd_router:get_all_routes(),
DomainLen = size(DomainName),
[Route || Route <- AllRoutes,
binary:longest_common_suffix([DomainName, Route])
=:= DomainLen].
-spec is_error(_) -> boolean().
is_error({error, _}) -> true;
is_error({error, _, _}) -> true;