2 * grunt-quality p12 parse tool.
4 * The PFX ripper in this file uses, and always will use, the
5 * app-space reference PBE and crypto routines in p12Crypto.{h,cpp}
6 * and p12pbe.{h,cpp} in this directory.
10 #include "pkcs12Parsed.h"
11 #include "pkcs12Utils.h"
12 #include <Security/asn1Templates.h>
13 #include <security_asn1/nssUtils.h>
14 #include <security_asn1/SecNssCoder.h>
15 #include <security_cdsa_utils/cuOidParser.h>
16 #include <security_cdsa_utils/cuPrintCert.h>
17 #include <security_pkcs12/pkcs7Templates.h>
18 #include <security_pkcs12/pkcs12Templates.h>
19 #include <security_cdsa_utils/cuFileIo.h>
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <security_cdsa_utils/cuCdsaUtils.h>
25 #include <Security/oidsattr.h>
29 * The stuff which gets passed around to all parse modules
34 P12ParseInfo(SecNssCoder
&coder
,
35 CSSM_CSP_HANDLE cspHand
,
37 /* NULL means don't verify MAC, don't decrypt */
39 /* if this second pwd is absent, use macPwd for both */
41 P12Parsed
&parsed
) // destination
47 importPwd(macPwd
, mPwd
);
48 importPwd(encrPwd
, mEncrPwd
);
53 void importPwd(CFStringRef str
, CSSM_DATA
&pwd
);
56 CSSM_CSP_HANDLE mCspHand
;
58 CSSM_DATA mPwd
; // unicode, double null terminated
60 P12Parsed
&mParsed
; // destination
64 void P12ParseInfo::importPwd(
73 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
,
74 str
, kCFStringEncodingUTF8
, 0);
76 printf("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
77 throw std::invalid_argument("bad passphrase");
79 unsigned keyLen
= CFDataGetLength(cfData
);
80 mCoder
.allocItem(pwd
, keyLen
);
81 memmove(pwd
.Data
, CFDataGetBytePtr(cfData
), keyLen
);
85 static void doIndent(unsigned depth
)
87 for(unsigned i
=0; i
<depth
; i
++) {
92 /* thread-unsafe oid-to-string converter */
93 static char oidStrBuf
[OID_PARSER_STRING_SIZE
];
95 static const char *oidStr(
99 parser
.oidParse(oid
.Data
, oid
.Length
, oidStrBuf
);
103 static void printDataAsHex(
105 unsigned maxToPrint
= 0) // optional, 0 means print it all
109 uint32 len
= d
->Length
;
112 if((maxToPrint
!= 0) && (len
> maxToPrint
)) {
116 printf("len %lu : ", d
->Length
);
117 for(i
=0; i
<len
; i
++) {
118 printf("%02X ", ((unsigned char *)cp
)[i
]);
128 static void printDataAsUnichars(
129 const CSSM_DATA
&data
)
131 if(data
.Length
& 1) {
132 printf("Unicode can not have odd number of bytes\n");
135 /* don't assume anything endian... */
136 unsigned strLen
= data
.Length
/ 2;
137 UniChar
*uc
= (UniChar
*)malloc(strLen
* sizeof(UniChar
));
138 const uint8
*inp
= data
.Data
;
140 while(inp
< (data
.Data
+ data
.Length
)) {
141 *outp
= (((unsigned)inp
[0]) << 8) | inp
[1];
146 CFStringRef cstr
= CFStringCreateWithCharacters(NULL
, uc
, strLen
);
148 printf("***Error creating CFString from unichars\n");
152 outStr
= (char *)malloc(strLen
+ 1);
153 if(CFStringGetCString(cstr
, outStr
, strLen
+ 1, kCFStringEncodingASCII
)) {
154 printf("%s\n", outStr
);
157 printf("***Error converting from unicode to ASCII\n");
169 const CSSM_DATA
&cdata
)
171 if((cdata
.Length
== 0) || (cdata
.Data
== NULL
)) {
174 uint32 len
= cdata
.Length
;
175 if(len
> sizeof(uint32
)) {
176 printf("***Bad formatting for DER integer\n");
177 len
= sizeof(uint32
);
181 uint8
*cp
= cdata
.Data
;
182 for(uint32 i
=0; i
<len
; i
++) {
183 rtn
= (rtn
<< 8) | *cp
++;
188 #ifdef old_and_in_the_way
189 static int writeAuthSafeContent(
190 const CSSM_DATA
&rawBlob
,
196 memset(&pfx
, 0, sizeof(pfx
));
197 if(coder
.decodeItem(rawBlob
, NSS_P12_RawPFXTemplate
, &pfx
)) {
198 printf("***Error on top-level decode of NSS_P12_RawPFX\n");
201 printf("...version = %u\n", (unsigned)dataToInt(pfx
.version
));
202 NSS_P7_RawContentInfo
&rci
= pfx
.authSafe
;
203 printf("...contentType = %s\n", oidStr(rci
.contentType
, parser
));
205 /* parse content per OID the only special case is PKCS7_Data,
206 * which we unwrap from an octet string before writing it */
208 if(nssCompareCssmData(&rci
.contentType
, &CSSMOID_PKCS7_Data
)) {
209 if(coder
.decodeItem(rci
.content
, SEC_OctetStringTemplate
,
211 printf("***Error decoding PKCS7_Data Octet string; writing"
213 toWrite
= rci
.content
;
216 else if(nssCompareCssmData(&rci
.contentType
,
217 &CSSMOID_PKCS7_SignedData
)) {
218 /* the only other legal content type here */
219 /* This is encoded SignedData which I am not even close
220 * to worrying about - Panther p12 won't do this */
221 toWrite
= rci
.content
;
224 printf("***writeAuthSafeContent: bad contentType\n");
227 if(writeFile(outFile
, toWrite
.Data
, toWrite
.Length
)) {
228 printf("***Error writing to %s\n", outFile
);
232 printf("...%u bytes written to %s\n",
233 (unsigned)toWrite
.Length
, outFile
);
237 #endif /* old_and_in_the_way */
240 * Decrypt the contents of a NSS_P7_EncryptedData
242 #define WRITE_DECRYPT_TEXT 0
243 #if WRITE_DECRYPT_TEXT
247 #define IMPORT_EXPORT_COMPLETE 1
249 static int encryptedDataDecrypt(
250 const NSS_P7_EncryptedData
&edata
,
252 NSS_P12_PBE_Params
*pbep
, // preparsed
253 CSSM_DATA
&ptext
) // result goes here in pinfo.coder space
255 /* see if we can grok the encr alg */
256 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
257 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
258 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
259 uint32 keySizeInBits
;
260 uint32 blockSizeInBytes
; // for IV, optional
261 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
262 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
263 #if IMPORT_EXPORT_COMPLETE
266 bool found
= pkcsOidToParams(&edata
.contentInfo
.encrAlg
.algorithm
,
267 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
268 padding
, mode
, pkcs
);
270 bool found
= pkcsOidToParams(&edata
.contentInfo
.encrAlg
.algorithm
,
271 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
273 #endif /* IMPORT_EXPORT_COMPLETE */
276 printf("***EncryptedData encrAlg not understood\n");
280 unsigned iterCount
= dataToInt(pbep
->iterations
);
283 CSSM_RETURN crtn
= p12Decrypt_app(pinfo
.mCspHand
,
284 edata
.contentInfo
.encrContent
,
285 keyAlg
, encrAlg
, pbeHashAlg
,
286 keySizeInBits
, blockSizeInBytes
,
288 iterCount
, pbep
->salt
,
292 #if WRITE_DECRYPT_TEXT
295 sprintf(fname
, "decrypt%d.der", ctr
++);
296 writeFile(fname
, ptext
.Data
, ptext
.Length
);
297 printf("...wrote %u bytes to %s\n",
298 (unsigned)ptext
.Length
, fname
);
307 * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12.
308 * Decode the alg params as a NSS_P12_PBE_Params and parse and
309 * return the result if the pbeParams is non-NULL.
311 static int p12AlgIdParse(
312 const CSSM_X509_ALGORITHM_IDENTIFIER
&algId
,
313 NSS_P12_PBE_Params
*pbeParams
, // optional
315 unsigned depth
) // print indent depth
318 printf("encrAlg = %s\n", oidStr(algId
.algorithm
, pinfo
.mParser
));
319 const CSSM_DATA
¶m
= algId
.parameters
;
320 if(pbeParams
== NULL
) {
321 /* alg params are uninterpreted */
323 printf("Alg Params : ");
324 printDataAsHex(¶m
);
328 if(param
.Length
== 0) {
329 printf("===warning: no alg parameters, this is not optional\n");
333 memset(pbeParams
, 0, sizeof(*pbeParams
));
334 if(pinfo
.mCoder
.decodeItem(param
,
335 NSS_P12_PBE_ParamsTemplate
, pbeParams
)) {
336 printf("***Error decoding NSS_P12_PBE_Params\n");
341 printDataAsHex(&pbeParams
->salt
);
343 if(pbeParams
->iterations
.Length
> 4) {
344 printf("warning: iterations greater than max int\n");
346 printf("Iterations : ");
347 printDataAsHex(&pbeParams
->iterations
);
350 printf("Iterations : %u\n",
351 (unsigned)dataToInt(pbeParams
->iterations
));
357 * Parse a NSS_P7_EncryptedData - specifically in the context
358 * of a P12 in password privacy mode. (The latter assumption is
359 * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters
362 static int encryptedDataParse(
363 const NSS_P7_EncryptedData
&edata
,
365 NSS_P12_PBE_Params
*pbep
, // optional, RETURNED
366 unsigned depth
) // print indent depth
369 printf("version = %u\n", (unsigned)dataToInt(edata
.version
));
370 const NSS_P7_EncrContentInfo
&ci
= edata
.contentInfo
;
372 printf("contentType = %s\n", oidStr(ci
.contentType
, pinfo
.mParser
));
375 * Parse the alg ID, safe PBE params for when we do the
378 const CSSM_X509_ALGORITHM_IDENTIFIER
&algId
= ci
.encrAlg
;
379 if(p12AlgIdParse(algId
, pbep
, pinfo
, depth
)) {
384 printf("encrContent : ");
385 printDataAsHex(&ci
.encrContent
, 12);
389 static int attrParse(
390 const NSS_Attribute
*attr
,
395 printf("attrType : %s\n", oidStr(attr
->attrType
, pinfo
.mParser
));
396 unsigned numVals
= nssArraySize((const void **)attr
->attrValue
);
398 printf("numValues = %u\n", numVals
);
400 for(unsigned dex
=0; dex
<numVals
; dex
++) {
402 printf("val[%u] : ", dex
);
405 * Note: these two enumerated types should only have one att value
406 * per PKCS9. Leave that to real apps, we want to see what's there
409 if(nssCompareCssmData(&attr
->attrType
, &CSSMOID_PKCS9_FriendlyName
)) {
410 /* BMP string (UniCode) */
412 if(pinfo
.mCoder
.decodeItem(*attr
->attrValue
[dex
],
413 kSecAsn1BMPStringTemplate
, &ustr
)) {
414 printf("***Error decoding BMP string\n");
417 printDataAsUnichars(ustr
);
419 else if(nssCompareCssmData(&attr
->attrType
,
420 &CSSMOID_PKCS9_LocalKeyId
)) {
423 if(pinfo
.mCoder
.decodeItem(*attr
->attrValue
[dex
],
424 kSecAsn1OctetStringTemplate
, &ostr
)) {
425 printf("***Error decoding LocalKeyId string\n");
428 printDataAsHex(&ostr
, 16);
431 printDataAsHex(attr
->attrValue
[dex
], 8);
438 * ShroudedKeyBag parser w/decrypt
440 static int shroudedKeyBagParse(
441 const NSS_P12_ShroudedKeyBag
*keyBag
,
445 const CSSM_X509_ALGORITHM_IDENTIFIER
&algId
= keyBag
->algorithm
;
446 NSS_P12_PBE_Params pbep
;
447 if(p12AlgIdParse(algId
, &pbep
, pinfo
, depth
)) {
450 if(pinfo
.mPwd
.Data
== NULL
) {
452 printf("=== Key not decrypted (no passphrase)===\n");
457 * Prepare for decryption
459 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
460 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
461 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
462 uint32 keySizeInBits
;
463 uint32 blockSizeInBytes
; // for IV, optional
464 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
465 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
466 #if IMPORT_EXPORT_COMPLETE
469 bool found
= pkcsOidToParams(&algId
.algorithm
,
470 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
471 padding
, mode
, pkcs
);
473 bool found
= pkcsOidToParams(&algId
.algorithm
,
474 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
479 printf("***ShroudedKeyBag encrAlg not understood\n");
483 unsigned iterCount
= dataToInt(pbep
.iterations
);
484 CSSM_DATA berPrivKey
;
486 /* decrypt, result is BER encoded private key */
487 CSSM_RETURN crtn
= p12Decrypt_app(pinfo
.mCspHand
,
488 keyBag
->encryptedData
,
489 keyAlg
, encrAlg
, pbeHashAlg
,
490 keySizeInBits
, blockSizeInBytes
,
492 iterCount
, pbep
.salt
,
498 printf("***Error decrypting private key\n");
503 NSS_PrivateKeyInfo privKey
;
504 memset(&privKey
, 0, sizeof(privKey
));
505 if(pinfo
.mCoder
.decodeItem(berPrivKey
,
506 kSecAsn1PrivateKeyInfoTemplate
, &privKey
)) {
508 printf("***Error decoding decrypted private key\n");
513 * in P12 library, we'd convert the result into a CSSM_KEY
516 CSSM_X509_ALGORITHM_IDENTIFIER
&privAlg
= privKey
.algorithm
;
518 printf("Priv Key Alg : %s\n", oidStr(privAlg
.algorithm
, pinfo
.mParser
));
520 printf("Priv Key Blob : ");
521 printDataAsHex(&privKey
.privateKey
, 16);
523 unsigned numAttrs
= nssArraySize((const void**)privKey
.attributes
);
526 printf("numAttrs = %u\n", numAttrs
);
527 for(unsigned i
=0; i
<numAttrs
; i
++) {
529 printf("attr[%u]:\n", i
);
530 attrParse(privKey
.attributes
[i
], pinfo
, depth
+6);
539 static int certBagParse(
540 const NSS_P12_CertBag
*certBag
,
544 /* fixe - we really need to store the attrs along with the cert here! */
545 switch(certBag
->type
) {
548 printf("X509 cert found, size %u\n",
549 (unsigned)certBag
->certValue
.Length
);
550 pinfo
.mParsed
.mCerts
.addBlob(certBag
->certValue
);
554 printf("Unknown cert type found\n");
555 P12UnknownBlob
*uk
= new P12UnknownBlob(certBag
->certValue
,
557 pinfo
.mParsed
.mUnknown
.addBlob(uk
);
565 static int crlBagParse(
566 const NSS_P12_CrlBag
*crlBag
,
570 /* fixe - we really need to store the attrs along with the crl here! */
571 switch(crlBag
->type
) {
574 printf("X509 CRL found, size %u\n",
575 (unsigned)crlBag
->crlValue
.Length
);
576 pinfo
.mParsed
.mCrls
.addBlob(crlBag
->crlValue
);
580 printf("Unknown CRL type found\n");
581 P12UnknownBlob
*uk
= new P12UnknownBlob(crlBag
->crlValue
,
583 pinfo
.mParsed
.mUnknown
.addBlob(uk
);
590 * Parse an encoded NSS_P12_SafeContents. This could be either
591 * present as plaintext in an AuthSafe or decrypted.
593 static int safeContentsParse(
594 const CSSM_DATA
&contentsBlob
,
596 unsigned depth
) // print indent depth
598 NSS_P12_SafeContents sc
;
599 memset(&sc
, 0, sizeof(sc
));
600 if(pinfo
.mCoder
.decodeItem(contentsBlob
, NSS_P12_SafeContentsTemplate
,
602 printf("***Error decoding SafeContents\n");
605 unsigned numBags
= nssArraySize((const void **)sc
.bags
);
607 printf("SafeContents num bags %u\n", numBags
);
610 for(unsigned dex
=0; dex
<numBags
; dex
++) {
611 NSS_P12_SafeBag
*bag
= sc
.bags
[dex
];
613 printf("Bag %u:\n", dex
);
615 /* common stuff here */
617 printf("bagId = %s\n", oidStr(bag
->bagId
, pinfo
.mParser
));
619 printf("type = %s\n", p12BagTypeStr(bag
->type
));
620 unsigned numAttrs
= nssArraySize((const void**)bag
->bagAttrs
);
623 printf("numAttrs = %u\n", numAttrs
);
624 for(unsigned i
=0; i
<numAttrs
; i
++) {
626 printf("attr[%u]:\n", i
);
627 attrParse(bag
->bagAttrs
[i
], pinfo
, depth
+6);
632 * Now break out to individual bag type
634 * This hacked line breaks when we have a real key bag defined
636 unsigned defaultLen
= (unsigned)bag
->bagValue
.keyBag
->Length
;
640 printf("KeyBag: size %u\n", defaultLen
);
642 case BT_ShroudedKeyBag
:
644 printf("ShroudedKeyBag:\n");
645 rtn
= shroudedKeyBagParse(bag
->bagValue
.shroudedKeyBag
,
651 printf("CertBag:\n");
652 rtn
= certBagParse(bag
->bagValue
.certBag
,
659 rtn
= crlBagParse(bag
->bagValue
.crlBag
,
665 printf("SecretBag: size %u\n", defaultLen
);
667 case BT_SafeContentsBag
:
669 printf("SafeContentsBag: size %u\n", defaultLen
);
673 printf("===Warning: unknownBagType (%u)\n",
674 (unsigned)bag
->type
);
685 * Parse a ContentInfo in the context of (i.e., as an element of)
686 * an element in a AuthenticatedSafe
688 static int authSafeElementParse(
689 const NSS_P7_DecodedContentInfo
*info
,
691 unsigned depth
) // print indent depth
693 char oidStr
[OID_PARSER_STRING_SIZE
];
694 pinfo
.mParser
.oidParse(info
->contentType
.Data
,
695 info
->contentType
.Length
, oidStr
);
698 printf("contentType = %s\n", oidStr
);
700 printf("type = %s\n", p7ContentInfoTypeStr(info
->type
));
704 /* unencrypted SafeContents */
706 printf("raw size: %u\n",
707 (unsigned)info
->content
.data
->Length
);
709 printf("Plaintext SafeContents:\n");
710 rtn
= safeContentsParse(*info
->content
.data
,
714 case CT_EncryptedData
:
717 printf("EncryptedData:\n");
718 NSS_P12_PBE_Params pbep
;
719 rtn
= encryptedDataParse(*info
->content
.encryptData
,
720 pinfo
, &pbep
, depth
+3);
724 if(pinfo
.mPwd
.Data
== NULL
) {
726 printf("=== Contents not decrypted (no passphrase)===\n");
730 * Decrypt contents to get a SafeContents and
733 CSSM_DATA ptext
= {0, NULL
};
734 rtn
= encryptedDataDecrypt(*info
->content
.encryptData
,
735 pinfo
, &pbep
, ptext
);
738 printf("***Error decrypting CT_EncryptedData\n");
741 printf("Decrypted SafeContents {\n");
742 rtn
= safeContentsParse(ptext
, pinfo
, depth
+3);
749 /* the rest map to an ASN_ANY/CSSM_DATA for now */
751 printf("size of %u is all we know today\n",
752 (unsigned)info
->content
.data
->Length
);
760 * Parse an encoded NSS_P12_AuthenticatedSafe
762 static int authSafeParse(
763 const CSSM_DATA authSafeBlob
,
765 unsigned depth
) // print indent depth
767 NSS_P12_AuthenticatedSafe authSafe
;
769 memset(&authSafe
, 0, sizeof(authSafe
));
770 if(pinfo
.mCoder
.decodeItem(authSafeBlob
,
771 NSS_P12_AuthenticatedSafeTemplate
,
773 printf("***Error decoding authSafe\n");
776 unsigned numInfos
= nssArraySize((const void **)authSafe
.info
);
778 printf("authSafe numInfos %u\n", numInfos
);
781 for(unsigned dex
=0; dex
<numInfos
; dex
++) {
782 NSS_P7_DecodedContentInfo
*info
= authSafe
.info
[dex
];
784 printf("AuthSafe.info[%u] {\n", dex
);
785 rtn
= authSafeElementParse(info
, pinfo
, depth
+3);
795 static int p12MacParse(
796 const NSS_P12_MacData
&macData
,
798 unsigned depth
) // print indent depth
800 if(p12AlgIdParse(macData
.mac
.digestAlgorithm
, NULL
, pinfo
, depth
)) {
805 printDataAsHex(&macData
.mac
.digest
, 20);
808 printDataAsHex(&macData
.macSalt
, 16);
809 const CSSM_DATA
&iter
= macData
.iterations
;
811 if(iter
.Length
> 4) {
813 printf("***Warning: malformed iteraton length (%u)\n",
814 (unsigned)iter
.Length
);
816 unsigned i
= dataToInt(iter
);
818 printf("Iterations = %u\n", i
);
823 const CSSM_DATA
&rawBlob
,
825 unsigned depth
) // print indent depth
827 NSS_P12_DecodedPFX pfx
;
828 memset(&pfx
, 0, sizeof(pfx
));
829 if(pinfo
.mCoder
.decodeItem(rawBlob
, NSS_P12_DecodedPFXTemplate
, &pfx
)) {
830 printf("***Error on top-level decode of NSS_P12_DecodedPFX\n");
834 printf("version = %u\n", (unsigned)dataToInt(pfx
.version
));
835 NSS_P7_DecodedContentInfo
&dci
= pfx
.authSafe
;
838 printf("contentType = %s\n", oidStr(dci
.contentType
, pinfo
.mParser
));
840 printf("type = %s\n", p7ContentInfoTypeStr(dci
.type
));
842 if(nssCompareCssmData(&dci
.contentType
, &CSSMOID_PKCS7_Data
)) {
844 printf("AuthenticatedSafe Length %u {\n",
845 (unsigned)dci
.content
.data
->Length
);
846 rtn
= authSafeParse(*dci
.content
.data
, pinfo
, depth
+3);
851 printf("Not parsing any other content type today.\n");
855 printf("Mac Data {\n");
856 p12MacParse(*pfx
.macData
, pinfo
, depth
+3);
859 if(pinfo
.mPwd
.Data
== NULL
) {
861 printf("=== MAC not verified (no passphrase)===\n");
864 CSSM_RETURN crtn
= p12VerifyMac_app(pfx
, pinfo
.mCspHand
,
865 pinfo
.mPwd
, pinfo
.mCoder
);
868 cssmPerror("p12VerifyMac", crtn
);
870 printf("***MAC verify failure.\n");
873 printf("MAC verifies OK.\n");
882 CSSM_CSP_HANDLE cspHand
,
888 P12Parsed
parsed(coder
);
889 P12ParseInfo
pinfo(coder
,
893 NULL
, // no separate pwd
896 printf("PKCS12 PFX:\n");
897 int rtn
= p12Parse(rawBlob
, pinfo
, 3);
901 P12KnownBlobs
&certs
= pinfo
.mParsed
.mCerts
;
902 if(certs
.mNumBlobs
) {
904 for(unsigned dex
=0; dex
<certs
.mNumBlobs
; dex
++) {
905 printf("Cert %u:\n", dex
);
906 printCert(certs
.mBlobs
[dex
].Data
,
907 certs
.mBlobs
[dex
].Length
, CSSM_FALSE
);
911 P12KnownBlobs
&crls
= pinfo
.mParsed
.mCrls
;
914 for(unsigned dex
=0; dex
<crls
.mNumBlobs
; dex
++) {
915 printf("CRL %u:\n", dex
);
916 printCrl(crls
.mBlobs
[dex
].Data
,
917 crls
.mBlobs
[dex
].Length
, CSSM_FALSE
);