| //! Bindings to Windows `PCCERT_CONTEXT` APIs. |
| |
| use std::ffi::{c_void, CStr, OsString}; |
| use std::io; |
| use std::mem; |
| use std::os::windows::prelude::*; |
| use std::ptr; |
| use std::slice; |
| |
| use windows_sys::Win32::Foundation; |
| use windows_sys::Win32::Security::Cryptography; |
| |
| use crate::cert_store::CertStore; |
| use crate::crypt_prov::{CryptProv, ProviderType}; |
| use crate::ncrypt_key::NcryptKey; |
| use crate::Inner; |
| |
| /// A supported hashing algorithm |
| pub struct HashAlgorithm(u32, usize); |
| |
| #[allow(missing_docs)] |
| impl HashAlgorithm { |
| pub fn md5() -> HashAlgorithm { |
| HashAlgorithm( |
| Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD5, |
| 16, |
| ) |
| } |
| |
| pub fn sha1() -> HashAlgorithm { |
| HashAlgorithm( |
| Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA1, |
| 20, |
| ) |
| } |
| |
| pub fn sha256() -> HashAlgorithm { |
| HashAlgorithm( |
| Cryptography::ALG_CLASS_HASH |
| | Cryptography::ALG_TYPE_ANY |
| | Cryptography::ALG_SID_SHA_256, |
| 32, |
| ) |
| } |
| |
| pub fn sha384() -> HashAlgorithm { |
| HashAlgorithm( |
| Cryptography::ALG_CLASS_HASH |
| | Cryptography::ALG_TYPE_ANY |
| | Cryptography::ALG_SID_SHA_384, |
| 48, |
| ) |
| } |
| |
| pub fn sha512() -> HashAlgorithm { |
| HashAlgorithm( |
| Cryptography::ALG_CLASS_HASH |
| | Cryptography::ALG_TYPE_ANY |
| | Cryptography::ALG_SID_SHA_512, |
| 64, |
| ) |
| } |
| } |
| |
| /// Wrapper of a winapi certificate, or a `PCCERT_CONTEXT`. |
| #[derive(Debug)] |
| pub struct CertContext(*const Cryptography::CERT_CONTEXT); |
| |
| unsafe impl Sync for CertContext {} |
| unsafe impl Send for CertContext {} |
| |
| impl Drop for CertContext { |
| fn drop(&mut self) { |
| unsafe { |
| Cryptography::CertFreeCertificateContext(self.0); |
| } |
| } |
| } |
| |
| impl Clone for CertContext { |
| fn clone(&self) -> CertContext { |
| unsafe { CertContext(Cryptography::CertDuplicateCertificateContext(self.0)) } |
| } |
| } |
| |
| inner!(CertContext, *const Cryptography::CERT_CONTEXT); |
| |
| impl CertContext { |
| /// Decodes a DER-formatted X509 certificate. |
| pub fn new(data: &[u8]) -> io::Result<CertContext> { |
| let ret = unsafe { |
| Cryptography::CertCreateCertificateContext( |
| Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING, |
| data.as_ptr(), |
| data.len() as u32, |
| ) |
| }; |
| if ret.is_null() { |
| Err(io::Error::last_os_error()) |
| } else { |
| Ok(CertContext(ret)) |
| } |
| } |
| |
| /// Get certificate in binary DER form |
| pub fn to_der(&self) -> &[u8] { |
| self.get_encoded_bytes() |
| } |
| |
| /// Certificate subject public key info |
| pub fn subject_public_key_info_der(&self) -> io::Result<Vec<u8>> { |
| unsafe { |
| let mut len: u32 = 0; |
| let ok = Cryptography::CryptEncodeObjectEx( |
| Cryptography::X509_ASN_ENCODING, |
| Cryptography::CERT_INFO_SUBJECT_PUBLIC_KEY_INFO_FLAG as *const u8, |
| &(*(*self.0).pCertInfo).SubjectPublicKeyInfo |
| as *const Cryptography::CERT_PUBLIC_KEY_INFO as _, |
| Cryptography::CRYPT_ENCODE_OBJECT_FLAGS::default(), |
| ptr::null_mut(), |
| ptr::null_mut(), |
| &mut len, |
| ); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| if len > 0 { |
| let mut buf = vec![0; len as usize]; |
| let ok = Cryptography::CryptEncodeObjectEx( |
| Cryptography::X509_ASN_ENCODING, |
| Cryptography::CERT_INFO_SUBJECT_PUBLIC_KEY_INFO_FLAG as *const u32 as *const _, |
| &(*(*self.0).pCertInfo).SubjectPublicKeyInfo |
| as *const Cryptography::CERT_PUBLIC_KEY_INFO as _, |
| Cryptography::CRYPT_ENCODE_OBJECT_FLAGS::default(), |
| ptr::null_mut(), |
| buf.as_mut_ptr() as _, |
| &mut len, |
| ); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| return Ok(buf); |
| } |
| } |
| Err(io::Error::last_os_error()) |
| } |
| |
| /// Decodes a PEM-formatted X509 certificate. |
| pub fn from_pem(pem: &str) -> io::Result<CertContext> { |
| unsafe { |
| assert!(pem.len() <= u32::max_value() as usize); |
| |
| let mut len = 0; |
| let ok = Cryptography::CryptStringToBinaryA( |
| pem.as_ptr(), |
| pem.len() as u32, |
| Cryptography::CRYPT_STRING_BASE64HEADER, |
| ptr::null_mut(), |
| &mut len, |
| ptr::null_mut(), |
| ptr::null_mut(), |
| ); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let mut buf = vec![0; len as usize]; |
| let ok = Cryptography::CryptStringToBinaryA( |
| pem.as_ptr(), |
| pem.len() as u32, |
| Cryptography::CRYPT_STRING_BASE64HEADER, |
| buf.as_mut_ptr(), |
| &mut len, |
| ptr::null_mut(), |
| ptr::null_mut(), |
| ); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| CertContext::new(&buf) |
| } |
| } |
| |
| /// Get certificate as PEM-formatted X509 certificate. |
| pub fn to_pem(&self) -> io::Result<String> { |
| unsafe { |
| let mut len = 0; |
| let ok = Cryptography::CryptBinaryToStringA( |
| (*self.0).pbCertEncoded, |
| (*self.0).cbCertEncoded, |
| Cryptography::CRYPT_STRING_BASE64HEADER, |
| ptr::null_mut(), |
| &mut len, |
| ); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let mut buf = vec![0; len as usize]; |
| let ok = Cryptography::CryptBinaryToStringA( |
| (*self.0).pbCertEncoded, |
| (*self.0).cbCertEncoded, |
| Cryptography::CRYPT_STRING_BASE64HEADER, |
| buf.as_mut_ptr(), |
| &mut len, |
| ); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| Ok(CStr::from_ptr(buf.as_ptr() as *const _) |
| .to_string_lossy() |
| .into_owned()) |
| } |
| } |
| |
| /// Returns a hash of this certificate |
| pub fn fingerprint(&self, alg: HashAlgorithm) -> io::Result<Vec<u8>> { |
| unsafe { |
| let mut buf = vec![0u8; alg.1]; |
| let mut len = buf.len() as u32; |
| |
| let ret = Cryptography::CryptHashCertificate( |
| Cryptography::HCRYPTPROV_LEGACY::default(), |
| alg.0, |
| 0, |
| (*self.0).pbCertEncoded, |
| (*self.0).cbCertEncoded, |
| buf.as_mut_ptr(), |
| &mut len, |
| ); |
| |
| if ret == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| Ok(buf) |
| } |
| } |
| |
| /// Returns the sha1 hash of this certificate |
| /// |
| /// The sha1 is returned as a 20-byte array representing the bits of the |
| /// sha1 hash. |
| #[deprecated(note = "please use fingerprint instead")] |
| pub fn sha1(&self) -> io::Result<[u8; 20]> { |
| let mut out = [0u8; 20]; |
| out.copy_from_slice(&self.fingerprint(HashAlgorithm::sha1())?); |
| Ok(out) |
| } |
| |
| /// Returns the `<SIGNATURE>/<HASH>` string representing the certificate |
| /// signature. |
| /// |
| /// The `<SIGNATURE>` value identifies the CNG public key |
| /// algorithm. The `<HASH>` value identifies the CNG hash algorithm. |
| /// |
| /// Common examples are: |
| /// |
| /// * `RSA/SHA1` |
| /// * `RSA/SHA256` |
| /// * `ECDSA/SHA256` |
| pub fn sign_hash_algorithms(&self) -> io::Result<String> { |
| self.get_string(Cryptography::CERT_SIGN_HASH_CNG_ALG_PROP_ID) |
| } |
| |
| /// Returns the signature hash. |
| pub fn signature_hash(&self) -> io::Result<Vec<u8>> { |
| self.get_bytes(Cryptography::CERT_SIGNATURE_HASH_PROP_ID) |
| } |
| |
| /// Returns the property displayed by the certificate UI. This property |
| /// allows the user to describe the certificate's use. |
| pub fn description(&self) -> io::Result<Vec<u8>> { |
| self.get_bytes(Cryptography::CERT_DESCRIPTION_PROP_ID) |
| } |
| |
| /// Returns a string that contains the display name for the certificate. |
| pub fn friendly_name(&self) -> io::Result<String> { |
| self.get_string(Cryptography::CERT_FRIENDLY_NAME_PROP_ID) |
| } |
| |
| /// Configures the string that contains the display name for this |
| /// certificate. |
| pub fn set_friendly_name(&self, name: &str) -> io::Result<()> { |
| self.set_string(Cryptography::CERT_FRIENDLY_NAME_PROP_ID, name) |
| } |
| |
| /// Verifies the time validity of this certificate relative to the system's |
| /// current time. |
| pub fn is_time_valid(&self) -> io::Result<bool> { |
| let ret = |
| unsafe { Cryptography::CertVerifyTimeValidity(ptr::null_mut(), (*self.0).pCertInfo) }; |
| Ok(ret == 0) |
| } |
| |
| /// Returns a builder used to acquire the private key corresponding to this certificate. |
| pub fn private_key(&self) -> AcquirePrivateKeyOptions { |
| AcquirePrivateKeyOptions { |
| cert: self, |
| flags: 0, |
| } |
| } |
| |
| /// Deletes this certificate from its certificate store. |
| pub fn delete(self) -> io::Result<()> { |
| unsafe { |
| let ret = Cryptography::CertDeleteCertificateFromStore(self.0); |
| mem::forget(self); |
| if ret != 0 { |
| Ok(()) |
| } else { |
| Err(io::Error::last_os_error()) |
| } |
| } |
| } |
| |
| /// Returns a builder used to set the private key associated with this certificate. |
| pub fn set_key_prov_info(&self) -> SetKeyProvInfo { |
| SetKeyProvInfo { |
| cert: self, |
| container: None, |
| provider: None, |
| type_: 0, |
| flags: 0, |
| key_spec: 0, |
| } |
| } |
| |
| /// Returns the valid uses for this certificate |
| pub fn valid_uses(&self) -> io::Result<ValidUses> { |
| unsafe { |
| let mut buf_len = 0; |
| let ok = |
| Cryptography::CertGetEnhancedKeyUsage(self.0, 0, ptr::null_mut(), &mut buf_len); |
| |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let mut buf = vec![0u8; buf_len as usize]; |
| let cert_enhkey_usage = buf.as_mut_ptr() as *mut Cryptography::CTL_USAGE; |
| |
| let ok = |
| Cryptography::CertGetEnhancedKeyUsage(self.0, 0, cert_enhkey_usage, &mut buf_len); |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let use_cnt = (*cert_enhkey_usage).cUsageIdentifier; |
| if use_cnt == 0 { |
| let last_error = io::Error::last_os_error(); |
| match last_error.raw_os_error() { |
| Some(Foundation::CRYPT_E_NOT_FOUND) => return Ok(ValidUses::All), |
| Some(Foundation::S_OK) => (), |
| _ => return Err(last_error), |
| }; |
| } |
| |
| let mut oids: Vec<String> = Vec::with_capacity(use_cnt as usize); |
| for i in 0..use_cnt { |
| let oid_ptr = (*cert_enhkey_usage).rgpszUsageIdentifier; |
| oids.push( |
| CStr::from_ptr(*(oid_ptr.offset(i as isize)) as *const _) |
| .to_string_lossy() |
| .into_owned(), |
| ); |
| } |
| Ok(ValidUses::Oids(oids)) |
| } |
| } |
| |
| /// For a remote certificate, returns a certificate store containing any intermediate |
| /// certificates provided by the remote sender. |
| pub fn cert_store(&self) -> Option<CertStore> { |
| unsafe { |
| let chain = (*self.0).hCertStore; |
| if chain.is_null() { |
| None |
| } else { |
| Some(CertStore::from_inner(Cryptography::CertDuplicateStore( |
| chain, |
| ))) |
| } |
| } |
| } |
| |
| fn get_encoded_bytes(&self) -> &[u8] { |
| unsafe { |
| let cert_ctx = *self.0; |
| slice::from_raw_parts(cert_ctx.pbCertEncoded, cert_ctx.cbCertEncoded as usize) |
| } |
| } |
| |
| fn get_bytes(&self, prop: u32) -> io::Result<Vec<u8>> { |
| unsafe { |
| let mut len = 0; |
| let ret = Cryptography::CertGetCertificateContextProperty( |
| self.0, |
| prop, |
| ptr::null_mut(), |
| &mut len, |
| ); |
| if ret == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let mut buf = vec![0u8; len as usize]; |
| let ret = Cryptography::CertGetCertificateContextProperty( |
| self.0, |
| prop, |
| buf.as_mut_ptr() as *mut c_void, |
| &mut len, |
| ); |
| if ret == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| Ok(buf) |
| } |
| } |
| |
| fn get_string(&self, prop: u32) -> io::Result<String> { |
| unsafe { |
| let mut len = 0; |
| let ret = Cryptography::CertGetCertificateContextProperty( |
| self.0, |
| prop, |
| ptr::null_mut(), |
| &mut len, |
| ); |
| if ret == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| // Divide by 2 b/c `len` is the byte length, but we're allocating |
| // u16 pairs which are 2 bytes each. |
| let amt = (len / 2) as usize; |
| let mut buf = vec![0u16; amt]; |
| let ret = Cryptography::CertGetCertificateContextProperty( |
| self.0, |
| prop, |
| buf.as_mut_ptr() as *mut c_void, |
| &mut len, |
| ); |
| if ret == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| // Chop off the trailing nul byte |
| Ok(OsString::from_wide(&buf[..amt - 1]).into_string().unwrap()) |
| } |
| } |
| |
| fn set_string(&self, prop: u32, s: &str) -> io::Result<()> { |
| unsafe { |
| let data = s.encode_utf16().chain(Some(0)).collect::<Vec<_>>(); |
| let data = Cryptography::CRYPT_INTEGER_BLOB { |
| cbData: (data.len() * 2) as u32, |
| pbData: data.as_ptr() as *mut _, |
| }; |
| let ret = Cryptography::CertSetCertificateContextProperty( |
| self.0, |
| prop, |
| 0, |
| &data as *const _ as *const _, |
| ); |
| if ret == 0 { |
| Err(io::Error::last_os_error()) |
| } else { |
| Ok(()) |
| } |
| } |
| } |
| } |
| |
| impl PartialEq for CertContext { |
| fn eq(&self, other: &CertContext) -> bool { |
| self.get_encoded_bytes() == other.get_encoded_bytes() |
| } |
| } |
| |
| /// A builder type for certificate private key lookup. |
| pub struct AcquirePrivateKeyOptions<'a> { |
| cert: &'a CertContext, |
| flags: u32, |
| } |
| |
| impl<'a> AcquirePrivateKeyOptions<'a> { |
| /// If set, the certificate's public key will be compared with the private key to ensure a |
| /// match. |
| pub fn compare_key(&mut self, compare_key: bool) -> &mut AcquirePrivateKeyOptions<'a> { |
| self.flag(Cryptography::CRYPT_ACQUIRE_COMPARE_KEY_FLAG, compare_key) |
| } |
| |
| /// If set, the lookup will not display any user interface, even if that causes the lookup to |
| /// fail. |
| pub fn silent(&mut self, silent: bool) -> &mut AcquirePrivateKeyOptions<'a> { |
| self.flag(Cryptography::CRYPT_ACQUIRE_SILENT_FLAG, silent) |
| } |
| |
| fn flag(&mut self, flag: u32, set: bool) -> &mut AcquirePrivateKeyOptions<'a> { |
| if set { |
| self.flags |= flag; |
| } else { |
| self.flags &= !flag; |
| } |
| self |
| } |
| |
| /// Acquires the private key handle. |
| pub fn acquire(&self) -> io::Result<PrivateKey> { |
| unsafe { |
| let flags = self.flags | Cryptography::CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG; |
| let mut handle = Cryptography::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE::default(); |
| let mut spec = Cryptography::CERT_KEY_SPEC::default(); |
| let mut free = Foundation::BOOL::default(); |
| let res = Cryptography::CryptAcquireCertificatePrivateKey( |
| self.cert.0, |
| flags, |
| ptr::null_mut(), |
| &mut handle, |
| &mut spec, |
| &mut free, |
| ); |
| if res == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| assert_ne!(free, 0); |
| if spec & Cryptography::CERT_NCRYPT_KEY_SPEC != 0 { |
| Ok(PrivateKey::NcryptKey(NcryptKey::from_inner(handle))) |
| } else { |
| Ok(PrivateKey::CryptProv(CryptProv::from_inner(handle))) |
| } |
| } |
| } |
| } |
| |
| /// The private key associated with a certificate context. |
| pub enum PrivateKey { |
| /// A CryptoAPI provider. |
| CryptProv(CryptProv), |
| /// A CNG provider. |
| NcryptKey(NcryptKey), |
| } |
| |
| /// A builder used to set the private key associated with a certificate. |
| pub struct SetKeyProvInfo<'a> { |
| cert: &'a CertContext, |
| container: Option<Vec<u16>>, |
| provider: Option<Vec<u16>>, |
| type_: u32, |
| flags: u32, |
| key_spec: u32, |
| } |
| |
| impl<'a> SetKeyProvInfo<'a> { |
| /// The name of the key container. |
| /// |
| /// If `type_` is not provided, this specifies the name of the key withing |
| /// the CNG key storage provider. |
| pub fn container(&mut self, container: &str) -> &mut SetKeyProvInfo<'a> { |
| self.container = Some(container.encode_utf16().chain(Some(0)).collect()); |
| self |
| } |
| |
| /// The name of the CSP. |
| /// |
| /// If `type_` is not provided, this contains the name of the CNG key |
| /// storage provider. |
| pub fn provider(&mut self, provider: &str) -> &mut SetKeyProvInfo<'a> { |
| self.provider = Some(provider.encode_utf16().chain(Some(0)).collect()); |
| self |
| } |
| |
| /// Sets the CSP type. |
| /// |
| /// If not provided, the key container is one of the CNG key storage |
| /// providers. |
| pub fn type_(&mut self, type_: ProviderType) -> &mut SetKeyProvInfo<'a> { |
| self.type_ = type_.as_raw(); |
| self |
| } |
| |
| /// If set, the handle to the key provider can be kept open for subsequent |
| /// calls to cryptographic functions. |
| pub fn keep_open(&mut self, keep_open: bool) -> &mut SetKeyProvInfo<'a> { |
| self.flag(Cryptography::CERT_SET_KEY_PROV_HANDLE_PROP_ID, keep_open) |
| } |
| |
| /// If set, the key container contains machine keys. |
| pub fn machine_keyset(&mut self, machine_keyset: bool) -> &mut SetKeyProvInfo<'a> { |
| self.flag(Cryptography::CRYPT_MACHINE_KEYSET, machine_keyset) |
| } |
| |
| /// If set, the key container will attempt to open keys without any user |
| /// interface prompts. |
| pub fn silent(&mut self, silent: bool) -> &mut SetKeyProvInfo<'a> { |
| self.flag(Cryptography::CRYPT_SILENT, silent) |
| } |
| |
| fn flag(&mut self, flag: u32, on: bool) -> &mut SetKeyProvInfo<'a> { |
| if on { |
| self.flags |= flag; |
| } else { |
| self.flags &= !flag; |
| } |
| self |
| } |
| |
| /// The specification of the private key to retrieve. |
| pub fn key_spec(&mut self, key_spec: KeySpec) -> &mut SetKeyProvInfo<'a> { |
| self.key_spec = key_spec.0; |
| self |
| } |
| |
| /// Sets the private key for this certificate. |
| pub fn set(&mut self) -> io::Result<()> { |
| unsafe { |
| let container = self |
| .container |
| .as_ref() |
| .map(|s| s.as_ptr()) |
| .unwrap_or(ptr::null()); |
| let provider = self |
| .provider |
| .as_ref() |
| .map(|s| s.as_ptr()) |
| .unwrap_or(ptr::null()); |
| |
| let info = Cryptography::CRYPT_KEY_PROV_INFO { |
| pwszContainerName: container as *mut _, |
| pwszProvName: provider as *mut _, |
| dwProvType: self.type_, |
| dwFlags: self.flags, |
| cProvParam: 0, |
| rgProvParam: ptr::null_mut(), |
| dwKeySpec: self.key_spec, |
| }; |
| |
| let res = Cryptography::CertSetCertificateContextProperty( |
| self.cert.0, |
| Cryptography::CERT_KEY_PROV_INFO_PROP_ID, |
| 0, |
| &info as *const _ as *const _, |
| ); |
| if res != 0 { |
| Ok(()) |
| } else { |
| Err(io::Error::last_os_error()) |
| } |
| } |
| } |
| } |
| |
| /// The specification of a private key. |
| #[derive(Copy, Clone)] |
| pub struct KeySpec(u32); |
| |
| impl KeySpec { |
| /// A key used to encrypt/decrypt session keys. |
| pub fn key_exchange() -> KeySpec { |
| KeySpec(Cryptography::AT_KEYEXCHANGE) |
| } |
| |
| /// A key used to create and verify digital signatures. |
| pub fn signature() -> KeySpec { |
| KeySpec(Cryptography::AT_SIGNATURE) |
| } |
| } |
| |
| /// Valid uses of a Certificate - All, or specific OIDs |
| pub enum ValidUses { |
| /// Certificate is valid for all uses |
| All, |
| |
| /// Certificate is valid for uses specified. No entries means that the certificate |
| /// has no valid uses. |
| Oids(Vec<String>), |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| #[test] |
| fn decode() { |
| let der = include_bytes!("../test/cert.der"); |
| let pem = include_str!("../test/cert.pem"); |
| |
| let der = CertContext::new(der).unwrap(); |
| let pem = CertContext::from_pem(pem).unwrap(); |
| assert_eq!(der, pem); |
| } |
| |
| #[test] |
| fn certcontext_to_der() { |
| let der = include_bytes!("../test/cert.der"); |
| let cert = CertContext::new(der).unwrap(); |
| let der2 = CertContext::to_der(&cert); |
| assert_eq!(der as &[u8], der2); |
| } |
| |
| #[test] |
| fn certcontext_to_pem() { |
| let der = include_bytes!("../test/cert.der"); |
| let pem1 = include_str!("../test/cert.pem").replace('\r', ""); |
| |
| let der = CertContext::new(der).unwrap(); |
| let pem2 = CertContext::to_pem(&der).unwrap().replace('\r', ""); |
| assert_eq!(pem1, pem2); |
| } |
| |
| #[test] |
| fn fingerprint() { |
| let der = include_bytes!("../test/cert.der"); |
| let pem = include_str!("../test/cert.pem"); |
| |
| let der = CertContext::new(der).unwrap(); |
| let pem = CertContext::from_pem(pem).unwrap(); |
| |
| let hash = der.fingerprint(HashAlgorithm::sha1()).unwrap(); |
| assert_eq!( |
| hash, |
| vec![ |
| 0x59, 0x17, 0x2D, 0x93, 0x13, 0xE8, 0x44, 0x59, 0xBC, 0xFF, 0x27, 0xF9, 0x67, 0xE7, |
| 0x9E, 0x6E, 0x92, 0x17, 0xE5, 0x84 |
| ] |
| ); |
| assert_eq!(hash, pem.fingerprint(HashAlgorithm::sha1()).unwrap()); |
| |
| let hash = der.fingerprint(HashAlgorithm::sha256()).unwrap(); |
| assert_eq!( |
| hash, |
| vec![ |
| 0x47, 0x12, 0xB9, 0x39, 0xFB, 0xCB, 0x42, 0xA6, 0xB5, 0x10, 0x1B, 0x42, 0x13, 0x9A, |
| 0x25, 0xB1, 0x4F, 0x81, 0xB4, 0x18, 0xFA, 0xCA, 0xBD, 0x37, 0x87, 0x46, 0xF1, 0x2F, |
| 0x85, 0xCC, 0x65, 0x44 |
| ] |
| ); |
| assert_eq!(hash, pem.fingerprint(HashAlgorithm::sha256()).unwrap()); |
| } |
| } |