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
::{X509Extension, 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(X509Extension
::new_nid(
61 Nid
::BASIC_CONSTRAINTS
,
64 ext
.push(X509Extension
::new_nid(
68 "digitalSignature,keyEncipherment",
70 ext
.push(X509Extension
::new_nid(
74 "serverAuth,clientAuth",
76 ext
.push(X509Extension
::new_nid(
79 Nid
::SUBJECT_ALT_NAME
,
82 .try_fold(String
::new(), |mut acc
, dns
| {
87 write
!(acc
, "DNS:{}", dns
.as_ref())?
;
88 Ok
::<_
, std
::fmt
::Error
>(acc
)
90 .map_err(|err
| Error
::Csr(err
.to_string()))?
,
92 csr
.add_extensions(&ext
)?
;
94 csr
.sign(&private_key
, MessageDigest
::sha256())?
;
97 data
: csr
.build().to_der()?
,