2 * Copyright (c) 2008-2010,2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <Security/SecCMS.h>
27 #include <Security/SecRandom.h>
28 #include <Security/SecIdentityPriv.h>
30 #include <AssertMacros.h>
31 #include <CommonCrypto/CommonDigest.h>
32 #include <CommonCrypto/CommonDigestSPI.h>
33 #include <Security/SecItem.h>
34 #include <Security/SecInternal.h>
35 #include <Security/SecCertificateInternal.h>
36 #include <Security/SecKeyPriv.h>
37 #include <Security/SecInternal.h>
38 #include <libDER/DER_Encode.h>
39 #include <uuid/uuid.h>
40 #include <utilities/array_size.h>
41 #include <utilities/debugging.h>
42 #include <utilities/SecIOFormat.h>
53 static CF_RETURNS_RETAINED CFDataRef
scep_oid(scep_attr_t type
)
55 /* +-------------------+-----------------------------------------------+
56 | Name | ASN.1 Definition |
57 +-------------------+-----------------------------------------------+
58 | id-VeriSign | OBJECT_IDENTIFIER ::= {2 16 US(840) 1 |
59 | | VeriSign(113733)} |
60 | id-pki | OBJECT_IDENTIFIER ::= {id-VeriSign pki(1)} |
61 | id-attributes | OBJECT_IDENTIFIER ::= {id-pki attributes(9)} |
62 | id-messageType | OBJECT_IDENTIFIER ::= {id-attributes |
64 | id-pkiStatus | OBJECT_IDENTIFIER ::= {id-attributes |
66 | id-failInfo | OBJECT_IDENTIFIER ::= {id-attributes |
68 | id-senderNonce | OBJECT_IDENTIFIER ::= {id-attributes |
70 | id-recipientNonce | OBJECT_IDENTIFIER ::= {id-attributes |
71 | | recipientNonce(6)} |
72 | id-transId | OBJECT_IDENTIFIER ::= {id-attributes |
74 | id-extensionReq | OBJECT_IDENTIFIER ::= {id-attributes |
75 | | extensionReq(8)} |
76 +-------------------+-----------------------------------------------+ */
77 uint8_t oid_scep_attrs
[] =
78 { 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0 };
79 /* messageType:2 pkiStatus:3 failInfo:4 senderNonce:5 recipientNonce:6 transId:7 */
80 if ((type
< messageType
) || (type
> transId
))
83 oid_scep_attrs
[sizeof(oid_scep_attrs
) - 1] = type
;
84 return CFDataCreate(kCFAllocatorDefault
, oid_scep_attrs
, sizeof(oid_scep_attrs
));
87 static const char CertRep
[] = "3";
88 static const char PKCSReq
[] = "19";
89 static const char GetCertInitial
[] = "20";
90 __unused
static const char GetCert
[] = "21";
91 __unused
static const char GetCRL
[] = "22";
92 static const char PKIStatusSUCCESS
[] = "0";
93 __unused
static const char PKIStatusFAILURE
[] = "2";
94 static const char PKIStatusPENDING
[] = "3";
96 static CF_RETURNS_RETAINED CFDataRef
97 printable_string_data(size_t length
, const char *bytes
)
99 DERSize der_length_len
= DERLengthOfLength(length
);
100 size_t value_length
= sizeof(SecASN1PrintableString
) + der_length_len
+ length
;
101 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, value_length
);
102 CFDataSetLength(data
, value_length
);
103 uint8_t *ptr
= (uint8_t *)CFDataGetBytePtr(data
);
104 *ptr
++ = SecASN1PrintableString
;
105 DEREncodeLength(length
, ptr
, &der_length_len
);
106 ptr
+= der_length_len
;
107 memcpy(ptr
, bytes
, length
);
108 return (CFDataRef
)data
;
111 #define scep_result(value) printable_string_data(sizeof(value)-1, value)
113 static CF_RETURNS_NOT_RETAINED CFTypeRef
114 dictionary_array_value_1(CFDictionaryRef attrs
, CFTypeRef attr
)
116 CFTypeRef value
= NULL
;
117 CFArrayRef attr_values
= NULL
;
119 require(attr_values
= (CFArrayRef
)CFDictionaryGetValue(attrs
, attr
), out
);
120 require(CFArrayGetCount(attr_values
) == 1, out
);
121 value
= CFArrayGetValueAtIndex(attr_values
, 0);
126 /* @@@ consider splitting into function returning single value
127 and function creating printable string from c str */
128 static bool scep_attr_has_val(CFDictionaryRef attrs
, scep_attr_t attr
, const char *val
)
131 CFDataRef msgtype_value_data
= printable_string_data(strlen(val
), val
);
132 CFArrayRef msgtype_value_datas
= CFArrayCreate(kCFAllocatorDefault
,
133 (const void **)&msgtype_value_data
, 1, &kCFTypeArrayCallBacks
);
134 CFRelease(msgtype_value_data
);
135 CFDataRef msgtype_oid_data
= scep_oid(attr
);
136 CFArrayRef msgtype_values
= (CFArrayRef
)CFDictionaryGetValue(attrs
, msgtype_oid_data
);
137 CFRelease(msgtype_oid_data
);
138 if (msgtype_values
&& CFEqual(msgtype_value_datas
, msgtype_values
))
140 CFRelease(msgtype_value_datas
);
145 static CF_RETURNS_RETAINED CFDataRef
hexencode(CFDataRef data
)
147 CFIndex ix
, length
= CFDataGetLength(data
);
148 const uint8_t *bin_data
= CFDataGetBytePtr(data
);
149 uint8_t *hex_data
= calloc(1, 2*length
+ 1);
150 require(length
&& bin_data
&& hex_data
, out
);
152 for (ix
= 0; ix
< length
; ix
++)
153 snprintf((char *)&hex_data
[2*ix
], 3, "%02X", bin_data
[ix
]);
155 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, hex_data
,
156 2*length
, kCFAllocatorMalloc
);
163 static CF_RETURNS_RETAINED CFDataRef
pubkeyhash(SecKeyRef key
)
165 CFTypeRef key_type
= NULL
;
166 CFDictionaryRef pubkey_attrs
= NULL
;
167 CFDataRef hash_pubkey_data
= NULL
, pubkey_data
= NULL
;
168 uint8_t pubkey_hash
[CC_SHA1_DIGEST_LENGTH
];
170 require(pubkey_attrs
= SecKeyCopyAttributeDictionary(key
), out
);
171 require( (key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyClass
)) &&
172 CFEqual(key_type
, kSecAttrKeyClassPublic
), out
);
173 require(pubkey_data
= CFDictionaryGetValue(pubkey_attrs
, kSecValueData
), out
);
174 require((unsigned long)CFDataGetLength(pubkey_data
)<=UINT32_MAX
, out
); /* Correct as long as CFIndex is long */
175 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(pubkey_data
), (CC_LONG
)CFDataGetLength(pubkey_data
), pubkey_hash
);
176 hash_pubkey_data
= CFDataCreate(kCFAllocatorDefault
, pubkey_hash
, sizeof(pubkey_hash
));
178 CFReleaseSafe(pubkey_attrs
);
179 return hash_pubkey_data
;
182 static int generate_sender_nonce(CFMutableDictionaryRef dict
)
184 /* random sender nonce, to be verified against recipient nonce in reply */
185 CFDataRef senderNonce_oid_data
= scep_oid(senderNonce
);
186 uint8_t senderNonce_value
[18] = { 4, 16, };
187 int status
= SecRandomCopyBytes(kSecRandomDefault
, sizeof(senderNonce_value
) - 2, senderNonce_value
+ 2);
188 CFDataRef senderNonce_value_data
= CFDataCreate(kCFAllocatorDefault
,
189 senderNonce_value
, sizeof(senderNonce_value
));
190 if (senderNonce_oid_data
&& senderNonce_value_data
)
191 CFDictionarySetValue(dict
, senderNonce_oid_data
, senderNonce_value_data
);
192 CFReleaseNull(senderNonce_oid_data
);
193 CFReleaseNull(senderNonce_value_data
);
197 SecIdentityRef
SecSCEPCreateTemporaryIdentity(SecKeyRef publicKey
, SecKeyRef privateKey
)
199 int key_usage
= kSecKeyUsageDigitalSignature
| kSecKeyUsageKeyEncipherment
;
200 CFDictionaryRef self_signed_parameters
= NULL
;
201 CFNumberRef key_usage_num
= NULL
;
202 SecCertificateRef self_signed_certificate
= NULL
;
203 SecIdentityRef self_signed_identity
= NULL
;
204 CFStringRef cn_uuid
= NULL
;
205 CFArrayRef cn_dn
= NULL
, cn_dns
= NULL
, unique_rdns
= NULL
;
207 key_usage_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_usage
);
208 require(key_usage_num
, out
);
210 const void *key
[] = { kSecCertificateKeyUsage
};
211 const void *val
[] = { key_usage_num
};
212 self_signed_parameters
= CFDictionaryCreate(kCFAllocatorDefault
,
213 key
, val
, array_size(key
),
214 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
215 require(self_signed_parameters
, out
);
217 char uuid_string
[37] = {};
219 uuid_generate_random(uuid
);
220 uuid_unparse(uuid
, uuid_string
);
221 cn_uuid
= CFStringCreateWithCString(kCFAllocatorDefault
, uuid_string
, kCFStringEncodingASCII
);
222 require(cn_uuid
, out
);
223 const void * cn
[] = { kSecOidCommonName
, cn_uuid
};
224 cn_dn
= CFArrayCreate(kCFAllocatorDefault
, cn
, 2, NULL
);
226 cn_dns
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&cn_dn
, 1, NULL
);
227 require(cn_dns
, out
);
228 unique_rdns
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&cn_dns
, 1, NULL
);
229 require(unique_rdns
, out
);
231 self_signed_certificate
= SecGenerateSelfSignedCertificate(unique_rdns
, self_signed_parameters
, publicKey
, privateKey
);
232 require(self_signed_certificate
, out
);
233 self_signed_identity
= SecIdentityCreate(kCFAllocatorDefault
, self_signed_certificate
, privateKey
);
236 CFReleaseSafe(key_usage_num
);
237 CFReleaseSafe(self_signed_parameters
);
238 CFReleaseSafe(self_signed_certificate
);
239 CFReleaseSafe(unique_rdns
);
240 CFReleaseSafe(cn_dns
);
241 CFReleaseSafe(cn_dn
);
242 CFReleaseSafe(cn_uuid
);
244 return self_signed_identity
;
248 SecSCEPGenerateCertificateRequest(CFArrayRef subject
, CFDictionaryRef parameters
,
249 SecKeyRef publicKey
, SecKeyRef privateKey
,
250 SecIdentityRef signer
, CFTypeRef recipients
)
252 CFDataRef csr
= NULL
;
253 CFMutableDataRef enveloped_data
= NULL
;
254 CFMutableDictionaryRef simple_attr
= NULL
;
255 SecIdentityRef self_signed_identity
= NULL
;
256 CFMutableDataRef signed_request
= NULL
;
257 SecCertificateRef recipient
= NULL
;
258 CFDataRef msgtype_value_data
= NULL
;
259 CFDataRef msgtype_oid_data
= NULL
;
261 if (CFGetTypeID(recipients
) == SecCertificateGetTypeID()) {
262 recipient
= (SecCertificateRef
)recipients
;
263 } else if (CFGetTypeID(recipients
) == CFArrayGetTypeID()) {
264 CFIndex recipient_count
= CFArrayGetCount(recipients
);
265 if (recipient_count
> 1) {
266 /* get the encryption cert */
267 recipient
= (SecCertificateRef
)CFArrayGetValueAtIndex(recipients
, 0);
268 } else if (recipient_count
== 1) {
269 /* if there is at least one we'll assume it's sign+encrypt */
270 recipient
= (SecCertificateRef
)CFArrayGetValueAtIndex(recipients
, 0);
273 require(recipient
, out
);
275 require(csr
= SecGenerateCertificateRequest(subject
, parameters
, publicKey
, privateKey
), out
);
276 require(enveloped_data
= CFDataCreateMutable(kCFAllocatorDefault
, 0), out
);
277 require_noerr(SecCMSCreateEnvelopedData(recipient
, parameters
, csr
, enveloped_data
), out
);
280 simple_attr
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 3,
281 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
283 /* generate a transaction id: hex encoded pubkey hash */
284 CFDataRef public_key_hash
= pubkeyhash(publicKey
);
285 CFDataRef public_key_hash_hex
= hexencode(public_key_hash
);
286 CFReleaseSafe(public_key_hash
);
287 CFDataRef transid_oid_data
= scep_oid(transId
);
288 CFDataRef transid_data
= printable_string_data(CFDataGetLength(public_key_hash_hex
),
289 (const char *)CFDataGetBytePtr(public_key_hash_hex
));
290 CFReleaseSafe(public_key_hash_hex
);
292 CFDictionarySetValue(simple_attr
, transid_oid_data
, transid_data
);
293 CFReleaseNull(transid_oid_data
);
294 CFReleaseNull(transid_data
);
296 /* message type: PKCSReq (19) */
297 msgtype_value_data
= NULL
;
298 msgtype_oid_data
= NULL
;
299 require(msgtype_oid_data
= scep_oid(messageType
), out
);
300 require(msgtype_value_data
= printable_string_data(strlen(PKCSReq
), PKCSReq
), out
);
302 CFDictionarySetValue(simple_attr
, msgtype_oid_data
, msgtype_value_data
);
303 CFReleaseNull(msgtype_oid_data
);
304 CFReleaseNull(msgtype_value_data
);
306 /* random sender nonce, to be verified against recipient nonce in reply */
307 require(generate_sender_nonce(simple_attr
) == errSecSuccess
, out
);
309 /* XXX/cs remove auto-generation once managedconfig is no longer using this */
311 self_signed_identity
= signer
;
312 CFRetain(self_signed_identity
);
314 self_signed_identity
= SecSCEPCreateTemporaryIdentity(publicKey
, privateKey
);
316 /* Add our temporary cert to the keychain for CMS decryption of
317 the reply. If we happened to have picked an existing UUID
318 we fail. We should pick a different UUID and try again. */
319 require(self_signed_identity
, out
);
320 CFDictionaryRef identity_add
= CFDictionaryCreate(NULL
,
321 (const void **)&kSecValueRef
, (const void **)&self_signed_identity
, 1, NULL
, NULL
);
322 require_noerr_action(SecItemAdd(identity_add
, NULL
), out
,
323 CFReleaseSafe(identity_add
));
324 CFReleaseSafe(identity_add
);
326 require(self_signed_identity
, out
);
328 signed_request
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
329 require_noerr_action(SecCMSCreateSignedData(self_signed_identity
, enveloped_data
,
330 parameters
, simple_attr
, signed_request
), out
, CFReleaseNull(signed_request
));
335 CFReleaseSafe(simple_attr
);
336 CFReleaseSafe(msgtype_oid_data
);
337 CFReleaseSafe(msgtype_value_data
);
338 CFReleaseSafe(self_signed_identity
);
339 CFReleaseSafe(enveloped_data
);
341 return signed_request
;
346 SecSCEPCertifyRequest(CFDataRef request
, SecIdentityRef ca_identity
, CFDataRef serialno
, bool pend_request
)
348 CFDictionaryRef simple_attr
= NULL
;
349 SecCertificateRef ca_certificate
= NULL
;
350 SecKeyRef ca_public_key
= NULL
;
351 SecCertificateRef cert
= NULL
;
352 SecPolicyRef policy
= NULL
;
353 CFDataRef cert_pkcs7
= NULL
;
354 CFMutableDataRef cert_msg
= NULL
;
355 CFMutableDataRef signed_reply
= NULL
;
356 SecTrustRef trust
= NULL
;
357 CFDataRef signed_content
= NULL
;
358 CFDictionaryRef signed_attributes
= NULL
;
359 SecCertificateRef signer_cert
= NULL
;
360 CFDataRef transid_oid_data
= NULL
, senderNonce_oid_data
= NULL
, transid_value
= NULL
;
361 CFDataRef subject
= NULL
, extensions
= NULL
, senderNonce_value
= NULL
;
362 CFStringRef challenge
= NULL
;
363 SecKeyRef tbsPublicKey
= NULL
;
364 CFMutableDataRef encrypted_content
= NULL
;
365 SecCertificateRef recipient
= NULL
;
366 CFDictionaryRef parameters
= NULL
;
368 require_noerr(SecIdentityCopyCertificate(ca_identity
, &ca_certificate
), out
);
369 ca_public_key
= SecCertificateCopyPublicKey(ca_certificate
); /*@@@*/
371 /* unwrap outer layer: */
372 policy
= SecPolicyCreateBasicX509();
374 require_noerr(SecCMSVerifyCopyDataAndAttributes(request
, NULL
,
375 policy
, &trust
, &signed_content
, &signed_attributes
), out
);
376 /* remember signer: is signer certified by us, then re-certify, no challenge needed */
377 SecTrustResultType result
;
378 require_noerr(SecTrustEvaluate(trust
, &result
), out
);
379 require (signer_cert
= SecTrustGetCertificateAtIndex(trust
, 0), out
);
380 bool recertify
= !SecCertificateIsSignedBy(signer_cert
, ca_public_key
);
382 /* msgType should be certreq msg */
383 require(scep_attr_has_val(signed_attributes
, messageType
, PKCSReq
), out
);
385 /* remember transaction id just for reuse */
386 require(transid_oid_data
= scep_oid(transId
), out
);
387 require(transid_value
=
388 dictionary_array_value_1(signed_attributes
, transid_oid_data
), out
);
390 /* senderNonce becomes recipientNonce */
391 require(senderNonce_oid_data
= scep_oid(senderNonce
), out
);
392 require(senderNonce_value
=
393 dictionary_array_value_1(signed_attributes
, senderNonce_oid_data
), out
);
395 /* decrypt the request */
396 encrypted_content
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
397 require_noerr(SecCMSDecryptEnvelopedData(signed_content
, encrypted_content
, &recipient
), out
);
398 require(recipient
&& CFEqual(ca_certificate
, recipient
), out
);
401 require(SecVerifyCertificateRequest(encrypted_content
, &tbsPublicKey
, &challenge
, &subject
, &extensions
), out
);
402 CFReleaseNull(encrypted_content
);
405 // alternatively send a pending message
406 // pkistatus {{id-attributes pkiStatus(3)} "FAILURE"}
407 // failInfo {{id-attributes failInfo(4)} "the reason to reject"}
410 /* verify challenge - this would need to be a callout that can determine
411 the challenge appropriate for the subject */
413 require( challenge
&& (CFStringGetTypeID() == CFGetTypeID(challenge
)) &&
414 CFEqual(CFSTR("magic"), challenge
), out
);
416 require(cert_msg
= CFDataCreateMutable(kCFAllocatorDefault
, 0), out
);
420 cert
= SecIdentitySignCertificate(ca_identity
, serialno
,
421 tbsPublicKey
, subject
, extensions
);
423 /* degenerate cms with cert */
424 require (cert_pkcs7
= SecCMSCreateCertificatesOnlyMessage(cert
), out
);
427 /* envelope for client */
428 require_noerr(SecCMSCreateEnvelopedData(signer_cert
, NULL
, cert_pkcs7
, cert_msg
), out
);
429 CFReleaseNull(cert_pkcs7
);
432 CFDataRef pki_status_oid
= scep_oid(pkiStatus
);
433 CFDataRef pki_status_value
= pend_request
? scep_result(PKIStatusPENDING
) : scep_result(PKIStatusSUCCESS
);
434 CFDataRef message_type_oid
= scep_oid(messageType
), message_type_value
= scep_result(CertRep
);
435 const void *oid
[] = { transid_oid_data
, pki_status_oid
, message_type_oid
};
436 const void *value
[] = { transid_value
, pki_status_value
, message_type_value
};
437 simple_attr
= CFDictionaryCreate(kCFAllocatorDefault
, oid
, value
, array_size(oid
),
438 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
439 CFReleaseSafe(pki_status_oid
); CFReleaseSafe(pki_status_value
);
440 CFReleaseSafe(message_type_oid
); CFReleaseSafe(message_type_value
);
442 /* sign with ra/ca cert and add attributes */
443 signed_reply
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
444 const void *signing_params
[] = { kSecCMSCertChainMode
};
445 const void *signing_params_vals
[] = { kSecCMSCertChainModeNone
};
446 parameters
= CFDictionaryCreate(kCFAllocatorDefault
, signing_params
, signing_params_vals
, array_size(signing_params
), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
447 require_noerr_action(SecCMSCreateSignedData(ca_identity
, cert_msg
, parameters
, simple_attr
, signed_reply
), out
, CFReleaseNull(signed_reply
));
450 CFReleaseSafe(ca_certificate
);
451 CFReleaseSafe(ca_public_key
);
453 CFReleaseSafe(cert_pkcs7
);
454 CFReleaseSafe(cert_msg
);
455 CFReleaseSafe(trust
);
456 CFReleaseSafe(policy
);
457 CFReleaseSafe(signed_content
);
458 CFReleaseSafe(signed_attributes
);
459 CFReleaseSafe(transid_oid_data
);
460 CFReleaseSafe(senderNonce_oid_data
);
461 CFReleaseSafe(subject
);
462 CFReleaseSafe(extensions
);
463 CFReleaseSafe(challenge
);
464 CFReleaseSafe(tbsPublicKey
);
465 CFReleaseSafe(encrypted_content
);
466 CFReleaseSafe(simple_attr
);
467 CFReleaseSafe(recipient
);
468 CFReleaseSafe(parameters
);
474 copy_signed_attr_printable_string_value(CFDictionaryRef signed_attributes
, scep_attr_t attr
)
476 CFStringRef printable_string
= NULL
;
477 CFDataRef key_oid
= NULL
;
479 key_oid
= scep_oid(attr
);
480 require(key_oid
, out
);
482 CFArrayRef values
= (CFArrayRef
)CFDictionaryGetValue(signed_attributes
, key_oid
);
483 require_quiet(values
&& (CFGetTypeID(values
) == CFArrayGetTypeID())
484 && (CFArrayGetCount(values
) == 1), out
);
485 CFDataRef value
= CFArrayGetValueAtIndex(values
, 0);
486 const uint8_t *bytes
= CFDataGetBytePtr(value
);
487 size_t length
= CFDataGetLength(value
);
488 require(length
>= 2, out
);
489 require(bytes
[0] == 0x13, out
);
490 /* no scep responses defined that are longer */
491 require(!(bytes
[1] & 0x80) && (bytes
[1] == length
-2), out
);
492 printable_string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
493 bytes
+ 2, length
- 2, kCFStringEncodingASCII
, false);
495 CFReleaseSafe(key_oid
);
497 return printable_string
;
501 SecSCEPVerifyReply(CFDataRef request
, CFDataRef reply
, CFTypeRef ca_certificates
,
502 CFErrorRef
*server_error
)
504 SecKeyRef ca_public_key
= NULL
;
505 SecCertificateRef cert
= NULL
;
506 SecPolicyRef policy
= NULL
;
507 CFDataRef cert_msg
= NULL
;
508 CFMutableDataRef enc_cert_msg
= NULL
;
509 SecTrustRef trust
= NULL
;
510 CFDataRef signed_content
= NULL
;
511 CFDictionaryRef signed_attributes
= NULL
;
512 CFDictionaryRef attributes
= NULL
;
513 SecCertificateRef signer_cert
= NULL
;
515 CFMutableDataRef encrypted_content
= NULL
;
516 SecCertificateRef recipient
= NULL
;
517 CFArrayRef certificates
= NULL
;
519 SecCertificateRef reply_signer
= NULL
;
521 CFStringRef msg_type
= NULL
;
522 CFStringRef pki_status
= NULL
;
524 if (CFGetTypeID(ca_certificates
) == SecCertificateGetTypeID()) {
525 reply_signer
= (SecCertificateRef
)ca_certificates
;
526 } else if (CFGetTypeID(ca_certificates
) == CFArrayGetTypeID()) {
527 CFIndex reply_signer_count
= CFArrayGetCount(ca_certificates
);
528 if (reply_signer_count
> 1) {
529 /* get the signer cert */
530 reply_signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(ca_certificates
, 1);
531 } else if (reply_signer_count
== 1) {
532 /* if there is at least one we'll assume it's sign+encrypt */
533 reply_signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(ca_certificates
, 0);
536 require(reply_signer
, out
);
538 /* unwrap outer layer */
539 policy
= SecPolicyCreateBasicX509();
540 CFArrayRef additional_certificates
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&reply_signer
, 1, &kCFTypeArrayCallBacks
);
541 require_noerr(SecCMSVerifySignedData(reply
, NULL
,
542 policy
, &trust
, additional_certificates
, &signed_content
, &attributes
), out
);
543 CFReleaseSafe(additional_certificates
);
545 signed_attributes
= CFDictionaryGetValue(attributes
, kSecCMSSignedAttributes
);
547 /* response should be signed by ra */
548 SecTrustResultType result
;
549 require_noerr(SecTrustEvaluate(trust
, &result
), out
);
550 require(signer_cert
= SecTrustGetCertificateAtIndex(trust
, 0), out
);
551 require(CFEqual(reply_signer
, signer_cert
), out
);
553 /* msgType should be certreq msg */
554 require(signed_attributes
, out
);
555 msg_type
= copy_signed_attr_printable_string_value(signed_attributes
, messageType
);
556 pki_status
= copy_signed_attr_printable_string_value(signed_attributes
, pkiStatus
);
558 if (msg_type
|| pki_status
) {
559 require(msg_type
&& CFEqual(msg_type
, CFSTR("3")), out
);
561 require(pki_status
, out
);
562 if (CFEqual(pki_status
, CFSTR("2"))) {
563 goto out
; // FAILURE, the end (return NULL)
564 } else if (CFEqual(pki_status
, CFSTR("3"))) {
565 CFDataRef transid_oid_data
= NULL
, transid_value
= NULL
;
566 CFDictionaryRef err_dict
= NULL
;
567 require(transid_oid_data
= scep_oid(transId
), inner_out
);
568 require(transid_value
= dictionary_array_value_1(signed_attributes
, transid_oid_data
), inner_out
);
569 err_dict
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)&transid_oid_data
, (const void **)&transid_value
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
571 *server_error
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("PENDING"), 3, err_dict
);
573 CFReleaseSafe(err_dict
);
574 CFReleaseSafe(transid_oid_data
);
577 require(CFEqual(pki_status
, CFSTR("0")), out
);
580 // can we decode the request?
581 encrypted_content
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
582 require_noerr(SecCMSDecryptEnvelopedData(signed_content
, encrypted_content
, &recipient
), out
);
583 require(recipient
, out
);
584 // verify recipient belongs with our private key
587 require(certificates
= SecCMSCertificatesOnlyMessageCopyCertificates(encrypted_content
), out
);
589 // recipient is either our temporary self-signed cert or the old cert we just used
590 // to recertify. if we have new certificates and have stored them successfully we
591 // can now get rid of the cert.
593 This should move outside of thise function when we force a signer
595 CFDictionaryRef cert_delete
= CFDictionaryCreate(NULL
,
596 (const void **)&kSecValueRef
, (const void **)&recipient
, 1, NULL
, NULL
);
597 require_noerr_action(SecItemDelete(cert_delete
), out
,
598 CFReleaseSafe(cert_delete
));
599 CFReleaseSafe(cert_delete
);
602 CFReleaseSafe(ca_public_key
);
604 CFReleaseSafe(cert_msg
);
605 CFReleaseSafe(enc_cert_msg
);
606 CFReleaseSafe(trust
);
607 CFReleaseSafe(policy
);
608 CFReleaseSafe(signed_content
);
609 CFReleaseSafe(encrypted_content
);
610 CFReleaseSafe(recipient
);
611 CFReleaseSafe(msg_type
);
612 CFReleaseSafe(pki_status
);
613 CFReleaseSafe(attributes
);
618 OSStatus
SecSCEPValidateCACertMessage(CFArrayRef certs
,
619 CFDataRef ca_fingerprint
,
620 SecCertificateRef
*ca_certificate
,
621 SecCertificateRef
*ra_signing_certificate
,
622 SecCertificateRef
*ra_encryption_certificate
)
624 OSStatus status
= errSecParam
;
625 SecCertificateRef _ca_certificate
= NULL
, _ra_signing_certificate
= NULL
,
626 _ra_encryption_certificate
= NULL
, _ra_certificate
= NULL
;
627 CFDataRef ca_cert_data
= NULL
;
628 CFDataRef ca_hash_cfdata
= NULL
;
629 CFIndex j
, count
= CFArrayGetCount(certs
);
630 CFMutableArrayRef chain
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
631 SecPolicyRef policy
= SecPolicyCreateBasicX509();
632 SecTrustRef trust
= NULL
;
634 for (j
=0; j
<count
; j
++) {
635 const void *candidate_leaf
= CFArrayGetValueAtIndex(certs
, j
);
636 CFArrayRemoveAllValues(chain
);
637 CFArraySetValueAtIndex(chain
, 0, candidate_leaf
);
638 CFArrayAppendArray(chain
, certs
, CFRangeMake(0, count
));
639 CFArrayRemoveValueAtIndex(chain
, 1 + j
);
640 require_noerr(SecTrustCreateWithCertificates(chain
,
641 policy
, &trust
), out
);
642 SecTrustResultType trust_result
;
643 SecTrustEvaluate(trust
, &trust_result
);
644 CFIndex chain_count
= SecTrustGetCertificateCount(trust
);
645 secdebug("scep", "candidate leaf: %@ forms chain of length %" PRIdCFIndex
, candidate_leaf
, chain_count
);
646 if (chain_count
> 1) {
647 SecCertificateRef leaf
= SecTrustGetCertificateAtIndex(trust
, 0);
648 SecCertificateRef ca_leaf
= SecTrustGetCertificateAtIndex(trust
, chain_count
- 1);
649 if (!_ca_certificate
) {
650 if (ca_fingerprint
) {
651 secdebug("scep", "checking ca %@ against fingerprint %@", ca_leaf
, ca_fingerprint
);
652 uint8_t ca_hash
[CC_SHA1_DIGEST_LENGTH
]; /*max(md5,sha-1)*/
653 ca_cert_data
= SecCertificateCopyData(ca_leaf
);
654 require(ca_cert_data
, out
);
655 size_t ca_data_len
= CFDataGetLength(ca_cert_data
);
656 size_t ca_fingerprint_len
= CFDataGetLength(ca_fingerprint
);
657 const uint8_t *ca_data
= CFDataGetBytePtr(ca_cert_data
);
658 require(ca_data_len
&& ca_data
, out
);
659 require(ca_data_len
<UINT32_MAX
, out
);
660 switch (ca_fingerprint_len
) {
661 case CC_MD5_DIGEST_LENGTH
:
662 CC_MD5(ca_data
, (CC_LONG
)ca_data_len
, ca_hash
);
665 case CC_SHA1_DIGEST_LENGTH
:
666 CCDigest(kCCDigestSHA1
, ca_data
, (CC_LONG
)ca_data_len
, ca_hash
);
672 CFReleaseNull(ca_cert_data
);
673 ca_hash_cfdata
= CFDataCreate(kCFAllocatorDefault
, ca_hash
, ca_fingerprint_len
);
674 require(ca_hash_cfdata
, out
);
675 require(CFEqual(ca_fingerprint
, ca_hash_cfdata
), out
);
676 CFReleaseNull(ca_hash_cfdata
);
678 _ca_certificate
= ca_leaf
;
681 // if ca_certificate is already set, this should be the same
682 require(CFEqual(_ca_certificate
, ca_leaf
), out
);
685 // is leaf allowed to sign and/or encrypt?
686 SecKeyUsage key_usage
= SecCertificateGetKeyUsage(leaf
);
687 bool can_sign
= (key_usage
& kSecKeyUsageDigitalSignature
);
688 bool can_enc
= (key_usage
& kSecKeyUsageKeyEncipherment
);
689 if (!_ra_certificate
&& can_sign
&& can_enc
) {
690 _ra_certificate
= leaf
;
693 else if (!_ra_encryption_certificate
&& !can_sign
&& can_enc
) {
694 _ra_encryption_certificate
= leaf
;
697 else if (!_ra_signing_certificate
&& !can_enc
&& can_sign
) {
698 _ra_signing_certificate
= leaf
;
702 if (trust
) { CFRelease(trust
); trust
= NULL
; }
705 // we should have both a ca certificate and at least one ra certificate now
706 require(_ca_certificate
, out
);
707 require(_ra_certificate
||
708 (_ra_signing_certificate
&& _ra_encryption_certificate
), out
);
710 if (ca_certificate
) {
711 *ca_certificate
= _ca_certificate
;
712 _ca_certificate
= NULL
;
714 if (_ra_signing_certificate
&& _ra_encryption_certificate
) {
715 if (ra_signing_certificate
) {
716 *ra_signing_certificate
= _ra_signing_certificate
;
717 _ra_signing_certificate
= NULL
;
719 if (ra_encryption_certificate
) {
720 *ra_encryption_certificate
= _ra_encryption_certificate
;
721 _ra_encryption_certificate
= NULL
;
723 } else if (_ra_certificate
) {
724 if (ra_signing_certificate
) {
725 *ra_signing_certificate
= _ra_certificate
;
726 _ra_certificate
= NULL
;
730 status
= errSecSuccess
;
733 CFReleaseSafe(_ra_encryption_certificate
);
734 CFReleaseSafe(_ra_signing_certificate
);
735 CFReleaseSafe(_ra_certificate
);
736 CFReleaseSafe(_ca_certificate
);
737 CFReleaseSafe(ca_cert_data
);
738 CFReleaseSafe(ca_hash_cfdata
);
739 CFReleaseSafe(policy
);
740 CFReleaseSafe(trust
);
741 CFReleaseSafe(chain
);
748 @function SecSCEPGetCertInitial
749 @abstract generate a scep cert initial request, to be presented to
750 a scep server, in case the first request timed out
753 // XXX/cs pass CA/RA certificates as a CFTypeRef: one or more certificates for ca_certificate and recipient
755 CF_RETURNS_RETAINED CFDataRef
756 SecSCEPGetCertInitial(SecCertificateRef ca_certificate
, CFArrayRef subject
, CFDictionaryRef parameters
,
757 CFDictionaryRef signed_attrs
, SecIdentityRef signer
, CFTypeRef recipient
)
759 CFMutableDataRef signed_request
= NULL
;
760 CFMutableDictionaryRef simple_attr
= NULL
;
761 CFDataRef pki_message_contents
= NULL
;
762 CFMutableDataRef enveloped_data
= NULL
;
763 CFDataRef msgtype_value_data
= NULL
;
764 CFDataRef msgtype_oid_data
= NULL
;
766 require(signed_attrs
, out
);
767 require(pki_message_contents
= SecGenerateCertificateRequestSubject(ca_certificate
, subject
), out
);
768 require(enveloped_data
= CFDataCreateMutable(kCFAllocatorDefault
, 0), out
);
769 require_noerr(SecCMSCreateEnvelopedData(recipient
, parameters
, pki_message_contents
, enveloped_data
), out
);
771 /* remember transaction id just for reuse */
772 simple_attr
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 3, signed_attrs
);
774 /* message type: GetCertInitial (20) */
775 require(msgtype_oid_data
= scep_oid(messageType
), out
);
776 require(msgtype_value_data
= printable_string_data(sizeof(GetCertInitial
) - 1, GetCertInitial
), out
);
777 CFDictionarySetValue(simple_attr
, msgtype_oid_data
, msgtype_value_data
);
778 CFReleaseNull(msgtype_oid_data
);
779 CFReleaseNull(msgtype_value_data
);
781 /* random sender nonce, to be verified against recipient nonce in reply */
782 generate_sender_nonce(simple_attr
);
783 signed_request
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
784 require_noerr_action(SecCMSCreateSignedData(signer
, enveloped_data
,
785 parameters
, simple_attr
, signed_request
), out
, CFReleaseNull(signed_request
));
788 CFReleaseSafe(simple_attr
);
789 CFReleaseSafe(pki_message_contents
);
790 CFReleaseSafe(enveloped_data
);
791 CFReleaseSafe(msgtype_oid_data
);
792 CFReleaseSafe(msgtype_value_data
);
793 return signed_request
;
798 +----------------+-----------------+---------------------------+
799 | Attribute | Encoding | Comment |
800 +----------------+-----------------+---------------------------+
801 | transactionID | PrintableString | Decimal value as a string |
802 | messageType | PrintableString | Decimal value as a string |
803 | pkiStatus | PrintableString | Decimal value as a string |
804 | failInfo | PrintableString | Decimal value as a string |
805 | senderNonce | OctetString | |
806 | recipientNonce | OctetString | |
807 +----------------+-----------------+---------------------------+
811 The transactionID is an attribute which uniquely identifies a
812 transaction. This attribute is required in all PKI messages.
814 Because the enrollment transaction could be interrupted by various
815 errors, including network connection errors or client reboot, the
816 SCEP client generates a transaction identifier by calculating a hash
817 on the public key value for which the enrollment is requested. This
818 retains the same transaction identifier throughout the enrollment
819 transaction, even if the client has rebooted or timed out, and issues
820 a new enrollment request for the same key pair.
822 It also provides the way for the CA to uniquely identify a
823 transaction in its database. At the requester side, it generates a
824 transaction identifier which is included in PKCSReq. If the CA
825 returns a response of PENDING, the requester will poll by
826 periodically sending out GetCertInitial with the same transaction
827 identifier until either a response other than PENDING is obtained, or
828 the configured maximum time has elapsed.
830 For non-enrollment message (for example GetCert and GetCRL), the
831 transactionID should be a number unique to the client.
836 The messageType attribute specify the type of operation performed by
837 the transaction. This attribute is required in all PKI messages.
838 Currently, the following message types are defined:
840 o PKCSReq (19) -- PKCS#10 [RFC2986] certificate request
842 o CertRep (3) -- Response to certificate or CRL request
844 o GetCertInitial (20) -- Certificate polling in manual enrollment
846 o GetCert (21) -- Retrieve a certificate
848 o GetCRL (22) -- Retrieve a CRL
852 All response message will include transaction status information
853 which is defined as pkiStatus attribute:
855 o SUCCESS (0) -- request granted
857 o FAILURE (2) -- request rejected. This also requires a failInfo
858 attribute to be present, as defined in section 4.2.4.
860 o PENDING (3) -- request pending for manual approval
865 The failInfo attribute will contain one of the following failure
868 o badAlg (0) -- Unrecognized or unsupported algorithm ident
870 o badMessageCheck (1) -- integrity check failed
872 o badRequest (2) -- transaction not permitted or supported
874 o badTime (3) -- Message time field was not sufficiently close to
877 o badCertId (4) -- No certificate could be identified matching the
880 4.2.5. senderNonce and responderNonce
882 The attributes of senderNonce and recipientNonce are the 16 byte
883 random numbers generated for each transaction to prevent the replay
886 When a requester sends a PKI message to the server, a senderNonce is
887 included in the message. After the server processes the request, it
888 will send back the requester senderNonce as the recipientNonce and
889 generates another nonce as the senderNonce in the response message.
890 Because the proposed PKI protocol is a two-way communication
891 protocol, it is clear that the nonce can only be used by the
892 requester to prevent the replay. The server has to employ extra
893 state related information to prevent a replay attack.