]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | * | |
23 | * SecExternalRep.cpp - private class representing an external representation of | |
24 | * a SecKeychainItemRef, used by SecImportExport.h | |
25 | */ | |
26 | ||
27 | #include "SecExternalRep.h" | |
28 | #include "SecImportExportPem.h" | |
29 | #include "SecImportExportAgg.h" | |
30 | #include "SecImportExportUtils.h" | |
31 | #include "SecImportExportPkcs8.h" | |
32 | #include "SecImportExportCrypto.h" | |
33 | #include "SecImportExportOpenSSH.h" | |
34 | #include <security_utilities/errors.h> | |
427c49bc | 35 | #include <Security/SecBase.h> |
b1ab9ed8 A |
36 | #include <Security/SecKeyPriv.h> |
37 | #include <Security/SecCertificate.h> | |
38 | #include <Security/cssmapi.h> | |
b1ab9ed8 A |
39 | |
40 | using namespace Security; | |
41 | using namespace KeychainCore; | |
42 | ||
43 | ||
44 | #pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() --- | |
45 | ||
46 | namespace SecExport { | |
47 | ||
48 | class Key : public SecExportRep | |
49 | { | |
50 | friend class SecExportRep; | |
51 | protected: | |
52 | Key( | |
53 | CFTypeRef kcItemRef); | |
54 | ~Key(); | |
55 | OSStatus exportRep( | |
56 | SecExternalFormat format, | |
57 | SecItemImportExportFlags flags, | |
58 | const SecKeyImportExportParameters *keyParams, // optional | |
59 | CFMutableDataRef outData, // data appended here | |
60 | const char **pemHeader); // e.g., "RSA PUBLIC KEY" | |
61 | ||
62 | private: | |
63 | CSSM_ALGORITHMS mKeyAlg; | |
64 | const CSSM_KEY *mCssmKey; | |
65 | }; | |
66 | ||
67 | class Cert : public SecExportRep | |
68 | { | |
69 | friend class SecExportRep; | |
70 | protected: | |
71 | Cert( | |
72 | CFTypeRef kcItemRef); | |
73 | ~Cert(); | |
74 | OSStatus exportRep( | |
75 | SecExternalFormat format, | |
76 | SecItemImportExportFlags flags, | |
77 | const SecKeyImportExportParameters *keyParams, // optional | |
78 | CFMutableDataRef outData, // data appended here | |
79 | const char **pemHeader); // e.g., "CERTIFICATE" | |
80 | }; | |
81 | ||
82 | } /* namespace SecExport */ | |
83 | ||
84 | #pragma mark --- SecExportRep: Representation of an internal object on export --- | |
85 | ||
86 | SecExportRep::SecExportRep( | |
87 | CFTypeRef kcItemRef) : | |
88 | mKcItem((SecKeychainItemRef)kcItemRef), | |
89 | mPemParamLines(NULL) | |
90 | { | |
91 | CFRetain(mKcItem); | |
92 | } | |
93 | ||
94 | SecExportRep::~SecExportRep() | |
95 | { | |
96 | if(mKcItem) { | |
97 | CFRelease(mKcItem); | |
98 | } | |
99 | if(mPemParamLines) { | |
100 | CFRelease(mPemParamLines); | |
101 | } | |
102 | } | |
103 | ||
104 | SecExportRep::SecExportRep() { | |
105 | MacOSError::throwMe(errSecInvalidItemRef); | |
106 | } | |
107 | ||
108 | /* must be implemented by subclass */ | |
109 | OSStatus SecExportRep::exportRep( | |
110 | SecExternalFormat format, | |
111 | SecItemImportExportFlags flags, | |
112 | const SecKeyImportExportParameters *keyParams, // optional | |
113 | CFMutableDataRef outData, // data appended here | |
114 | const char **pemHeader) // e.g., "X509 CERTIFICATE" | |
115 | { | |
116 | MacOSError::throwMe(errSecInvalidItemRef); | |
117 | } | |
118 | ||
119 | /* | |
120 | * Sole public means of obtaining a SecExportRep object. In fact only instances | |
121 | * of subclasses are vended but caller does not know that. | |
122 | * | |
123 | * Gleans SecExternalItemType from incoming type, throws MacOSError if | |
124 | * incoming type is bogus. | |
125 | * | |
126 | * Vended object holds a reference to kcItem for its lifetime. | |
127 | */ | |
128 | SecExportRep *SecExportRep::vend( | |
129 | CFTypeRef kcItemRef) | |
130 | { | |
131 | CFTypeID itemType = CFGetTypeID(kcItemRef); | |
132 | if(itemType == SecCertificateGetTypeID()) { | |
133 | return new SecExport::Cert(kcItemRef); | |
134 | } | |
135 | else if(itemType == SecKeyGetTypeID()) { | |
136 | return new SecExport::Key(kcItemRef); | |
137 | } | |
138 | else { | |
139 | MacOSError::throwMe(errSecInvalidItemRef); | |
140 | } | |
141 | } | |
142 | ||
143 | #pragma mark --- Key External rep --- | |
144 | ||
145 | SecExport::Key::Key( | |
146 | CFTypeRef kcItemRef) : | |
147 | SecExportRep(kcItemRef) | |
148 | { | |
149 | ||
150 | /* figure out if it's public, private, or session */ | |
151 | OSStatus ortn; | |
152 | ortn = SecKeyGetCSSMKey((SecKeyRef)kcItemRef, &mCssmKey); | |
153 | if(ortn) { | |
154 | SecImpExpDbg("SecKeyGetCSSMKey failure in SecExportRep::Key()"); | |
155 | MacOSError::throwMe(ortn); | |
156 | } | |
157 | switch(mCssmKey->KeyHeader.KeyClass) { | |
158 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
159 | mExternType = kSecItemTypePublicKey; | |
160 | SecImpExpDbg("SecExportRep::Key(): SET_PubKey"); | |
161 | break; | |
162 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
163 | mExternType = kSecItemTypePrivateKey; | |
164 | SecImpExpDbg("SecExportRep::Key(): SET_PrivKey"); | |
165 | break; | |
166 | case CSSM_KEYCLASS_SESSION_KEY: | |
167 | mExternType = kSecItemTypeSessionKey; | |
168 | SecImpExpDbg("SecExportRep::Key(): SET_SessionKey"); | |
169 | break; | |
170 | default: | |
171 | SecImpExpDbg("SecExportRep::Key(): invalid KeyClass (%lu)", | |
172 | (unsigned long)mCssmKey->KeyHeader.KeyClass); | |
173 | MacOSError::throwMe(errSecInvalidItemRef); | |
174 | } | |
175 | mKeyAlg = mCssmKey->KeyHeader.AlgorithmId; | |
176 | } | |
177 | ||
178 | SecExport::Key::~Key() | |
179 | { | |
180 | /* nothing for now */ | |
181 | } | |
182 | ||
183 | /* | |
184 | * The heart of this class: cook up external representation, appending to | |
185 | * existing CFMutableDataRef. | |
186 | */ | |
187 | OSStatus SecExport::Key::exportRep( | |
188 | SecExternalFormat format, | |
189 | SecItemImportExportFlags flags, | |
190 | const SecKeyImportExportParameters *keyParams, // optional | |
191 | CFMutableDataRef outData, // data appended here | |
192 | const char **pemHeader)// e.g., "X509 CERTIFICATE" | |
193 | { | |
194 | assert(outData != NULL); | |
195 | assert(mKcItem != NULL); | |
196 | assert(mCssmKey != NULL); | |
197 | ||
198 | /* | |
199 | * Currently only OpsnSSH formats allow for a DescriptiveData field | |
200 | * in either wrapped or NULL wrap forms. (In OpenSSH parlance this is | |
201 | * the 'comment' field). Infer the DescriptiveData to be embedded | |
202 | * in the exported key from the item's PrintName attribute. | |
203 | */ | |
204 | CssmAutoData descrData(Allocator::standard()); | |
205 | switch(format) { | |
206 | case kSecFormatSSH: | |
207 | case kSecFormatSSHv2: | |
208 | case kSecFormatWrappedSSH: | |
209 | impExpOpensshInferDescData((SecKeyRef)mKcItem, descrData); | |
210 | break; | |
211 | default: | |
212 | break; | |
213 | } | |
214 | ||
215 | /* | |
216 | * Handle wrapped key formats. | |
217 | */ | |
218 | switch(format) { | |
219 | case kSecFormatWrappedPKCS8: | |
220 | return impExpPkcs8Export((SecKeyRef)mKcItem, flags, keyParams, | |
221 | outData, pemHeader); | |
222 | case kSecFormatWrappedOpenSSL: | |
223 | return impExpWrappedKeyOpenSslExport((SecKeyRef)mKcItem, flags, keyParams, | |
224 | outData, pemHeader, &mPemParamLines); | |
225 | case kSecFormatWrappedSSH: | |
226 | return impExpWrappedOpenSSHExport((SecKeyRef)mKcItem, flags, keyParams, | |
227 | descrData, outData); | |
228 | case kSecFormatWrappedLSH: | |
229 | return errSecUnsupportedFormat; | |
230 | default: | |
231 | break; | |
232 | } | |
233 | ||
234 | /* | |
235 | * Remaining formats just do a NULL key wrap. Figure out the appropriate | |
236 | * CDSA-specific format and wrap parameters. | |
237 | */ | |
427c49bc | 238 | OSStatus ortn = errSecSuccess; |
b1ab9ed8 A |
239 | CSSM_KEYBLOB_FORMAT blobForm; |
240 | ||
241 | switch(mExternType) { | |
242 | case kSecItemTypePublicKey: | |
243 | switch(mKeyAlg) { | |
244 | case CSSM_ALGID_RSA: | |
245 | *pemHeader = PEM_STRING_RSA_PUBLIC; | |
246 | break; | |
247 | case CSSM_ALGID_DH: | |
248 | *pemHeader = PEM_STRING_DH_PUBLIC; | |
249 | break; | |
250 | case CSSM_ALGID_DSA: | |
251 | *pemHeader = PEM_STRING_DSA_PUBLIC; | |
252 | break; | |
253 | case CSSM_ALGID_ECDSA: | |
254 | *pemHeader = PEM_STRING_ECDSA_PUBLIC; | |
255 | break; | |
256 | default: | |
257 | SecImpExpDbg("SecExportRep::exportRep unknown public key alg %lu", | |
258 | (unsigned long)mKeyAlg); | |
259 | return errSecUnsupportedFormat; | |
260 | } /* end switch(mKeyAlg) */ | |
261 | break; /* from case externType kSecItemTypePublicKey */ | |
262 | ||
263 | case kSecItemTypePrivateKey: | |
264 | switch(mKeyAlg) { | |
265 | case CSSM_ALGID_RSA: | |
266 | *pemHeader = PEM_STRING_RSA; | |
267 | break; | |
268 | case CSSM_ALGID_DH: | |
269 | *pemHeader = PEM_STRING_DH_PRIVATE; | |
270 | break; | |
271 | case CSSM_ALGID_DSA: | |
272 | *pemHeader = PEM_STRING_DSA; | |
273 | break; | |
274 | case CSSM_ALGID_ECDSA: | |
275 | *pemHeader = PEM_STRING_ECDSA_PRIVATE; | |
276 | break; | |
277 | default: | |
278 | SecImpExpDbg("SecExportRep::exportRep unknown private key alg " | |
279 | "%lu", (unsigned long)mKeyAlg); | |
280 | return errSecUnsupportedFormat; | |
281 | } /* end switch(mKeyAlg) */ | |
282 | break; /* from case externType kSecItemTypePrivateKey */ | |
283 | ||
284 | case kSecItemTypeSessionKey: | |
285 | *pemHeader = PEM_STRING_SESSION; | |
286 | break; | |
287 | default: | |
288 | assert(0); | |
289 | return errSecInvalidItemRef; | |
290 | } /* switch(mExternType) */ | |
291 | ||
292 | /* Map our external params to CDSA blob format */ | |
293 | CSSM_KEYCLASS keyClass; | |
294 | ortn = impExpKeyForm(format, mExternType, mKeyAlg, &blobForm, &keyClass); | |
295 | if(ortn) { | |
296 | return ortn; | |
297 | } | |
298 | ||
299 | /* Specify format of null-wrapped key */ | |
300 | CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE; | |
301 | switch(mExternType) { | |
302 | case kSecItemTypePrivateKey: | |
303 | formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT; | |
304 | break; | |
305 | case kSecItemTypePublicKey: | |
306 | formatAttrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT; | |
307 | break; | |
308 | /* symmetric key doesn't have a format */ | |
309 | default: | |
310 | break; | |
311 | } | |
312 | ||
313 | CSSM_CSP_HANDLE cspHand; | |
314 | ortn = SecKeyGetCSPHandle((SecKeyRef)mKcItem, &cspHand); | |
315 | if(ortn) { | |
316 | SecImpExpDbg("SecExportRep::exportRep SecKeyGetCSPHandle error"); | |
317 | return ortn; | |
318 | } | |
319 | ||
320 | /* perform the NULL wrap --> wrapped Key */ | |
321 | CSSM_KEY wrappedKey; | |
322 | memset(&wrappedKey, 0, sizeof(wrappedKey)); | |
323 | const CSSM_DATA &dd = descrData; | |
324 | ortn = impExpExportKeyCommon(cspHand, | |
325 | (SecKeyRef)mKcItem, | |
326 | NULL, // wrappingKey not used for NULL | |
327 | &wrappedKey, // destination | |
328 | CSSM_ALGID_NONE, | |
329 | CSSM_ALGMODE_NONE, | |
330 | CSSM_PADDING_NONE, | |
331 | CSSM_KEYBLOB_WRAPPED_FORMAT_NONE, | |
332 | formatAttrType, | |
333 | blobForm, | |
334 | &dd, // descriptiveData | |
335 | NULL); // IV | |
336 | ||
337 | if(ortn == CSSM_OK) { | |
338 | /* pass key data back to caller */ | |
339 | CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length); | |
340 | } | |
341 | CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE); | |
342 | return ortn; | |
343 | } | |
344 | ||
345 | #pragma mark --- Certificate External rep --- | |
346 | ||
347 | SecExport::Cert::Cert( | |
348 | CFTypeRef kcItemRef) : | |
349 | SecExportRep(kcItemRef) | |
350 | { | |
351 | mExternType = kSecItemTypeCertificate; | |
352 | } | |
353 | ||
354 | SecExport::Cert::~Cert() | |
355 | { | |
356 | /* nothing for now */ | |
357 | } | |
358 | ||
359 | /* | |
360 | * The heart of this class: cook up external representation, appending to | |
361 | * existing CFMutableDataRef. | |
362 | */ | |
363 | OSStatus SecExport::Cert::exportRep( | |
364 | SecExternalFormat format, | |
365 | SecItemImportExportFlags flags, | |
366 | const SecKeyImportExportParameters *keyParams, // optional | |
367 | CFMutableDataRef outData, // data appended here | |
368 | const char **pemHeader)// e.g., "X509 CERTIFICATE" | |
369 | { | |
370 | assert(outData != NULL); | |
371 | assert(mKcItem != NULL); | |
372 | ||
373 | switch(format) { | |
374 | case kSecFormatUnknown: // default | |
375 | case kSecFormatX509Cert: // currently, only supported format | |
376 | break; | |
377 | default: | |
378 | SecImpExpDbg("SecExportRep::exportRep unsupported format for cert"); | |
379 | return errSecUnsupportedFormat; | |
380 | } | |
381 | ||
382 | CFDataRef cdata = SecCertificateCopyData((SecCertificateRef)mKcItem); | |
383 | if(!cdata) { | |
384 | SecImpExpDbg("SecExportRep::exportRep SecCertificateGetData error"); | |
385 | return errSecUnsupportedFormat; | |
386 | } | |
387 | ||
388 | CFDataAppendBytes(outData, CFDataGetBytePtr(cdata), CFDataGetLength(cdata)); | |
389 | CFRelease(cdata); | |
390 | *pemHeader = PEM_STRING_X509; | |
427c49bc | 391 | return errSecSuccess; |
b1ab9ed8 A |
392 | } |
393 | ||
394 | #pragma mark --- SecImportRep: Representation of an external object on import --- | |
395 | ||
396 | /* | |
397 | * for import, when we have the external representation. | |
398 | * All arguments except for the CFDataRef are optional (i.e., "unknown" | |
399 | * is legal). | |
400 | */ | |
401 | SecImportRep::SecImportRep( | |
402 | CFDataRef external, | |
403 | SecExternalItemType externType, // may be unknown | |
404 | SecExternalFormat externFormat, // may be unknown | |
405 | CSSM_ALGORITHMS keyAlg, // may be unknown, CSSM_ALGID_NONE | |
406 | CFArrayRef pemParamLines /* = NULL */ ) : | |
407 | mPrintName(NULL), | |
408 | mExternal(external), | |
409 | mExternType(externType), | |
410 | mExternFormat(externFormat), | |
411 | mKeyAlg(keyAlg), | |
412 | mPemParamLines(pemParamLines) | |
413 | { | |
414 | CFRetain(mExternal); | |
415 | } | |
416 | ||
417 | SecImportRep::~SecImportRep() | |
418 | { | |
419 | if(mPrintName) { | |
420 | free(mPrintName); | |
421 | } | |
422 | if(mExternal) { | |
423 | CFRelease(mExternal); | |
424 | } | |
425 | if(mPemParamLines) { | |
426 | CFRelease(mPemParamLines); | |
427 | } | |
428 | } | |
429 | ||
430 | /* | |
431 | * Convert to one or more SecItemRefs and/or import to keychain. | |
432 | * The cspHand handle MUST be a CSPDL handle, not a raw CSP handle. | |
433 | */ | |
434 | OSStatus SecImportRep::importRep( | |
435 | SecKeychainRef importKeychain, // optional | |
436 | CSSM_CSP_HANDLE cspHand, // required | |
437 | SecItemImportExportFlags flags, | |
438 | const SecKeyImportExportParameters *keyParams, // optional | |
439 | ImpPrivKeyImportState &keyImportState, // IN/OUT | |
440 | CFMutableArrayRef outArray) // optional, append here | |
441 | { | |
442 | /* caller must have sorted this out by now */ | |
443 | assert((mExternType != kSecItemTypeUnknown) && | |
444 | (mExternFormat != kSecFormatUnknown)); | |
445 | ||
446 | /* app could conceivably botch these with crafty PEM hacking */ | |
447 | if((mExternal == NULL) || (CFDataGetLength(mExternal) == 0)) { | |
427c49bc | 448 | return errSecParam; |
b1ab9ed8 A |
449 | } |
450 | ||
451 | /* handle the easy ones first */ | |
452 | switch(mExternFormat) { | |
453 | case kSecFormatPKCS12: | |
454 | return impExpPkcs12Import(mExternal, flags, keyParams, | |
455 | keyImportState, importKeychain, cspHand, outArray); | |
456 | case kSecFormatX509Cert: | |
457 | case kSecFormatPKCS7: | |
458 | { | |
459 | OSStatus rx = impExpPkcs7Import(mExternal, flags, keyParams, importKeychain, | |
460 | outArray); | |
461 | if (rx == errSecUnknownFormat) | |
462 | { | |
463 | CSSM_DATA cdata; | |
464 | cdata.Data = (uint8 *)CFDataGetBytePtr(mExternal); | |
465 | cdata.Length = (CSSM_SIZE)CFDataGetLength(mExternal); | |
466 | return impExpImportCertCommon(&cdata, importKeychain, outArray); | |
467 | } | |
468 | return rx; | |
469 | } | |
470 | case kSecFormatNetscapeCertSequence: | |
471 | return impExpNetscapeCertImport(mExternal, flags, keyParams, importKeychain, | |
472 | outArray); | |
473 | default: | |
474 | break; | |
475 | } | |
476 | ||
477 | if((mExternType == kSecItemTypeCertificate) || | |
478 | (mExternType == kSecItemTypeAggregate)) { | |
479 | SecImpExpDbg("SecImportRep::importRep screwup"); | |
427c49bc | 480 | return errSecUnimplemented; |
b1ab9ed8 A |
481 | } |
482 | ||
483 | /* | |
484 | * All that's left: keys. | |
485 | */ | |
486 | if((mExternType == kSecItemTypePrivateKey) && (keyImportState == PIS_NoMore)) { | |
487 | /* multi key import against caller's wishes */ | |
488 | return errSecMultiplePrivKeys; | |
489 | } | |
490 | ||
491 | /* optionally infer PrintName attribute */ | |
492 | switch(mExternFormat) { | |
493 | case kSecFormatSSH: | |
494 | case kSecFormatWrappedSSH: | |
495 | case kSecFormatSSHv2: | |
496 | mPrintName = impExpOpensshInferPrintName(mExternal, mExternType, mExternFormat); | |
497 | break; | |
498 | default: | |
499 | /* use defaults */ | |
500 | break; | |
501 | } | |
502 | ||
427c49bc | 503 | OSStatus ortn = errSecSuccess; |
b1ab9ed8 A |
504 | |
505 | switch(mExternFormat) { | |
506 | case kSecFormatOpenSSL: | |
507 | case kSecFormatSSH: | |
508 | case kSecFormatSSHv2: | |
509 | case kSecFormatBSAFE: | |
510 | case kSecFormatRawKey: | |
511 | ortn = impExpImportRawKey(mExternal, mExternFormat, mExternType, | |
512 | mKeyAlg, importKeychain, cspHand, flags, keyParams, mPrintName, outArray); | |
513 | break; | |
514 | case kSecFormatWrappedPKCS8: | |
515 | ortn = impExpPkcs8Import(mExternal, importKeychain, cspHand, flags, | |
516 | keyParams, outArray); | |
517 | break; | |
518 | case kSecFormatWrappedOpenSSL: | |
519 | ortn = importWrappedKeyOpenssl(importKeychain, cspHand, flags, keyParams, | |
520 | outArray); | |
521 | break; | |
522 | case kSecFormatWrappedSSH: | |
523 | ortn = impExpWrappedOpenSSHImport(mExternal, importKeychain, cspHand, | |
524 | flags, keyParams, mPrintName, outArray); | |
525 | break; | |
526 | case kSecFormatWrappedLSH: | |
527 | default: | |
528 | return errSecUnknownFormat; | |
529 | } | |
427c49bc | 530 | if((ortn == errSecSuccess) && (keyImportState == PIS_AllowOne)) { |
b1ab9ed8 A |
531 | /* reached our limit */ |
532 | keyImportState = PIS_NoMore; | |
533 | } | |
534 | return ortn; | |
535 | } | |
536 |