From d78acc729bbe0960ddbeabb40530427175512a29 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 7 Nov 2016 20:42:43 +0000 Subject: [PATCH] Add an X509ReqBuilder --- openssl-sys/src/lib.rs | 4 ++ openssl/src/x509/mod.rs | 96 +++++++++++++++++++++++++++++++++------ openssl/src/x509/tests.rs | 30 +++++++++++- 3 files changed, 115 insertions(+), 15 deletions(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index ac339a4b..5852e7d0 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1685,6 +1685,10 @@ extern { pub fn X509V3_set_ctx(ctx: *mut X509V3_CTX, issuer: *mut X509, subject: *mut X509, req: *mut X509_REQ, crl: *mut X509_CRL, flags: c_int); pub fn X509V3_set_nconf(ctx: *mut X509V3_CTX, conf: *mut CONF); + pub fn X509_REQ_new() -> *mut X509_REQ; + pub fn X509_REQ_set_version(req: *mut X509_REQ, version: c_long) -> c_int; + pub fn X509_REQ_set_subject_name(req: *mut X509_REQ, name: *mut X509_NAME) -> c_int; + pub fn X509_REQ_set_pubkey(req: *mut X509_REQ, pkey: *mut EVP_PKEY) -> c_int; pub fn X509_REQ_add_extensions(req: *mut X509_REQ, exts: *mut stack_st_X509_EXTENSION) -> c_int; pub fn X509_REQ_sign(x: *mut X509_REQ, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> c_int; diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index 57d94ae1..97d9fa3f 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -597,6 +597,10 @@ impl<'a> X509v3Context<'a> { type_!(X509Extension, X509ExtensionRef, ffi::X509_EXTENSION, ffi::X509_EXTENSION_free); +impl Stackable for X509Extension { + type StackType = ffi::stack_st_X509_EXTENSION; +} + impl X509Extension { pub fn new(conf: Option<&ConfRef>, context: Option<&X509v3Context>, @@ -743,8 +747,86 @@ impl X509NameEntryRef { } } +pub struct X509ReqBuilder(X509Req); + +impl X509ReqBuilder { + pub fn new() -> Result { + unsafe { cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p))) } + } + + pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version.into())).map(|_| ()) } + } + + pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_set_subject_name(self.0.as_ptr(), subject_name.as_ptr())).map(|_| ()) + } + } + + pub fn set_pubkey(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + pub fn x509v3_context<'a>(&'a self, + conf: Option<&'a ConfRef>) + -> X509v3Context<'a> { + unsafe { + let mut ctx = mem::zeroed(); + + ffi::X509V3_set_ctx(&mut ctx, + ptr::null_mut(), + ptr::null_mut(), + self.0.as_ptr(), + ptr::null_mut(), + 0); + + // nodb case taken care of since we zeroed ctx above + if let Some(conf) = conf { + ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr()); + } + + X509v3Context(ctx, PhantomData) + } + } + + pub fn add_extensions(&mut self, + extensions: &StackRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_add_extensions(self.0.as_ptr(), extensions.as_ptr())).map(|_| ()) + } + } + + pub fn sign(&mut self, key: &PKeyRef, hash: MessageDigest) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_REQ_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) } + } + + pub fn build(self) -> X509Req { + self.0 + } +} + type_!(X509Req, X509ReqRef, ffi::X509_REQ, ffi::X509_REQ_free); +impl X509Req { + pub fn builder() -> Result { + X509ReqBuilder::new() + } + + /// Reads CSR from PEM + pub fn from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); + unsafe { + let handle = try!(cvt_p(ffi::PEM_read_bio_X509_REQ(mem_bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut()))); + Ok(X509Req::from_ptr(handle)) + } + } +} + impl X509ReqRef { /// Writes CSR as PEM pub fn to_pem(&self) -> Result, ErrorStack> { @@ -765,20 +847,6 @@ impl X509ReqRef { } } -impl X509Req { - /// Reads CSR from PEM - pub fn from_pem(buf: &[u8]) -> Result { - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let handle = try!(cvt_p(ffi::PEM_read_bio_X509_REQ(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(X509Req::from_ptr(handle)) - } - } -} - /// A collection of X.509 extensions. /// /// Upholds the invariant that a certificate MUST NOT include more than one diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 510bb437..514b8edc 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -5,7 +5,8 @@ use bn::{BigNum, MSB_MAYBE_ZERO}; use hash::MessageDigest; use pkey::PKey; use rsa::Rsa; -use x509::{X509, X509Generator, X509Name}; +use stack::Stack; +use x509::{X509, X509Generator, X509Name, X509Req}; use x509::extension::{Extension, BasicConstraints, KeyUsage, ExtendedKeyUsage, SubjectKeyIdentifier, AuthorityKeyIdentifier, SubjectAlternativeName}; use x509::extension::AltNameOption as SAN; @@ -187,6 +188,7 @@ fn x509_builder() { let name = name.build(); let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); builder.set_subject_name(&name).unwrap(); builder.set_issuer_name(&name).unwrap(); builder.set_not_before(&Asn1Time::days_from_now(0).unwrap()).unwrap(); @@ -232,3 +234,29 @@ fn x509_builder() { let cn = x509.subject_name().entries_by_nid(nid::COMMONNAME).next().unwrap(); assert_eq!("foobar.com".as_bytes(), cn.data().as_slice()); } + +#[test] +fn x509_req_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(nid::COMMONNAME, "foobar.com").unwrap(); + let name = name.build(); + + let mut builder = X509Req::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut extensions = Stack::new().unwrap(); + let key_usage = KeyUsage::new().digital_signature().key_encipherment().build().unwrap(); + extensions.push(key_usage).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None)) + .unwrap(); + extensions.push(subject_alternative_name).unwrap(); + builder.add_extensions(&extensions).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); +}