Start of extension support
This commit is contained in:
parent
1939e6fd78
commit
5f18ffa4b3
|
|
@ -1683,6 +1683,7 @@ extern {
|
|||
pub fn X509_STORE_CTX_get_error_depth(ctx: *mut X509_STORE_CTX) -> c_int;
|
||||
|
||||
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_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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::fmt;
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use error::ErrorStack;
|
||||
use nid::{self, Nid};
|
||||
use x509::X509Extension;
|
||||
|
||||
/// Type-only version of the `Extension` enum.
|
||||
///
|
||||
|
|
@ -219,3 +221,159 @@ impl fmt::Display for AltNameOption {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BasicConstraints {
|
||||
critical: bool,
|
||||
ca: bool,
|
||||
pathlen: Option<u32>,
|
||||
}
|
||||
|
||||
impl BasicConstraints {
|
||||
pub fn new() -> BasicConstraints {
|
||||
BasicConstraints {
|
||||
critical: false,
|
||||
ca: false,
|
||||
pathlen: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn critical(&mut self, critical: bool) -> &mut BasicConstraints {
|
||||
self.critical = critical;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ca(&mut self, ca: bool) -> &mut BasicConstraints {
|
||||
self.ca = ca;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pathlen(&mut self, pathlen: u32) -> &mut BasicConstraints {
|
||||
self.pathlen = Some(pathlen);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result<X509Extension, ErrorStack> {
|
||||
let mut value = String::new();
|
||||
if self.critical {
|
||||
value.push_str("critical,");
|
||||
}
|
||||
value.push_str("CA:");
|
||||
if self.ca {
|
||||
value.push_str("TRUE");
|
||||
} else {
|
||||
value.push_str("FALSE");
|
||||
}
|
||||
if let Some(pathlen) = self.pathlen {
|
||||
write!(value, ",pathlen:{}", pathlen).unwrap();
|
||||
}
|
||||
X509Extension::new_nid(None, None, nid::BASIC_CONSTRAINTS, &value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyUsage {
|
||||
critical: bool,
|
||||
digital_signature: bool,
|
||||
non_repudiation: bool,
|
||||
key_encipherment: bool,
|
||||
data_encipherment: bool,
|
||||
key_agreement: bool,
|
||||
key_cert_sign: bool,
|
||||
crl_sign: bool,
|
||||
encipher_only: bool,
|
||||
decipher_only: bool,
|
||||
}
|
||||
|
||||
impl KeyUsage {
|
||||
pub fn new() -> KeyUsage {
|
||||
KeyUsage {
|
||||
critical: false,
|
||||
digital_signature: false,
|
||||
non_repudiation: false,
|
||||
key_encipherment: false,
|
||||
data_encipherment: false,
|
||||
key_agreement: false,
|
||||
key_cert_sign: false,
|
||||
crl_sign: false,
|
||||
encipher_only: false,
|
||||
decipher_only: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn critical(&mut self, critical: bool) -> &mut KeyUsage {
|
||||
self.critical = critical;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn digital_signature(&mut self, digital_signature: bool) -> &mut KeyUsage {
|
||||
self.digital_signature = digital_signature;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn non_repudiation(&mut self, non_repudiation: bool) -> &mut KeyUsage {
|
||||
self.non_repudiation = non_repudiation;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn key_encipherment(&mut self, key_encipherment: bool) -> &mut KeyUsage {
|
||||
self.key_encipherment = key_encipherment;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn data_encipherment(&mut self, data_encipherment: bool) -> &mut KeyUsage {
|
||||
self.data_encipherment = data_encipherment;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn key_agreement(&mut self, key_agreement: bool) -> &mut KeyUsage {
|
||||
self.key_agreement = key_agreement;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn key_cert_sign(&mut self, key_cert_sign: bool) -> &mut KeyUsage {
|
||||
self.key_cert_sign = key_cert_sign;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn crl_sign(&mut self, crl_sign: bool) -> &mut KeyUsage {
|
||||
self.crl_sign = crl_sign;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn encipher_only(&mut self, encipher_only: bool) -> &mut KeyUsage {
|
||||
self.encipher_only = encipher_only;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn decipher_only(&mut self, decipher_only: bool) -> &mut KeyUsage {
|
||||
self.decipher_only = decipher_only;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result<X509Extension, ErrorStack> {
|
||||
let mut value = String::new();
|
||||
let mut first = true;
|
||||
append(&mut value, &mut first, self.critical, "critical");
|
||||
append(&mut value, &mut first, self.digital_signature, "digitalSignature");
|
||||
append(&mut value, &mut first, self.non_repudiation, "nonRepudiation");
|
||||
append(&mut value, &mut first, self.key_encipherment, "keyEncipherment");
|
||||
append(&mut value, &mut first, self.data_encipherment, "dataEncipherment");
|
||||
append(&mut value, &mut first, self.key_agreement, "keyAgreement");
|
||||
append(&mut value, &mut first, self.key_cert_sign, "keyCertSign");
|
||||
append(&mut value, &mut first, self.crl_sign, "cRLSign");
|
||||
append(&mut value, &mut first, self.encipher_only, "encipherOnly");
|
||||
append(&mut value, &mut first, self.decipher_only, "decipherOnly");
|
||||
X509Extension::new_nid(None, None, nid::KEY_USAGE, &value)
|
||||
}
|
||||
}
|
||||
|
||||
fn append(value: &mut String, first: &mut bool, should: bool, element: &str) {
|
||||
if !should {
|
||||
return;
|
||||
}
|
||||
|
||||
if !*first {
|
||||
value.push(',');
|
||||
}
|
||||
*first = false;
|
||||
value.push_str(element);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::collections::HashMap;
|
|||
use std::error::Error;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
|
|
@ -14,6 +15,7 @@ use std::str;
|
|||
use {cvt, cvt_p};
|
||||
use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1IntegerRef};
|
||||
use bio::{MemBio, MemBioSlice};
|
||||
use conf::ConfRef;
|
||||
use hash::MessageDigest;
|
||||
use pkey::{PKey, PKeyRef};
|
||||
use rand::rand_bytes;
|
||||
|
|
@ -396,6 +398,37 @@ impl X509Builder {
|
|||
unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
pub fn x509v3_context<'a>(&'a self,
|
||||
issuer: Option<&'a X509Ref>,
|
||||
conf: Option<&'a ConfRef>)
|
||||
-> X509v3Context<'a> {
|
||||
unsafe {
|
||||
let mut ctx = mem::zeroed();
|
||||
|
||||
let issuer = match issuer {
|
||||
Some(issuer) => issuer.as_ptr(),
|
||||
None => self.0.as_ptr(),
|
||||
};
|
||||
let subject = self.0.as_ptr();
|
||||
ffi::X509V3_set_ctx(&mut ctx, issuer, subject, ptr::null_mut(), 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 append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
try!(cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1)));
|
||||
mem::forget(extension);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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(|_| ()) }
|
||||
}
|
||||
|
|
@ -554,6 +587,51 @@ impl Stackable for X509 {
|
|||
type StackType = ffi::stack_st_X509;
|
||||
}
|
||||
|
||||
pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
|
||||
|
||||
impl<'a> X509v3Context<'a> {
|
||||
pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
|
||||
&self.0 as *const _ as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
type_!(X509Extension, X509ExtensionRef, ffi::X509_EXTENSION, ffi::X509_EXTENSION_free);
|
||||
|
||||
impl X509Extension {
|
||||
pub fn new(conf: Option<&ConfRef>,
|
||||
context: Option<&X509v3Context>,
|
||||
name: &str,
|
||||
value: &str)
|
||||
-> Result<X509Extension, ErrorStack> {
|
||||
let name = CString::new(name).unwrap();
|
||||
let value = CString::new(value).unwrap();
|
||||
unsafe {
|
||||
let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
|
||||
let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr);
|
||||
let name = name.as_ptr() as *mut _;
|
||||
let value = value.as_ptr() as *mut _;
|
||||
|
||||
cvt_p(ffi::X509V3_EXT_nconf(conf, context, name, value)).map(X509Extension)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_nid(conf: Option<&ConfRef>,
|
||||
context: Option<&X509v3Context>,
|
||||
name: Nid,
|
||||
value: &str)
|
||||
-> Result<X509Extension, ErrorStack> {
|
||||
let value = CString::new(value).unwrap();
|
||||
unsafe {
|
||||
let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
|
||||
let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr);
|
||||
let name = name.as_raw();
|
||||
let value = value.as_ptr() as *mut _;
|
||||
|
||||
cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context, name, value)).map(X509Extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct X509NameBuilder(X509Name);
|
||||
|
||||
impl X509NameBuilder {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use hash::MessageDigest;
|
|||
use pkey::PKey;
|
||||
use rsa::Rsa;
|
||||
use x509::{X509, X509Generator, X509Name};
|
||||
use x509::extension::Extension::{KeyUsage, ExtKeyUsage, SubjectAltName, OtherNid, OtherStr};
|
||||
use x509::extension::{Extension, BasicConstraints, KeyUsage};
|
||||
use x509::extension::AltNameOption as SAN;
|
||||
use x509::extension::KeyUsageOption::{DigitalSignature, KeyEncipherment};
|
||||
use x509::extension::ExtKeyUsageOption::{self, ClientAuth, ServerAuth};
|
||||
|
|
@ -17,13 +17,13 @@ fn get_generator() -> X509Generator {
|
|||
.set_valid_period(365 * 2)
|
||||
.add_name("CN".to_string(), "test_me".to_string())
|
||||
.set_sign_hash(MessageDigest::sha1())
|
||||
.add_extension(KeyUsage(vec![DigitalSignature, KeyEncipherment]))
|
||||
.add_extension(ExtKeyUsage(vec![ClientAuth,
|
||||
.add_extension(Extension::KeyUsage(vec![DigitalSignature, KeyEncipherment]))
|
||||
.add_extension(Extension::ExtKeyUsage(vec![ClientAuth,
|
||||
ServerAuth,
|
||||
ExtKeyUsageOption::Other("2.999.1".to_owned())]))
|
||||
.add_extension(SubjectAltName(vec![(SAN::DNS, "example.com".to_owned())]))
|
||||
.add_extension(OtherNid(nid::BASIC_CONSTRAINTS, "critical,CA:TRUE".to_owned()))
|
||||
.add_extension(OtherStr("2.999.2".to_owned(), "ASN1:UTF8:example value".to_owned()))
|
||||
.add_extension(Extension::SubjectAltName(vec![(SAN::DNS, "example.com".to_owned())]))
|
||||
.add_extension(Extension::OtherNid(nid::BASIC_CONSTRAINTS, "critical,CA:TRUE".to_owned()))
|
||||
.add_extension(Extension::OtherStr("2.999.2".to_owned(), "ASN1:UTF8:example value".to_owned()))
|
||||
}
|
||||
|
||||
fn pkey() -> PKey {
|
||||
|
|
@ -50,8 +50,8 @@ fn test_cert_gen() {
|
|||
fn test_cert_gen_extension_ordering() {
|
||||
let pkey = pkey();
|
||||
get_generator()
|
||||
.add_extension(OtherNid(nid::SUBJECT_KEY_IDENTIFIER, "hash".to_owned()))
|
||||
.add_extension(OtherNid(nid::AUTHORITY_KEY_IDENTIFIER, "keyid:always".to_owned()))
|
||||
.add_extension(Extension::OtherNid(nid::SUBJECT_KEY_IDENTIFIER, "hash".to_owned()))
|
||||
.add_extension(Extension::OtherNid(nid::AUTHORITY_KEY_IDENTIFIER, "keyid:always".to_owned()))
|
||||
.sign(&pkey)
|
||||
.expect("Failed to generate cert with order-dependent extensions");
|
||||
}
|
||||
|
|
@ -62,8 +62,8 @@ fn test_cert_gen_extension_ordering() {
|
|||
fn test_cert_gen_extension_bad_ordering() {
|
||||
let pkey = pkey();
|
||||
let result = get_generator()
|
||||
.add_extension(OtherNid(nid::AUTHORITY_KEY_IDENTIFIER, "keyid:always".to_owned()))
|
||||
.add_extension(OtherNid(nid::SUBJECT_KEY_IDENTIFIER, "hash".to_owned()))
|
||||
.add_extension(Extension::OtherNid(nid::AUTHORITY_KEY_IDENTIFIER, "keyid:always".to_owned()))
|
||||
.add_extension(Extension::OtherNid(nid::SUBJECT_KEY_IDENTIFIER, "hash".to_owned()))
|
||||
.sign(&pkey);
|
||||
|
||||
assert!(result.is_err());
|
||||
|
|
@ -178,7 +178,7 @@ fn test_subject_alt_name_iter() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_x509_builder() {
|
||||
fn x509_builder() {
|
||||
let pkey = pkey();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
|
|
@ -196,6 +196,11 @@ fn test_x509_builder() {
|
|||
serial.rand(128, MSB_MAYBE_ZERO, false).unwrap();
|
||||
builder.set_serial_number(&serial.to_asn1_integer().unwrap()).unwrap();
|
||||
|
||||
let basic_constraints = BasicConstraints::new().critical(true).ca(true).build().unwrap();
|
||||
builder.append_extension(basic_constraints).unwrap();
|
||||
let key_usage = KeyUsage::new().digital_signature(true).key_encipherment(true).build().unwrap();
|
||||
builder.append_extension(key_usage).unwrap();
|
||||
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let x509 = builder.build();
|
||||
|
|
|
|||
Loading…
Reference in New Issue