diff options
| author | Steven Fackler <[email protected]> | 2016-11-06 14:07:34 -0800 |
|---|---|---|
| committer | Steven Fackler <[email protected]> | 2016-11-06 14:07:34 -0800 |
| commit | b83edbad0d2a1074ea956e2049f4814894ea85c7 (patch) | |
| tree | d1f5a85ff30ffd9c274328d752ba8baa98148b5a /openssl/src | |
| parent | Support client CA advertisement (diff) | |
| download | rust-openssl-b83edbad0d2a1074ea956e2049f4814894ea85c7.tar.xz rust-openssl-b83edbad0d2a1074ea956e2049f4814894ea85c7.zip | |
Start on an X509Builder
Diffstat (limited to 'openssl/src')
| -rw-r--r-- | openssl/src/asn1.rs | 2 | ||||
| -rw-r--r-- | openssl/src/bn.rs | 9 | ||||
| -rw-r--r-- | openssl/src/x509/mod.rs | 105 | ||||
| -rw-r--r-- | openssl/src/x509/tests.rs | 33 |
4 files changed, 147 insertions, 2 deletions
diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index c0a23591..998f9878 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -63,6 +63,8 @@ impl Asn1StringRef { } } +type_!(Asn1Integer, Asn1IntegerRef, ffi::ASN1_INTEGER, ffi::ASN1_INTEGER_free); + #[cfg(any(ossl101, ossl102))] use ffi::ASN1_STRING_data; diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs index d52be884..ec0b1aba 100644 --- a/openssl/src/bn.rs +++ b/openssl/src/bn.rs @@ -6,6 +6,7 @@ use std::{fmt, ptr}; use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Deref}; use {cvt, cvt_p, cvt_n}; +use asn1::Asn1Integer; use crypto::CryptoString; use error::ErrorStack; use types::{OpenSslType, OpenSslTypeRef}; @@ -492,6 +493,14 @@ impl BigNumRef { Ok(CryptoString::from_null_terminated(buf)) } } + + /// Returns an `Asn1Integer` containing the value of `self`. + pub fn to_asn1_integer(&self) -> Result<Asn1Integer, ErrorStack> { + unsafe { + cvt_p(ffi::BN_to_ASN1_INTEGER(self.as_ptr(), ptr::null_mut())) + .map(|p| Asn1Integer::from_ptr(p)) + } + } } type_!(BigNum, BigNumRef, ffi::BIGNUM, ffi::BN_free); diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index e98e6006..f52e8844 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -12,7 +12,7 @@ use std::slice; use std::str; use {cvt, cvt_p}; -use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef}; +use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1IntegerRef}; use bio::{MemBio, MemBioSlice}; use hash::MessageDigest; use pkey::{PKey, PKeyRef}; @@ -353,6 +353,58 @@ impl X509Generator { } } +pub struct X509Builder(X509); + +impl X509Builder { + pub fn new() -> Result<X509Builder, ErrorStack> { + unsafe { + cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p))) + } + } + + pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(X509_set_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) } + } + + pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(X509_set_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) } + } + + pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version.into())).map(|_| ()) } + } + + pub fn set_serial_number(&mut self, + serial_number: &Asn1IntegerRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_serialNumber(self.0.as_ptr(), serial_number.as_ptr())).map(|_| ()) + } + } + + pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_set_issuer_name(self.0.as_ptr(), issuer_name.as_ptr())).map(|_| ()) } + } + + pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_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_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + pub fn sign(&mut self, key: &PKeyRef, hash: MessageDigest) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) } + } + + pub fn build(self) -> X509 { + self.0 + } +} + type_!(X509, X509Ref, ffi::X509, ffi::X509_free); impl X509Ref { @@ -446,6 +498,11 @@ impl ToOwned for X509Ref { } impl X509 { + /// Returns a new builder. + pub fn builder() -> Result<X509Builder, ErrorStack> { + X509Builder::new() + } + /// Reads a certificate from DER. pub fn from_der(buf: &[u8]) -> Result<X509, ErrorStack> { unsafe { @@ -497,9 +554,55 @@ impl Stackable for X509 { type StackType = ffi::stack_st_X509; } +pub struct X509NameBuilder(X509Name); + +impl X509NameBuilder { + pub fn new() -> Result<X509NameBuilder, ErrorStack> { + unsafe { cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p))) } + } + + pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> { + unsafe { + let field = CString::new(field).unwrap(); + assert!(value.len() <= c_int::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_txt(self.0.as_ptr(), + field.as_ptr() as *mut _, + ffi::MBSTRING_UTF8, + value.as_ptr(), + value.len() as c_int, + -1, + 0)) + .map(|_| ()) + } + } + + pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> { + unsafe { + assert!(value.len() <= c_int::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_NID(self.0.as_ptr(), + field.as_raw(), + ffi::MBSTRING_UTF8, + value.as_ptr() as *mut _, + value.len() as c_int, + -1, + 0)) + .map(|_| ()) + } + } + + pub fn build(self) -> X509Name { + self.0 + } +} + type_!(X509Name, X509NameRef, ffi::X509_NAME, ffi::X509_NAME_free); impl X509Name { + /// Returns a new builder. + pub fn builder() -> Result<X509NameBuilder, ErrorStack> { + X509NameBuilder::new() + } + /// Loads subject names from a file containing PEM-formatted certificates. /// /// This is commonly used in conjunction with `SslContextBuilder::set_client_ca_list`. diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 2527d538..8dad8759 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -1,9 +1,11 @@ use serialize::hex::FromHex; +use asn1::Asn1Time; +use bn::{BigNum, MSB_MAYBE_ZERO}; use hash::MessageDigest; use pkey::PKey; use rsa::Rsa; -use x509::{X509, X509Generator}; +use x509::{X509, X509Generator, X509Name}; use x509::extension::Extension::{KeyUsage, ExtKeyUsage, SubjectAltName, OtherNid, OtherStr}; use x509::extension::AltNameOption as SAN; use x509::extension::KeyUsageOption::{DigitalSignature, KeyEncipherment}; @@ -174,3 +176,32 @@ fn test_subject_alt_name_iter() { Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..])); assert!(subject_alt_names_iter.next().is_none()); } + +#[test] +fn test_x509_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 = X509::builder().unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder.set_not_before(&Asn1Time::days_from_now(0).unwrap()).unwrap(); + builder.set_not_after(&Asn1Time::days_from_now(365).unwrap()).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut serial = BigNum::new().unwrap();; + serial.rand(128, MSB_MAYBE_ZERO, false).unwrap(); + builder.set_serial_number(&serial.to_asn1_integer().unwrap()).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let x509 = builder.build(); + + assert!(pkey.public_eq(&x509.public_key().unwrap())); + + let cn = x509.subject_name().entries_by_nid(nid::COMMONNAME).next().unwrap(); + assert_eq!("foobar.com".as_bytes(), cn.data().as_slice()); +} |