Skip to content

Commit 1f757b5

Browse files
SergejJureckokornelski
authored andcommitted
Public key DER export (#75)
1 parent 50ed298 commit 1f757b5

File tree

4 files changed

+152
-3
lines changed

4 files changed

+152
-3
lines changed

security-framework-sys/src/certificate.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ extern "C" {
3939
) -> SecCertificateRef;
4040
pub fn SecCertificateCopyData(certificate: SecCertificateRef) -> CFDataRef;
4141
pub fn SecCertificateCopySubjectSummary(certificate: SecCertificateRef) -> CFStringRef;
42-
#[cfg(target_os = "macos")]
4342
pub fn SecCertificateCopyCommonName(
4443
certificate: SecCertificateRef,
4544
common_name: *mut CFStringRef,

security-framework-sys/src/key.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use core_foundation_sys::base::CFTypeID;
2-
#[cfg(target_os = "macos")]
32
use core_foundation_sys::data::CFDataRef;
43
use core_foundation_sys::dictionary::CFDictionaryRef;
5-
#[cfg(target_os = "macos")]
64
use core_foundation_sys::error::CFErrorRef;
75

86
use base::SecKeyRef;

security-framework/src/certificate.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,31 @@ use core_foundation_sys::base::kCFAllocatorDefault;
77
use security_framework_sys::base::{errSecParam, SecCertificateRef};
88
use security_framework_sys::certificate::*;
99
use std::fmt;
10+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
11+
use std::ptr;
1012

1113
use base::{Error, Result};
14+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
15+
use core_foundation::base::FromVoid;
16+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
17+
use core_foundation::number::CFNumber;
18+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
19+
use core_foundation_sys::base::CFRelease;
20+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
21+
use security_framework_sys::base::SecPolicyRef;
22+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
23+
use security_framework_sys::item::*;
24+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
25+
use security_framework_sys::policy::SecPolicyCreateBasicX509;
26+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
27+
use security_framework_sys::trust::{
28+
SecTrustCopyPublicKey, SecTrustCreateWithCertificates, SecTrustEvaluate, SecTrustRef,
29+
SecTrustResultType,
30+
};
31+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
32+
use std::ops::Deref;
33+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
34+
use {cvt, key};
1235

1336
declare_TCFType! {
1437
/// A type representing a certificate.
@@ -57,8 +80,107 @@ impl SecCertificate {
5780
CFString::wrap_under_create_rule(summary).to_string()
5881
}
5982
}
83+
84+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
85+
/// Returns DER encoded subjectPublicKeyInfo of certificate if available. This can be used
86+
/// for certificate pinning.
87+
pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> {
88+
// Imported from TrustKit
89+
// https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/TSKSPKIHashCache.m
90+
let public_key = self.public_key()?;
91+
Ok(self.pk_to_der(public_key))
92+
}
93+
94+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
95+
fn pk_to_der(&self, public_key: key::SecKey) -> Option<Vec<u8>> {
96+
let public_key_attributes = public_key.attributes();
97+
let public_key_type =
98+
public_key_attributes.find(unsafe { kSecAttrKeyType } as *const std::os::raw::c_void)?;
99+
let public_keysize = public_key_attributes
100+
.find(unsafe { kSecAttrKeySizeInBits } as *const std::os::raw::c_void)?;
101+
let public_keysize = unsafe { CFNumber::from_void(*public_keysize.deref()) };
102+
let public_keysize_val = public_keysize.to_i64()? as u32;
103+
let hdr_bytes = get_asn1_header_bytes(
104+
unsafe { CFString::wrap_under_get_rule(*public_key_type.deref() as _) },
105+
public_keysize_val,
106+
)?;
107+
let public_key_data = public_key.external_representation()?;
108+
let mut out = Vec::with_capacity(hdr_bytes.len() + public_key_data.len() as usize);
109+
out.extend_from_slice(hdr_bytes);
110+
out.extend_from_slice(public_key_data.bytes());
111+
Some(out)
112+
}
113+
114+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
115+
/// Get public key from certificate
116+
pub fn public_key(&self) -> Result<key::SecKey> {
117+
unsafe {
118+
// Create an X509 trust using the using the certificate
119+
let mut trust: SecTrustRef = ptr::null_mut();
120+
let policy: SecPolicyRef = SecPolicyCreateBasicX509();
121+
cvt(SecTrustCreateWithCertificates(
122+
self.as_concrete_TypeRef() as _,
123+
policy as _,
124+
&mut trust,
125+
))?;
126+
127+
// Get a public key reference for the certificate from the trust
128+
let mut result: SecTrustResultType = 0;
129+
cvt(SecTrustEvaluate(trust, &mut result))?;
130+
let public_key = SecTrustCopyPublicKey(trust);
131+
CFRelease(policy as _);
132+
CFRelease(trust as _);
133+
134+
Ok(key::SecKey::wrap_under_create_rule(public_key))
135+
}
136+
}
60137
}
61138

139+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
140+
fn get_asn1_header_bytes(pkt: CFString, ksz: u32) -> Option<&'static [u8]> {
141+
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 2048 {
142+
return Some(&RSA_2048_ASN1_HEADER);
143+
}
144+
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 4096 {
145+
return Some(&RSA_4096_ASN1_HEADER);
146+
}
147+
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) }
148+
&& ksz == 256
149+
{
150+
return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER);
151+
}
152+
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) }
153+
&& ksz == 384
154+
{
155+
return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER);
156+
}
157+
None
158+
}
159+
160+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
161+
const RSA_2048_ASN1_HEADER: [u8; 24] = [
162+
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
163+
0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
164+
];
165+
166+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
167+
const RSA_4096_ASN1_HEADER: [u8; 24] = [
168+
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
169+
0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
170+
];
171+
172+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
173+
const EC_DSA_SECP_256_R1_ASN1_HEADER: [u8; 26] = [
174+
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
175+
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
176+
];
177+
178+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
179+
const EC_DSA_SECP_384_R1_ASN1_HEADER: [u8; 23] = [
180+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
181+
0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00,
182+
];
183+
62184
#[cfg(test)]
63185
mod test {
64186
use test::certificate;

security-framework/src/key.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
//! Encryption key support
22
33
use core_foundation::base::TCFType;
4+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
5+
use core_foundation::base::ToVoid;
6+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
7+
use core_foundation::data::CFData;
8+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
9+
use core_foundation::dictionary::CFDictionary;
10+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
11+
use core_foundation_sys::error::CFErrorRef;
412
use security_framework_sys::base::SecKeyRef;
513
use security_framework_sys::key::SecKeyGetTypeID;
14+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
15+
use security_framework_sys::key::{SecKeyCopyAttributes, SecKeyCopyExternalRepresentation};
616
use std::fmt;
717

818
declare_TCFType! {
@@ -14,6 +24,26 @@ impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
1424
unsafe impl Sync for SecKey {}
1525
unsafe impl Send for SecKey {}
1626

27+
impl SecKey {
28+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
29+
/// Translates to SecKeyCopyAttributes
30+
pub fn attributes(&self) -> CFDictionary {
31+
let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) };
32+
unsafe { CFDictionary::wrap_under_create_rule(pka) }
33+
}
34+
35+
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
36+
/// Translates to SecKeyCopyExternalRepresentation
37+
pub fn external_representation(&self) -> Option<CFData> {
38+
let mut error: CFErrorRef = ::std::ptr::null_mut();
39+
let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) };
40+
if data == ::std::ptr::null() {
41+
return None;
42+
}
43+
Some(unsafe { CFData::wrap_under_create_rule(data) })
44+
}
45+
}
46+
1747
// FIXME
1848
impl fmt::Debug for SecKey {
1949
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {

0 commit comments

Comments
 (0)