1 //! Certificate utility methods for convenience (such as CSR generation).
3 use std
::collections
::HashMap
;
5 use openssl
::hash
::MessageDigest
;
7 use openssl
::pkey
::PKey
;
9 use openssl
::x509
::{self, X509Name, X509Req}
;
13 /// A certificate signing request.
15 /// DER encoded certificate request.
18 /// PEM formatted PKCS#8 private key.
19 pub private_key_pem
: Vec
<u8>,
23 /// Generate a CSR in DER format with a PEM formatted PKCS8 private key.
25 /// The `identifiers` should be a list of domains. The `attributes` should have standard names
26 /// recognized by openssl.
28 identifiers
: &[impl AsRef
<str>],
29 attributes
: &HashMap
<String
, &str>,
30 ) -> Result
<Self, Error
> {
31 if identifiers
.is_empty() {
32 return Err(Error
::Csr("cannot generate empty CSR".to_string()));
35 let private_key
= Rsa
::generate(4096)
36 .and_then(PKey
::from_rsa
)
37 .map_err(|err
| Error
::Ssl("failed to generate RSA key: {}", err
))?
;
39 let private_key_pem
= private_key
40 .private_key_to_pem_pkcs8()
41 .map_err(|err
| Error
::Ssl("failed to format private key as PEM pkcs8: {}", err
))?
;
43 let mut name
= X509Name
::builder()?
;
44 if !attributes
.contains_key("CN") {
45 name
.append_entry_by_nid(Nid
::COMMONNAME
, identifiers
[0].as_ref())?
;
47 for (key
, value
) in attributes
{
48 name
.append_entry_by_text(key
, value
)?
;
50 let name
= name
.build();
52 let mut csr
= X509Req
::builder()?
;
53 csr
.set_subject_name(&name
)?
;
54 csr
.set_pubkey(&private_key
)?
;
56 let context
= csr
.x509v3_context(None
);
57 let mut ext
= openssl
::stack
::Stack
::new()?
;
58 ext
.push(x509
::extension
::BasicConstraints
::new().build()?
)?
;
60 x509
::extension
::KeyUsage
::new()
66 x509
::extension
::ExtendedKeyUsage
::new()
71 let mut san
= x509
::extension
::SubjectAlternativeName
::new();
72 for dns
in identifiers
{
73 san
.dns(dns
.as_ref());
75 ext
.push({ san }
.build(&context
)?
)?
;
76 csr
.add_extensions(&ext
)?
;
78 csr
.sign(&private_key
, MessageDigest
::sha256())?
;
81 data
: csr
.build().to_der()?
,