2 * Copyright (c) 2008-2010 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 <Security/SecItem.h>
33 #include <Security/SecInternal.h>
34 #include <Security/SecCertificateInternal.h>
35 #include <Security/SecKeyPriv.h>
36 #include <Security/SecInternal.h>
37 #include <libDER/DER_Encode.h>
38 #include <uuid/uuid.h>
39 #include <security_utilities/debugging.h>
50 static CFDataRef
scep_oid(scep_attr_t type
)
52 /* +-------------------+-----------------------------------------------+
53 | Name | ASN.1 Definition |
54 +-------------------+-----------------------------------------------+
55 | id-VeriSign | OBJECT_IDENTIFIER ::= {2 16 US(840) 1 |
56 | | VeriSign(113733)} |
57 | id-pki | OBJECT_IDENTIFIER ::= {id-VeriSign pki(1)} |
58 | id-attributes | OBJECT_IDENTIFIER ::= {id-pki attributes(9)} |
59 | id-messageType | OBJECT_IDENTIFIER ::= {id-attributes |
61 | id-pkiStatus | OBJECT_IDENTIFIER ::= {id-attributes |
63 | id-failInfo | OBJECT_IDENTIFIER ::= {id-attributes |
65 | id-senderNonce | OBJECT_IDENTIFIER ::= {id-attributes |
67 | id-recipientNonce | OBJECT_IDENTIFIER ::= {id-attributes |
68 | | recipientNonce(6)} |
69 | id-transId | OBJECT_IDENTIFIER ::= {id-attributes |
71 | id-extensionReq | OBJECT_IDENTIFIER ::= {id-attributes |
72 | | extensionReq(8)} |
73 +-------------------+-----------------------------------------------+ */
74 uint8_t oid_scep_attrs
[] =
75 { 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0 };
76 /* messageType:2 pkiStatus:3 failInfo:4 senderNonce:5 recipientNonce:6 transId:7 */
77 if ((type
< messageType
) || (type
> transId
))
80 oid_scep_attrs
[sizeof(oid_scep_attrs
) - 1] = type
;
81 return CFDataCreate(kCFAllocatorDefault
, oid_scep_attrs
, sizeof(oid_scep_attrs
));
84 static const char CertRep
[] = "3";
85 static const char PKCSReq
[] = "19";
86 static const char GetCertInitial
[] = "20";
87 static const char GetCert
[] = "21";
88 static const char GetCRL
[] = "22";
89 static const char PKIStatusSUCCESS
[] = "0";
90 static const char PKIStatusFAILURE
[] = "2";
91 static const char PKIStatusPENDING
[] = "3";
94 printable_string_data(size_t length
, const char *bytes
)
96 DERSize der_length_len
= DERLengthOfLength(length
);
97 size_t value_length
= sizeof(SecASN1PrintableString
) + der_length_len
+ length
;
98 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, value_length
);
99 CFDataSetLength(data
, value_length
);
100 uint8_t *ptr
= (uint8_t *)CFDataGetBytePtr(data
);
101 *ptr
++ = SecASN1PrintableString
;
102 DEREncodeLength(length
, ptr
, &der_length_len
);
103 ptr
+= der_length_len
;
104 memcpy(ptr
, bytes
, length
);
105 return (CFDataRef
)data
;
108 #define scep_result(value) printable_string_data(sizeof(value)-1, value)
111 dictionary_array_value_1(CFDictionaryRef attrs
, CFTypeRef attr
)
113 CFTypeRef value
= NULL
;
114 CFArrayRef attr_values
= NULL
;
116 require(attr_values
= (CFArrayRef
)CFDictionaryGetValue(attrs
, attr
), out
);
117 require(CFArrayGetCount(attr_values
) == 1, out
);
118 value
= CFArrayGetValueAtIndex(attr_values
, 0);
123 /* @@@ consider splitting into function returning single value
124 and function creating printable string from c str */
125 static bool scep_attr_has_val(CFDictionaryRef attrs
, scep_attr_t attr
, const char *val
)
128 CFDataRef msgtype_value_data
= printable_string_data(strlen(val
), val
);
129 CFArrayRef msgtype_value_datas
= CFArrayCreate(kCFAllocatorDefault
,
130 (const void **)&msgtype_value_data
, 1, &kCFTypeArrayCallBacks
);
131 CFRelease(msgtype_value_data
);
132 CFDataRef msgtype_oid_data
= scep_oid(attr
);
133 CFArrayRef msgtype_values
= (CFArrayRef
)CFDictionaryGetValue(attrs
, msgtype_oid_data
);
134 CFRelease(msgtype_oid_data
);
135 if (msgtype_values
&& CFEqual(msgtype_value_datas
, msgtype_values
))
137 CFRelease(msgtype_value_datas
);
142 static CFDataRef
hexencode(CFDataRef data
)
144 CFIndex ix
, length
= CFDataGetLength(data
);
145 const uint8_t *bin_data
= CFDataGetBytePtr(data
);
146 uint8_t *hex_data
= calloc(1, 2*length
+ 1);
147 require(length
&& bin_data
&& hex_data
, out
);
149 for (ix
= 0; ix
< length
; ix
++)
150 snprintf((char *)&hex_data
[2*ix
], 3, "%02X", bin_data
[ix
]);
152 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, hex_data
,
153 2*length
, kCFAllocatorMalloc
);
160 static CFDataRef
pubkeyhash(SecKeyRef key
)
162 CFTypeRef key_type
= NULL
;
163 CFDictionaryRef pubkey_attrs
= NULL
;
164 CFDataRef hash_pubkey_data
= NULL
, pubkey_data
= NULL
;
165 uint8_t pubkey_hash
[CC_SHA1_DIGEST_LENGTH
];
167 require(pubkey_attrs
= SecKeyCopyAttributeDictionary(key
), out
);
168 require( (key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyClass
)) &&
169 CFEqual(key_type
, kSecAttrKeyClassPublic
), out
);
170 require(pubkey_data
= CFDictionaryGetValue(pubkey_attrs
, kSecValueData
), out
);
171 require((unsigned long)CFDataGetLength(pubkey_data
)<=UINT32_MAX
, out
); /* Correct as long as CFIndex is long */
172 CC_SHA1(CFDataGetBytePtr(pubkey_data
), (CC_LONG
)CFDataGetLength(pubkey_data
), pubkey_hash
);
173 hash_pubkey_data
= CFDataCreate(kCFAllocatorDefault
, pubkey_hash
, sizeof(pubkey_hash
));
175 CFReleaseSafe(pubkey_attrs
);
176 return hash_pubkey_data
;
179 static void generate_sender_nonce(CFMutableDictionaryRef dict
)
181 /* random sender nonce, to be verified against recipient nonce in reply */
182 CFDataRef senderNonce_oid_data
= scep_oid(senderNonce
);
183 uint8_t senderNonce_value
[18] = { 4, 16, };
184 SecRandomCopyBytes(kSecRandomDefault
, sizeof(senderNonce_value
) - 2, senderNonce_value
+ 2);
185 CFDataRef senderNonce_value_data
= CFDataCreate(kCFAllocatorDefault
,
186 senderNonce_value
, sizeof(senderNonce_value
));
187 if (senderNonce_oid_data
&& senderNonce_value_data
)
188 CFDictionarySetValue(dict
, senderNonce_oid_data
, senderNonce_value_data
);
189 CFReleaseNull(senderNonce_oid_data
);
190 CFReleaseNull(senderNonce_value_data
);
193 SecIdentityRef
SecSCEPCreateTemporaryIdentity(SecKeyRef publicKey
, SecKeyRef privateKey
)
195 int key_usage
= kSecKeyUsageDigitalSignature
| kSecKeyUsageKeyEncipherment
;
196 CFDictionaryRef self_signed_parameters
= NULL
;
197 CFNumberRef key_usage_num
= NULL
;
198 SecCertificateRef self_signed_certificate
= NULL
;
199 SecIdentityRef self_signed_identity
= NULL
;
200 CFStringRef cn_uuid
= NULL
;
201 CFArrayRef cn_dn
= NULL
, cn_dns
= NULL
, unique_rdns
= NULL
;
203 key_usage_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_usage
);
204 require(key_usage_num
, out
);
206 const void *key
[] = { kSecCertificateKeyUsage
};
207 const void *val
[] = { key_usage_num
};
208 self_signed_parameters
= CFDictionaryCreate(kCFAllocatorDefault
,
209 key
, val
, sizeof(key
)/sizeof(*key
),
210 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
211 require(self_signed_parameters
, out
);
213 char uuid_string
[37] = {};
215 uuid_generate_random(uuid
);
216 uuid_unparse(uuid
, uuid_string
);
217 cn_uuid
= CFStringCreateWithCString(kCFAllocatorDefault
, uuid_string
, kCFStringEncodingASCII
);
218 require(cn_uuid
, out
);
219 const void * cn
[] = { kSecOidCommonName
, cn_uuid
};
220 cn_dn
= CFArrayCreate(kCFAllocatorDefault
, cn
, 2, NULL
);
222 cn_dns
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&cn_dn
, 1, NULL
);
223 require(cn_dns
, out
);
224 unique_rdns
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&cn_dns
, 1, NULL
);
225 require(unique_rdns
, out
);
227 self_signed_certificate
= SecGenerateSelfSignedCertificate(unique_rdns
, self_signed_parameters
, publicKey
, privateKey
);
228 require(self_signed_certificate
, out
);
229 self_signed_identity
= SecIdentityCreate(kCFAllocatorDefault
, self_signed_certificate
, privateKey
);
232 CFReleaseSafe(key_usage_num
);
233 CFReleaseSafe(self_signed_parameters
);
234 CFReleaseSafe(self_signed_certificate
);
235 CFReleaseSafe(unique_rdns
);
236 CFReleaseSafe(cn_dns
);
237 CFReleaseSafe(cn_dn
);
238 CFReleaseSafe(cn_uuid
);
240 return self_signed_identity
;
244 SecSCEPGenerateCertificateRequest(CFArrayRef subject
, CFDictionaryRef parameters
,
245 SecKeyRef publicKey
, SecKeyRef privateKey
,
246 SecIdentityRef signer
, CFTypeRef recipients
)
248 CFDataRef csr
= NULL
;
249 CFMutableDataRef enveloped_data
= NULL
;
250 CFMutableDictionaryRef simple_attr
= NULL
;
251 SecIdentityRef self_signed_identity
= NULL
;
252 CFMutableDataRef signed_request
= NULL
;
253 SecCertificateRef recipient
= NULL
;
255 if (CFGetTypeID(recipients
) == SecCertificateGetTypeID()) {
256 recipient
= (SecCertificateRef
)recipients
;
257 } else if (CFGetTypeID(recipients
) == CFArrayGetTypeID()) {
258 CFIndex recipient_count
= CFArrayGetCount(recipients
);
259 if (recipient_count
> 1) {
260 /* get the encryption cert */
261 recipient
= (SecCertificateRef
)CFArrayGetValueAtIndex(recipients
, 0);
262 } else if (recipient_count
== 1) {
263 /* if there is at least one we'll assume it's sign+encrypt */
264 recipient
= (SecCertificateRef
)CFArrayGetValueAtIndex(recipients
, 0);
267 require(recipient
, out
);
269 require(csr
= SecGenerateCertificateRequest(subject
, parameters
, publicKey
, privateKey
), out
);
270 require(enveloped_data
= CFDataCreateMutable(kCFAllocatorDefault
, 0), out
);
271 require_noerr(SecCMSCreateEnvelopedData(recipient
, parameters
, csr
, enveloped_data
), out
);
274 simple_attr
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 3,
275 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
277 /* generate a transaction id: hex encoded pubkey hash */
278 CFDataRef public_key_hash
= pubkeyhash(publicKey
);
279 CFDataRef public_key_hash_hex
= hexencode(public_key_hash
);
280 CFReleaseSafe(public_key_hash
);
281 CFDataRef transid_oid_data
= scep_oid(transId
);
282 CFDataRef transid_data
= printable_string_data(CFDataGetLength(public_key_hash_hex
),
283 (const char *)CFDataGetBytePtr(public_key_hash_hex
));
284 CFReleaseSafe(public_key_hash_hex
);
286 CFDictionarySetValue(simple_attr
, transid_oid_data
, transid_data
);
287 CFReleaseNull(transid_oid_data
);
288 CFReleaseNull(transid_data
);
290 /* message type: PKCSReq (19) */
291 CFDataRef msgtype_value_data
= NULL
;
292 CFDataRef msgtype_oid_data
= NULL
;
293 require(msgtype_oid_data
= scep_oid(messageType
), out
);
294 require(msgtype_value_data
= printable_string_data(strlen(PKCSReq
), PKCSReq
), out
);
296 CFDictionarySetValue(simple_attr
, msgtype_oid_data
, msgtype_value_data
);
297 CFReleaseNull(msgtype_oid_data
);
298 CFReleaseNull(msgtype_value_data
);
300 /* random sender nonce, to be verified against recipient nonce in reply */
301 generate_sender_nonce(simple_attr
);
303 /* XXX/cs remove auto-generation once managedconfig is no longer using this */
305 self_signed_identity
= signer
;
306 CFRetain(self_signed_identity
);
308 self_signed_identity
= SecSCEPCreateTemporaryIdentity(publicKey
, privateKey
);
310 /* Add our temporary cert to the keychain for CMS decryption of
311 the reply. If we happened to have picked an existing UUID
312 we fail. We should pick a different UUID and try again. */
313 require(self_signed_identity
, out
);
314 CFDictionaryRef identity_add
= CFDictionaryCreate(NULL
,
315 &kSecValueRef
, (const void **)&self_signed_identity
, 1, NULL
, NULL
);
316 require_noerr_action(SecItemAdd(identity_add
, NULL
), out
,
317 CFReleaseSafe(identity_add
));
318 CFReleaseSafe(identity_add
);
320 require(self_signed_identity
, out
);
322 signed_request
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
323 require_noerr_action(SecCMSCreateSignedData(self_signed_identity
, enveloped_data
,
324 parameters
, simple_attr
, signed_request
), out
, CFReleaseNull(signed_request
));
329 CFReleaseSafe(simple_attr
);
330 CFReleaseSafe(self_signed_identity
);
331 CFReleaseSafe(enveloped_data
);
333 return signed_request
;
338 SecSCEPCertifyRequest(CFDataRef request
, SecIdentityRef ca_identity
, CFDataRef serialno
, bool pend_request
)
340 CFDictionaryRef simple_attr
= NULL
;
341 SecCertificateRef ca_certificate
= NULL
;
342 SecKeyRef ca_public_key
= NULL
;
343 SecCertificateRef cert
= NULL
;
344 SecPolicyRef policy
= NULL
;
345 CFDataRef cert_pkcs7
= NULL
;
346 CFMutableDataRef cert_msg
= NULL
;
347 CFMutableDataRef signed_reply
= NULL
;
348 SecTrustRef trust
= NULL
;
349 CFDataRef signed_content
= NULL
;
350 CFDictionaryRef signed_attributes
= NULL
;
351 SecCertificateRef signer_cert
= NULL
;
352 CFDataRef transid_oid_data
= NULL
, senderNonce_oid_data
= NULL
, transid_value
= NULL
;
353 CFDataRef subject
= NULL
, extensions
= NULL
, senderNonce_value
= NULL
;
354 CFStringRef challenge
= NULL
;
355 SecKeyRef tbsPublicKey
= NULL
;
356 CFMutableDataRef encrypted_content
= NULL
;
357 SecCertificateRef recipient
= NULL
;
358 CFDictionaryRef parameters
= NULL
;
360 require_noerr(SecIdentityCopyCertificate(ca_identity
, &ca_certificate
), out
);
361 ca_public_key
= SecCertificateCopyPublicKey(ca_certificate
); /*@@@*/
363 /* unwrap outer layer: */
364 policy
= SecPolicyCreateBasicX509();
366 require_noerr(SecCMSVerifyCopyDataAndAttributes(request
, NULL
,
367 policy
, &trust
, &signed_content
, &signed_attributes
), out
);
368 /* remember signer: is signer certified by us, then re-certify, no challenge needed */
369 SecTrustResultType result
;
370 require_noerr(SecTrustEvaluate(trust
, &result
), out
);
371 require (signer_cert
= SecTrustGetCertificateAtIndex(trust
, 0), out
);
372 bool recertify
= !SecCertificateIsSignedBy(signer_cert
, ca_public_key
);
374 /* msgType should be certreq msg */
375 require(scep_attr_has_val(signed_attributes
, messageType
, PKCSReq
), out
);
377 /* remember transaction id just for reuse */
378 require(transid_oid_data
= scep_oid(transId
), out
);
379 require(transid_value
=
380 dictionary_array_value_1(signed_attributes
, transid_oid_data
), out
);
382 /* senderNonce becomes recipientNonce */
383 require(senderNonce_oid_data
= scep_oid(senderNonce
), out
);
384 require(senderNonce_value
=
385 dictionary_array_value_1(signed_attributes
, senderNonce_oid_data
), out
);
387 /* decrypt the request */
388 encrypted_content
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
389 require_noerr(SecCMSDecryptEnvelopedData(signed_content
, encrypted_content
, &recipient
), out
);
390 require(recipient
&& CFEqual(ca_certificate
, recipient
), out
);
393 require(SecVerifyCertificateRequest(encrypted_content
, &tbsPublicKey
, &challenge
, &subject
, &extensions
), out
);
394 CFReleaseNull(encrypted_content
);
397 // alternatively send a pending message
398 // pkistatus {{id-attributes pkiStatus(3)} "FAILURE"}
399 // failInfo {{id-attributes failInfo(4)} "the reason to reject"}
402 /* verify challenge - this would need to be a callout that can determine
403 the challenge appropriate for the subject */
405 require( challenge
&& (CFStringGetTypeID() == CFGetTypeID(challenge
)) &&
406 CFEqual(CFSTR("magic"), challenge
), out
);
408 require(cert_msg
= CFDataCreateMutable(kCFAllocatorDefault
, 0), out
);
412 cert
= SecIdentitySignCertificate(ca_identity
, serialno
,
413 tbsPublicKey
, subject
, extensions
);
415 /* degenerate cms with cert */
416 require (cert_pkcs7
= SecCMSCreateCertificatesOnlyMessage(cert
), out
);
419 /* envelope for client */
420 require_noerr(SecCMSCreateEnvelopedData(signer_cert
, NULL
, cert_pkcs7
, cert_msg
), out
);
421 CFReleaseNull(cert_pkcs7
);
424 CFDataRef pki_status_oid
= scep_oid(pkiStatus
);
425 CFDataRef pki_status_value
= pend_request
? scep_result(PKIStatusPENDING
) : scep_result(PKIStatusSUCCESS
);
426 CFDataRef message_type_oid
= scep_oid(messageType
), message_type_value
= scep_result(CertRep
);
427 const void *oid
[] = { transid_oid_data
, pki_status_oid
, message_type_oid
};
428 const void *value
[] = { transid_value
, pki_status_value
, message_type_value
};
429 simple_attr
= CFDictionaryCreate(kCFAllocatorDefault
, oid
, value
, sizeof(oid
)/sizeof(*oid
),
430 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
431 CFReleaseSafe(pki_status_oid
); CFReleaseSafe(pki_status_value
);
432 CFReleaseSafe(message_type_oid
); CFReleaseSafe(message_type_value
);
434 /* sign with ra/ca cert and add attributes */
435 signed_reply
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
436 const void *signing_params
[] = { kSecCMSCertChainMode
};
437 const void *signing_params_vals
[] = { kSecCMSCertChainModeNone
};
438 parameters
= CFDictionaryCreate(kCFAllocatorDefault
, signing_params
, signing_params_vals
, sizeof(signing_params
)/sizeof(*signing_params
), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
439 require_noerr_action(SecCMSCreateSignedData(ca_identity
, cert_msg
, parameters
, simple_attr
, signed_reply
), out
, CFReleaseNull(signed_reply
));
442 CFReleaseSafe(ca_certificate
);
443 CFReleaseSafe(ca_public_key
);
445 CFReleaseSafe(cert_pkcs7
);
446 CFReleaseSafe(cert_msg
);
447 CFReleaseSafe(trust
);
448 CFReleaseSafe(policy
);
449 CFReleaseSafe(signed_content
);
450 CFReleaseSafe(signed_attributes
);
451 CFReleaseSafe(transid_oid_data
);
452 CFReleaseSafe(senderNonce_oid_data
);
453 CFReleaseSafe(subject
);
454 CFReleaseSafe(extensions
);
455 CFReleaseSafe(challenge
);
456 CFReleaseSafe(tbsPublicKey
);
457 CFReleaseSafe(encrypted_content
);
458 CFReleaseSafe(simple_attr
);
459 CFReleaseSafe(recipient
);
460 CFReleaseSafe(parameters
);
466 copy_signed_attr_printable_string_value(CFDictionaryRef signed_attributes
, scep_attr_t attr
)
468 CFStringRef printable_string
= NULL
;
469 CFDataRef key_oid
= NULL
;
471 key_oid
= scep_oid(attr
);
472 require(key_oid
, out
);
474 CFArrayRef values
= (CFArrayRef
)CFDictionaryGetValue(signed_attributes
, key_oid
);
475 require_quiet(values
&& (CFGetTypeID(values
) == CFArrayGetTypeID())
476 && (CFArrayGetCount(values
) == 1), out
);
477 CFDataRef value
= CFArrayGetValueAtIndex(values
, 0);
478 const uint8_t *bytes
= CFDataGetBytePtr(value
);
479 size_t length
= CFDataGetLength(value
);
480 require(length
>= 2, out
);
481 require(bytes
[0] == 0x13, out
);
482 /* no scep responses defined that are longer */
483 require(!(bytes
[1] & 0x80) && (bytes
[1] == length
-2), out
);
484 printable_string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
485 bytes
+ 2, length
- 2, kCFStringEncodingASCII
, false);
487 CFReleaseSafe(key_oid
);
489 return printable_string
;
493 SecSCEPVerifyReply(CFDataRef request
, CFDataRef reply
, CFTypeRef ca_certificates
,
494 CFErrorRef
*server_error
)
496 SecKeyRef ca_public_key
= NULL
;
497 SecCertificateRef cert
= NULL
;
498 SecPolicyRef policy
= NULL
;
499 CFDataRef cert_msg
= NULL
;
500 CFMutableDataRef enc_cert_msg
= NULL
;
501 SecTrustRef trust
= NULL
;
502 CFDataRef signed_content
= NULL
;
503 CFDictionaryRef signed_attributes
= NULL
;
504 CFDictionaryRef attributes
= NULL
;
505 SecCertificateRef signer_cert
= NULL
;
507 CFMutableDataRef encrypted_content
= NULL
;
508 SecCertificateRef recipient
= NULL
;
509 CFArrayRef certificates
= NULL
;
511 SecCertificateRef reply_signer
= NULL
;
513 CFStringRef msg_type
= NULL
;
514 CFStringRef pki_status
= NULL
;
516 if (CFGetTypeID(ca_certificates
) == SecCertificateGetTypeID()) {
517 reply_signer
= (SecCertificateRef
)ca_certificates
;
518 } else if (CFGetTypeID(ca_certificates
) == CFArrayGetTypeID()) {
519 CFIndex reply_signer_count
= CFArrayGetCount(ca_certificates
);
520 if (reply_signer_count
> 1) {
521 /* get the signer cert */
522 reply_signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(ca_certificates
, 1);
523 } else if (reply_signer_count
== 1) {
524 /* if there is at least one we'll assume it's sign+encrypt */
525 reply_signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(ca_certificates
, 0);
528 require(reply_signer
, out
);
530 /* unwrap outer layer */
531 policy
= SecPolicyCreateBasicX509();
532 CFArrayRef additional_certificates
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&reply_signer
, 1, &kCFTypeArrayCallBacks
);
533 require_noerr(SecCMSVerifySignedData(reply
, NULL
,
534 policy
, &trust
, additional_certificates
, &signed_content
, &attributes
), out
);
535 CFReleaseSafe(additional_certificates
);
537 signed_attributes
= CFDictionaryGetValue(attributes
, kSecCMSSignedAttributes
);
539 /* response should be signed by ra */
540 SecTrustResultType result
;
541 require_noerr(SecTrustEvaluate(trust
, &result
), out
);
542 require(signer_cert
= SecTrustGetCertificateAtIndex(trust
, 0), out
);
543 require(CFEqual(reply_signer
, signer_cert
), out
);
545 /* msgType should be certreq msg */
546 require(signed_attributes
, out
);
547 msg_type
= copy_signed_attr_printable_string_value(signed_attributes
, messageType
);
548 pki_status
= copy_signed_attr_printable_string_value(signed_attributes
, pkiStatus
);
550 if (msg_type
|| pki_status
) {
551 require(msg_type
&& CFEqual(msg_type
, CFSTR("3")), out
);
553 require(pki_status
, out
);
554 if (CFEqual(pki_status
, CFSTR("2"))) {
555 goto out
; // FAILURE, the end (return NULL)
556 } else if (CFEqual(pki_status
, CFSTR("3"))) {
557 CFDataRef transid_oid_data
= NULL
, transid_value
= NULL
;
558 require(transid_oid_data
= scep_oid(transId
), out
);
559 require(transid_value
= dictionary_array_value_1(signed_attributes
, transid_oid_data
), out
);
560 CFDictionaryRef err_dict
= CFDictionaryCreate(kCFAllocatorDefault
, (const void **)&transid_oid_data
, (const void **)&transid_value
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
561 *server_error
= CFErrorCreate(kCFAllocatorDefault
,
562 CFSTR("PENDING"), 3, err_dict
);
563 CFReleaseSafe(err_dict
);
564 CFReleaseSafe(transid_oid_data
);
567 require(CFEqual(pki_status
, CFSTR("0")), out
);
570 // can we decode the request?
571 encrypted_content
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
572 require_noerr(SecCMSDecryptEnvelopedData(signed_content
, encrypted_content
, &recipient
), out
);
573 require(recipient
, out
);
574 // verify recipient belongs with our private key
577 require(certificates
= SecCMSCertificatesOnlyMessageCopyCertificates(encrypted_content
), out
);
579 // recipient is either our temporary self-signed cert or the old cert we just used
580 // to recertify. if we have new certificates and have stored them successfully we
581 // can now get rid of the cert.
583 This should move outside of thise function when we force a signer
585 CFDictionaryRef cert_delete
= CFDictionaryCreate(NULL
,
586 &kSecValueRef
, (const void **)&recipient
, 1, NULL
, NULL
);
587 require_noerr_action(SecItemDelete(cert_delete
), out
,
588 CFReleaseSafe(cert_delete
));
589 CFReleaseSafe(cert_delete
);
592 CFReleaseSafe(ca_public_key
);
594 CFReleaseSafe(cert_msg
);
595 CFReleaseSafe(enc_cert_msg
);
596 CFReleaseSafe(trust
);
597 CFReleaseSafe(policy
);
598 CFReleaseSafe(signed_content
);
599 CFReleaseSafe(encrypted_content
);
600 CFReleaseSafe(recipient
);
601 CFReleaseSafe(msg_type
);
602 CFReleaseSafe(pki_status
);
603 CFReleaseSafe(attributes
);
608 OSStatus
SecSCEPValidateCACertMessage(CFArrayRef certs
,
609 CFDataRef ca_fingerprint
,
610 SecCertificateRef
*ca_certificate
,
611 SecCertificateRef
*ra_signing_certificate
,
612 SecCertificateRef
*ra_encryption_certificate
)
614 OSStatus status
= errSecParam
;
615 SecCertificateRef _ca_certificate
= NULL
, _ra_signing_certificate
= NULL
,
616 _ra_encryption_certificate
= NULL
, _ra_certificate
= NULL
;
618 CFIndex j
, count
= CFArrayGetCount(certs
);
619 CFMutableArrayRef chain
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
620 SecPolicyRef policy
= SecPolicyCreateBasicX509();
621 SecTrustRef trust
= NULL
;
623 for (j
=0; j
<count
; j
++) {
624 const void *candidate_leaf
= CFArrayGetValueAtIndex(certs
, j
);
625 CFArrayRemoveAllValues(chain
);
626 CFArraySetValueAtIndex(chain
, 0, candidate_leaf
);
627 CFArrayAppendArray(chain
, certs
, CFRangeMake(0, count
));
628 CFArrayRemoveValueAtIndex(chain
, 1 + j
);
629 require_noerr(SecTrustCreateWithCertificates(chain
,
630 policy
, &trust
), out
);
631 SecTrustResultType trust_result
;
632 SecTrustEvaluate(trust
, &trust_result
);
633 CFIndex chain_count
= SecTrustGetCertificateCount(trust
);
634 secdebug("scep", "candidate leaf: %@ forms chain of length %d", candidate_leaf
, chain_count
);
635 if (chain_count
> 1) {
636 SecCertificateRef leaf
= SecTrustGetCertificateAtIndex(trust
, 0);
637 SecCertificateRef ca_leaf
= SecTrustGetCertificateAtIndex(trust
, chain_count
- 1);
638 if (!_ca_certificate
) {
639 if (ca_fingerprint
) {
640 secdebug("scep", "checking ca %@ against fingerprint %@", ca_leaf
, ca_fingerprint
);
641 uint8_t ca_hash
[CC_SHA1_DIGEST_LENGTH
]; /*max(md5,sha-1)*/
642 CFDataRef ca_cert_data
= SecCertificateCopyData(ca_leaf
);
643 require(ca_cert_data
, out
);
644 size_t ca_data_len
= CFDataGetLength(ca_cert_data
);
645 size_t ca_fingerprint_len
= CFDataGetLength(ca_fingerprint
);
646 const uint8_t *ca_data
= CFDataGetBytePtr(ca_cert_data
);
647 require(ca_data_len
&& ca_data
, out
);
648 require(ca_data_len
<UINT32_MAX
, out
);
649 switch (ca_fingerprint_len
) {
650 case CC_MD5_DIGEST_LENGTH
:
651 CC_MD5(ca_data
, (CC_LONG
)ca_data_len
, ca_hash
);
654 case CC_SHA1_DIGEST_LENGTH
:
655 CC_SHA1(ca_data
, (CC_LONG
)ca_data_len
, ca_hash
);
661 CFRelease(ca_cert_data
);
662 CFDataRef ca_hash_cfdata
= CFDataCreate(kCFAllocatorDefault
,
663 ca_hash
, ca_fingerprint_len
);
664 require(ca_hash_cfdata
, out
);
665 require_action(CFEqual(ca_fingerprint
, ca_hash_cfdata
),
666 out
, CFRelease(ca_hash_cfdata
));
667 CFRelease(ca_hash_cfdata
);
669 _ca_certificate
= ca_leaf
;
672 // if ca_certificate is already set, this should be the same
673 require(CFEqual(_ca_certificate
, ca_leaf
), out
);
676 // is leaf allowed to sign and/or encrypt?
677 SecKeyUsage key_usage
= SecCertificateGetKeyUsage(leaf
);
678 bool can_sign
= (key_usage
& kSecKeyUsageDigitalSignature
);
679 bool can_enc
= (key_usage
& kSecKeyUsageKeyEncipherment
);
680 if (!_ra_certificate
&& can_sign
&& can_enc
) {
681 _ra_certificate
= leaf
;
684 else if (!_ra_encryption_certificate
&& !can_sign
&& can_enc
) {
685 _ra_encryption_certificate
= leaf
;
688 else if (!_ra_signing_certificate
&& !can_enc
&& can_sign
) {
689 _ra_signing_certificate
= leaf
;
693 if (trust
) { CFRelease(trust
); trust
= NULL
; }
696 // we should have both a ca certificate and at least one ra certificate now
697 require(_ca_certificate
, out
);
698 require(_ra_certificate
||
699 (_ra_signing_certificate
&& _ra_encryption_certificate
), out
);
701 if (ca_certificate
) {
702 *ca_certificate
= _ca_certificate
;
703 _ca_certificate
= NULL
;
705 if (_ra_signing_certificate
&& _ra_encryption_certificate
) {
706 if (ra_signing_certificate
) {
707 *ra_signing_certificate
= _ra_signing_certificate
;
708 _ra_signing_certificate
= NULL
;
710 if (ra_encryption_certificate
) {
711 *ra_encryption_certificate
= _ra_encryption_certificate
;
712 _ra_encryption_certificate
= NULL
;
714 } else if (_ra_certificate
) {
715 if (ra_signing_certificate
) {
716 *ra_signing_certificate
= _ra_certificate
;
717 _ra_certificate
= NULL
;
724 if (_ra_encryption_certificate
) CFRelease(_ra_encryption_certificate
);
725 if (_ra_signing_certificate
) CFRelease(_ra_signing_certificate
);
726 if (_ca_certificate
) CFRelease(_ca_certificate
);
727 if (policy
) CFRelease(policy
);
728 if (trust
) CFRelease(trust
);
729 if (chain
) CFRelease(chain
);
736 @function SecSCEPGetCertInitial
737 @abstract generate a scep cert initial request, to be presented to
738 a scep server, in case the first request timed out
741 // XXX/cs pass CA/RA certificates as a CFTypeRef: one or more certificates for ca_certificate and recipient
744 SecSCEPGetCertInitial(SecCertificateRef ca_certificate
, CFArrayRef subject
, CFDictionaryRef parameters
,
745 CFDictionaryRef signed_attrs
, SecIdentityRef signer
, CFTypeRef recipient
)
747 CFMutableDataRef signed_request
= NULL
;
748 CFMutableDictionaryRef simple_attr
= NULL
;
749 CFDataRef pki_message_contents
= NULL
;
750 CFMutableDataRef enveloped_data
= NULL
;
752 require(signed_attrs
, out
);
753 require(pki_message_contents
= SecGenerateCertificateRequestSubject(ca_certificate
, subject
), out
);
754 require(enveloped_data
= CFDataCreateMutable(kCFAllocatorDefault
, 0), out
);
755 require_noerr(SecCMSCreateEnvelopedData(recipient
, parameters
, pki_message_contents
, enveloped_data
), out
);
757 /* remember transaction id just for reuse */
758 simple_attr
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 3, signed_attrs
);
760 /* message type: GetCertInitial (20) */
761 CFDataRef msgtype_value_data
= NULL
;
762 CFDataRef msgtype_oid_data
= NULL
;
763 require(msgtype_oid_data
= scep_oid(messageType
), out
);
764 require(msgtype_value_data
= printable_string_data(sizeof(GetCertInitial
) - 1, GetCertInitial
), out
);
765 CFDictionarySetValue(simple_attr
, msgtype_oid_data
, msgtype_value_data
);
766 CFReleaseNull(msgtype_oid_data
);
767 CFReleaseNull(msgtype_value_data
);
769 /* random sender nonce, to be verified against recipient nonce in reply */
770 generate_sender_nonce(simple_attr
);
771 signed_request
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
772 require_noerr_action(SecCMSCreateSignedData(signer
, enveloped_data
,
773 parameters
, simple_attr
, signed_request
), out
, CFReleaseNull(signed_request
));
776 CFReleaseSafe(simple_attr
);
777 CFReleaseSafe(pki_message_contents
);
778 CFReleaseSafe(enveloped_data
);
779 return signed_request
;
784 +----------------+-----------------+---------------------------+
785 | Attribute | Encoding | Comment |
786 +----------------+-----------------+---------------------------+
787 | transactionID | PrintableString | Decimal value as a string |
788 | messageType | PrintableString | Decimal value as a string |
789 | pkiStatus | PrintableString | Decimal value as a string |
790 | failInfo | PrintableString | Decimal value as a string |
791 | senderNonce | OctetString | |
792 | recipientNonce | OctetString | |
793 +----------------+-----------------+---------------------------+
797 The transactionID is an attribute which uniquely identifies a
798 transaction. This attribute is required in all PKI messages.
800 Because the enrollment transaction could be interrupted by various
801 errors, including network connection errors or client reboot, the
802 SCEP client generates a transaction identifier by calculating a hash
803 on the public key value for which the enrollment is requested. This
804 retains the same transaction identifier throughout the enrollment
805 transaction, even if the client has rebooted or timed out, and issues
806 a new enrollment request for the same key pair.
808 It also provides the way for the CA to uniquely identify a
809 transaction in its database. At the requester side, it generates a
810 transaction identifier which is included in PKCSReq. If the CA
811 returns a response of PENDING, the requester will poll by
812 periodically sending out GetCertInitial with the same transaction
813 identifier until either a response other than PENDING is obtained, or
814 the configured maximum time has elapsed.
816 For non-enrollment message (for example GetCert and GetCRL), the
817 transactionID should be a number unique to the client.
822 The messageType attribute specify the type of operation performed by
823 the transaction. This attribute is required in all PKI messages.
824 Currently, the following message types are defined:
826 o PKCSReq (19) -- PKCS#10 [RFC2986] certificate request
828 o CertRep (3) -- Response to certificate or CRL request
830 o GetCertInitial (20) -- Certificate polling in manual enrollment
832 o GetCert (21) -- Retrieve a certificate
834 o GetCRL (22) -- Retrieve a CRL
838 All response message will include transaction status information
839 which is defined as pkiStatus attribute:
841 o SUCCESS (0) -- request granted
843 o FAILURE (2) -- request rejected. This also requires a failInfo
844 attribute to be present, as defined in section 4.2.4.
846 o PENDING (3) -- request pending for manual approval
851 The failInfo attribute will contain one of the following failure
854 o badAlg (0) -- Unrecognized or unsupported algorithm ident
856 o badMessageCheck (1) -- integrity check failed
858 o badRequest (2) -- transaction not permitted or supported
860 o badTime (3) -- Message time field was not sufficiently close to
863 o badCertId (4) -- No certificate could be identified matching the
866 4.2.5. senderNonce and responderNonce
868 The attributes of senderNonce and recipientNonce are the 16 byte
869 random numbers generated for each transaction to prevent the replay
872 When a requester sends a PKI message to the server, a senderNonce is
873 included in the message. After the server processes the request, it
874 will send back the requester senderNonce as the recipientNonce and
875 generates another nonce as the senderNonce in the response message.
876 Because the proposed PKI protocol is a two-way communication
877 protocol, it is clear that the nonce can only be used by the
878 requester to prevent the replay. The server has to employ extra
879 state related information to prevent a replay attack.