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.
8 #include "SecNssCoder.h"
9 #include <Security/asn1Templates.h>
10 #include <security_asn1/nssUtils.h>
11 #include <security_cdsa_utils/cuOidParser.h>
12 #include <security_cdsa_utils/cuPrintCert.h>
13 #include <security_pkcs12/pkcs7Templates.h>
14 #include <security_pkcs12/pkcs12Templates.h>
15 #include <security_pkcs12/pkcs12Utils.h>
16 #include "p12Crypto.h"
17 #include <security_cdsa_utils/cuFileIo.h>
18 #include "pkcs12Parsed.h"
22 #include <CoreFoundation/CoreFoundation.h>
23 #include <security_cdsa_utils/cuCdsaUtils.h>
24 #include <Security/oidsattr.h>
27 * The stuff which gets passed around to all parse modules
32 P12ParseInfo(SecNssCoder
&coder
,
33 CSSM_CSP_HANDLE cspHand
,
35 /* NULL means don't verify MAC, don't decrypt */
37 /* if this second pwd is absent, use macPwd for both */
39 P12Parsed
&parsed
) // destination
45 pwdToUcode(macPwd
, mPwd
);
46 pwdToUcode(encrPwd
, mEncrPwd
);
51 void pwdToUcode(CFStringRef str
, CSSM_DATA
&pwd
);
54 CSSM_CSP_HANDLE mCspHand
;
56 CSSM_DATA mPwd
; // unicode, double null terminated
58 P12Parsed
&mParsed
; // destination
62 void P12ParseInfo::pwdToUcode(
71 CFIndex len
= CFStringGetLength(str
);
72 mCoder
.allocItem(pwd
, (len
* sizeof(UniChar
)) + 2);
74 for(CFIndex dex
=0; dex
<len
; dex
++) {
75 UniChar uc
= CFStringGetCharacterAtIndex(str
, dex
);
83 static void doIndent(unsigned depth
)
85 for(unsigned i
=0; i
<depth
; i
++) {
90 /* thread-unsafe oid-to-string converter */
91 static char oidStrBuf
[OID_PARSER_STRING_SIZE
];
93 static const char *oidStr(
97 parser
.oidParse(oid
.Data
, oid
.Length
, oidStrBuf
);
101 static void printDataAsHex(
103 unsigned maxToPrint
= 0) // optional, 0 means print it all
107 uint32 len
= d
->Length
;
110 if((maxToPrint
!= 0) && (len
> maxToPrint
)) {
114 for(i
=0; i
<len
; i
++) {
115 printf("%02X ", ((unsigned char *)cp
)[i
]);
125 static void printDataAsUnichars(
126 const CSSM_DATA
&data
)
128 if(data
.Length
& 1) {
129 printf("Unicode can not have odd number of bytes\n");
132 /* don't assume anything endian... */
133 unsigned strLen
= data
.Length
/ 2;
134 UniChar
*uc
= (UniChar
*)malloc(strLen
* sizeof(UniChar
));
135 const uint8
*inp
= data
.Data
;
137 while(inp
< (data
.Data
+ data
.Length
)) {
138 *outp
= (((unsigned)inp
[0]) << 8) | inp
[1];
143 CFStringRef cstr
= CFStringCreateWithCharacters(NULL
, uc
, strLen
);
145 printf("***Error creating CFString from unichars\n");
149 outStr
= (char *)malloc(strLen
+ 1);
150 if(CFStringGetCString(cstr
, outStr
, strLen
+ 1, kCFStringEncodingASCII
)) {
151 printf("%s\n", outStr
);
154 printf("***Error converting from unicode to ASCII\n");
166 const CSSM_DATA
&cdata
)
168 if((cdata
.Length
== 0) || (cdata
.Data
== NULL
)) {
171 uint32 len
= cdata
.Length
;
172 if(len
> sizeof(uint32
)) {
173 printf("***Bad formatting for DER integer\n");
174 len
= sizeof(uint32
);
178 uint8
*cp
= cdata
.Data
;
179 for(uint32 i
=0; i
<len
; i
++) {
180 rtn
= (rtn
<< 8) | *cp
++;
185 #ifdef old_and_in_the_way
186 static int writeAuthSafeContent(
187 const CSSM_DATA
&rawBlob
,
193 memset(&pfx
, 0, sizeof(pfx
));
194 if(coder
.decodeItem(rawBlob
, NSS_P12_RawPFXTemplate
, &pfx
)) {
195 printf("***Error on top-level decode of NSS_P12_RawPFX\n");
198 printf("...version = %u\n", (unsigned)dataToInt(pfx
.version
));
199 NSS_P7_RawContentInfo
&rci
= pfx
.authSafe
;
200 printf("...contentType = %s\n", oidStr(rci
.contentType
, parser
));
202 /* parse content per OID the only special case is PKCS7_Data,
203 * which we unwrap from an octet string before writing it */
205 if(nssCompareCssmData(&rci
.contentType
, &CSSMOID_PKCS7_Data
)) {
206 if(coder
.decodeItem(rci
.content
, SEC_OctetStringTemplate
,
208 printf("***Error decoding PKCS7_Data Octet string; writing"
210 toWrite
= rci
.content
;
213 else if(nssCompareCssmData(&rci
.contentType
,
214 &CSSMOID_PKCS7_SignedData
)) {
215 /* the only other legal content type here */
216 /* This is encoded SignedData which I am not even close
217 * to worrying about - Panther p12 won't do this */
218 toWrite
= rci
.content
;
221 printf("***writeAuthSafeContent: bad contentType\n");
224 if(writeFile(outFile
, toWrite
.Data
, toWrite
.Length
)) {
225 printf("***Error writing to %s\n", outFile
);
229 printf("...%u bytes written to %s\n",
230 (unsigned)toWrite
.Length
, outFile
);
234 #endif /* old_and_in_the_way */
237 * Decrypt the contents of a NSS_P7_EncryptedData
239 #define WRITE_DECRYPT_TEXT 0
240 #if WRITE_DECRYPT_TEXT
244 #define IMPORT_EXPORT_COMPLETE 1
246 static int encryptedDataDecrypt(
247 const NSS_P7_EncryptedData
&edata
,
249 NSS_P12_PBE_Params
*pbep
, // preparsed
250 CSSM_DATA
&ptext
) // result goes here in pinfo.coder space
252 /* see if we can grok the encr alg */
253 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
254 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
255 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
256 uint32 keySizeInBits
;
257 uint32 blockSizeInBytes
; // for IV, optional
258 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
259 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
260 #if IMPORT_EXPORT_COMPLETE
263 bool found
= pkcsOidToParams(&edata
.contentInfo
.encrAlg
.algorithm
,
264 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
265 padding
, mode
, pkcs
);
267 bool found
= pkcsOidToParams(&edata
.contentInfo
.encrAlg
.algorithm
,
268 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
270 #endif /* IMPORT_EXPORT_COMPLETE */
273 printf("***EncryptedData encrAlg not understood\n");
277 unsigned iterCount
= dataToInt(pbep
->iterations
);
280 CSSM_RETURN crtn
= p12Decrypt_app(pinfo
.mCspHand
,
281 edata
.contentInfo
.encrContent
,
282 keyAlg
, encrAlg
, pbeHashAlg
,
283 keySizeInBits
, blockSizeInBytes
,
285 iterCount
, pbep
->salt
,
289 #if WRITE_DECRYPT_TEXT
292 sprintf(fname
, "decrypt%d.der", ctr
++);
293 writeFile(fname
, ptext
.Data
, ptext
.Length
);
294 printf("...wrote %u bytes to %s\n",
295 (unsigned)ptext
.Length
, fname
);
304 * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12.
305 * Decode the alg params as a NSS_P12_PBE_Params and parse and
306 * return the result if the pbeParams is non-NULL.
308 static int p12AlgIdParse(
309 const CSSM_X509_ALGORITHM_IDENTIFIER
&algId
,
310 NSS_P12_PBE_Params
*pbeParams
, // optional
312 unsigned depth
) // print indent depth
315 printf("encrAlg = %s\n", oidStr(algId
.algorithm
, pinfo
.mParser
));
316 const CSSM_DATA
¶m
= algId
.parameters
;
317 if(pbeParams
== NULL
) {
318 /* alg params are uninterpreted */
320 printf("Alg Params : ");
321 printDataAsHex(¶m
);
325 if(param
.Length
== 0) {
326 printf("===warning: no alg parameters, this is not optional\n");
330 memset(pbeParams
, 0, sizeof(*pbeParams
));
331 if(pinfo
.mCoder
.decodeItem(param
,
332 NSS_P12_PBE_ParamsTemplate
, pbeParams
)) {
333 printf("***Error decoding NSS_P12_PBE_Params\n");
338 printDataAsHex(&pbeParams
->salt
);
340 if(pbeParams
->iterations
.Length
> 4) {
341 printf("warning: iterations greater than max int\n");
343 printf("Iterations : ");
344 printDataAsHex(&pbeParams
->iterations
);
347 printf("Iterations : %u\n",
348 (unsigned)dataToInt(pbeParams
->iterations
));
354 * Parse a NSS_P7_EncryptedData - specifically in the context
355 * of a P12 in password privacy mode. (The latter assumption is
356 * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters
359 static int encryptedDataParse(
360 const NSS_P7_EncryptedData
&edata
,
362 NSS_P12_PBE_Params
*pbep
, // optional, RETURNED
363 unsigned depth
) // print indent depth
366 printf("version = %u\n", (unsigned)dataToInt(edata
.version
));
367 const NSS_P7_EncrContentInfo
&ci
= edata
.contentInfo
;
369 printf("contentType = %s\n", oidStr(ci
.contentType
, pinfo
.mParser
));
372 * Parse the alg ID, safe PBE params for when we do the
375 const CSSM_X509_ALGORITHM_IDENTIFIER
&algId
= ci
.encrAlg
;
376 if(p12AlgIdParse(algId
, pbep
, pinfo
, depth
)) {
381 printf("encrContent : ");
382 printDataAsHex(&ci
.encrContent
, 12);
386 static int attrParse(
387 const NSS_Attribute
*attr
,
392 printf("attrType : %s\n", oidStr(attr
->attrType
, pinfo
.mParser
));
393 unsigned numVals
= nssArraySize((const void **)attr
->attrValue
);
395 printf("numValues = %u\n", numVals
);
397 for(unsigned dex
=0; dex
<numVals
; dex
++) {
399 printf("val[%u] : ", dex
);
402 * Note: these two enumerated types should only have one att value
403 * per PKCS9. Leave that to real apps, we want to see what's there
406 if(nssCompareCssmData(&attr
->attrType
, &CSSMOID_PKCS9_FriendlyName
)) {
407 /* BMP string (UniCode) */
409 if(pinfo
.mCoder
.decodeItem(*attr
->attrValue
[dex
],
410 kSecAsn1BMPStringTemplate
, &ustr
)) {
411 printf("***Error decoding BMP string\n");
414 printDataAsUnichars(ustr
);
416 else if(nssCompareCssmData(&attr
->attrType
,
417 &CSSMOID_PKCS9_LocalKeyId
)) {
420 if(pinfo
.mCoder
.decodeItem(*attr
->attrValue
[dex
],
421 kSecAsn1ObjectIDTemplate
, &ostr
)) {
422 printf("***Error decoding LocalKeyId string\n");
425 printDataAsHex(&ostr
, 16);
428 printDataAsHex(attr
->attrValue
[dex
], 8);
435 * ShroudedKeyBag parser w/decrypt
437 static int shroudedKeyBagParse(
438 const NSS_P12_ShroudedKeyBag
*keyBag
,
442 const CSSM_X509_ALGORITHM_IDENTIFIER
&algId
= keyBag
->algorithm
;
443 NSS_P12_PBE_Params pbep
;
444 if(p12AlgIdParse(algId
, &pbep
, pinfo
, depth
)) {
447 if(pinfo
.mPwd
.Data
== NULL
) {
449 printf("=== Key not decrypted (no passphrase)===\n");
454 * Prepare for decryption
456 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
457 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
458 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
459 uint32 keySizeInBits
;
460 uint32 blockSizeInBytes
; // for IV, optional
461 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
462 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
463 #if IMPORT_EXPORT_COMPLETE
466 bool found
= pkcsOidToParams(&algId
.algorithm
,
467 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
468 padding
, mode
, pkcs
);
470 bool found
= pkcsOidToParams(&algId
.algorithm
,
471 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
476 printf("***ShroudedKeyBag encrAlg not understood\n");
480 unsigned iterCount
= dataToInt(pbep
.iterations
);
481 CSSM_DATA berPrivKey
;
483 /* decrypt, result is BER encoded private key */
484 CSSM_RETURN crtn
= p12Decrypt_app(pinfo
.mCspHand
,
485 keyBag
->encryptedData
,
486 keyAlg
, encrAlg
, pbeHashAlg
,
487 keySizeInBits
, blockSizeInBytes
,
489 iterCount
, pbep
.salt
,
495 printf("***Error decrypting private key\n");
500 NSS_PrivateKeyInfo privKey
;
501 memset(&privKey
, 0, sizeof(privKey
));
502 if(pinfo
.mCoder
.decodeItem(berPrivKey
,
503 kSecAsn1PrivateKeyInfoTemplate
, &privKey
)) {
505 printf("***Error decoding decrypted private key\n");
510 * in P12 library, we'd convert the result into a CSSM_KEY
513 CSSM_X509_ALGORITHM_IDENTIFIER
&privAlg
= privKey
.algorithm
;
515 printf("Priv Key Alg : %s\n", oidStr(privAlg
.algorithm
, pinfo
.mParser
));
517 printf("Priv Key Blob : ");
518 printDataAsHex(&privKey
.privateKey
, 16);
520 unsigned numAttrs
= nssArraySize((const void**)privKey
.attributes
);
523 printf("numAttrs = %u\n", numAttrs
);
524 for(unsigned i
=0; i
<numAttrs
; i
++) {
526 printf("attr[%u]:\n", i
);
527 attrParse(privKey
.attributes
[i
], pinfo
, depth
+6);
536 static int certBagParse(
537 const NSS_P12_CertBag
*certBag
,
541 /* fixe - we really need to store the attrs along with the cert here! */
542 switch(certBag
->type
) {
545 printf("X509 cert found, size %u\n",
546 (unsigned)certBag
->certValue
.Length
);
547 pinfo
.mParsed
.mCerts
.addBlob(certBag
->certValue
);
551 printf("Unknown cert type found\n");
552 P12UnknownBlob
*uk
= new P12UnknownBlob(certBag
->certValue
,
554 pinfo
.mParsed
.mUnknown
.addBlob(uk
);
562 static int crlBagParse(
563 const NSS_P12_CrlBag
*crlBag
,
567 /* fixe - we really need to store the attrs along with the crl here! */
568 switch(crlBag
->type
) {
571 printf("X509 CRL found, size %u\n",
572 (unsigned)crlBag
->crlValue
.Length
);
573 pinfo
.mParsed
.mCrls
.addBlob(crlBag
->crlValue
);
577 printf("Unknown CRL type found\n");
578 P12UnknownBlob
*uk
= new P12UnknownBlob(crlBag
->crlValue
,
580 pinfo
.mParsed
.mUnknown
.addBlob(uk
);
587 * Parse an encoded NSS_P12_SafeContents. This could be either
588 * present as plaintext in an AuthSafe or decrypted.
590 static int safeContentsParse(
591 const CSSM_DATA
&contentsBlob
,
593 unsigned depth
) // print indent depth
595 NSS_P12_SafeContents sc
;
596 memset(&sc
, 0, sizeof(sc
));
597 if(pinfo
.mCoder
.decodeItem(contentsBlob
, NSS_P12_SafeContentsTemplate
,
599 printf("***Error decoding SafeContents\n");
602 unsigned numBags
= nssArraySize((const void **)sc
.bags
);
604 printf("SafeContents num bags %u\n", numBags
);
607 for(unsigned dex
=0; dex
<numBags
; dex
++) {
608 NSS_P12_SafeBag
*bag
= sc
.bags
[dex
];
610 printf("Bag %u:\n", dex
);
612 /* common stuff here */
614 printf("bagId = %s\n", oidStr(bag
->bagId
, pinfo
.mParser
));
616 printf("type = %s\n", p12BagTypeStr(bag
->type
));
617 unsigned numAttrs
= nssArraySize((const void**)bag
->bagAttrs
);
620 printf("numAttrs = %u\n", numAttrs
);
621 for(unsigned i
=0; i
<numAttrs
; i
++) {
623 printf("attr[%u]:\n", i
);
624 attrParse(bag
->bagAttrs
[i
], pinfo
, depth
+6);
629 * Now break out to individual bag type
631 * This hacked line breaks when we have a real key bag defined
633 unsigned defaultLen
= (unsigned)bag
->bagValue
.keyBag
->Length
;
637 printf("KeyBag: size %u\n", defaultLen
);
639 case BT_ShroudedKeyBag
:
641 printf("ShroudedKeyBag:\n");
642 rtn
= shroudedKeyBagParse(bag
->bagValue
.shroudedKeyBag
,
648 printf("CertBag:\n");
649 rtn
= certBagParse(bag
->bagValue
.certBag
,
656 rtn
= crlBagParse(bag
->bagValue
.crlBag
,
662 printf("SecretBag: size %u\n", defaultLen
);
664 case BT_SafeContentsBag
:
666 printf("SafeContentsBag: size %u\n", defaultLen
);
670 printf("===Warning: unknownBagType (%u)\n",
671 (unsigned)bag
->type
);
682 * Parse a ContentInfo in the context of (i.e., as an element of)
683 * an element in a AuthenticatedSafe
685 static int authSafeElementParse(
686 const NSS_P7_DecodedContentInfo
*info
,
688 unsigned depth
) // print indent depth
690 char oidStr
[OID_PARSER_STRING_SIZE
];
691 pinfo
.mParser
.oidParse(info
->contentType
.Data
,
692 info
->contentType
.Length
, oidStr
);
695 printf("contentType = %s\n", oidStr
);
697 printf("type = %s\n", p7ContentInfoTypeStr(info
->type
));
701 /* unencrypted SafeContents */
703 printf("raw size: %u\n",
704 (unsigned)info
->content
.data
->Length
);
706 printf("Plaintext SafeContents:\n");
707 rtn
= safeContentsParse(*info
->content
.data
,
711 case CT_EncryptedData
:
714 printf("EncryptedData:\n");
715 NSS_P12_PBE_Params pbep
;
716 rtn
= encryptedDataParse(*info
->content
.encryptData
,
717 pinfo
, &pbep
, depth
+3);
721 if(pinfo
.mPwd
.Data
== NULL
) {
723 printf("=== Contents not decrypted (no passphrase)===\n");
727 * Decrypt contents to get a SafeContents and
730 CSSM_DATA ptext
= {0, NULL
};
731 rtn
= encryptedDataDecrypt(*info
->content
.encryptData
,
732 pinfo
, &pbep
, ptext
);
735 printf("***Error decrypting CT_EncryptedData\n");
738 printf("Decrypted SafeContents {\n");
739 rtn
= safeContentsParse(ptext
, pinfo
, depth
+3);
746 /* the rest map to an ASN_ANY/CSSM_DATA for now */
748 printf("size of %u is all we know today\n",
749 (unsigned)info
->content
.data
->Length
);
757 * Parse an encoded NSS_P12_AuthenticatedSafe
759 static int authSafeParse(
760 const CSSM_DATA authSafeBlob
,
762 unsigned depth
) // print indent depth
764 NSS_P12_AuthenticatedSafe authSafe
;
766 memset(&authSafe
, 0, sizeof(authSafe
));
767 if(pinfo
.mCoder
.decodeItem(authSafeBlob
,
768 NSS_P12_AuthenticatedSafeTemplate
,
770 printf("***Error decoding authSafe\n");
773 unsigned numInfos
= nssArraySize((const void **)authSafe
.info
);
775 printf("authSafe numInfos %u\n", numInfos
);
778 for(unsigned dex
=0; dex
<numInfos
; dex
++) {
779 NSS_P7_DecodedContentInfo
*info
= authSafe
.info
[dex
];
781 printf("AuthSafe.info[%u] {\n", dex
);
782 rtn
= authSafeElementParse(info
, pinfo
, depth
+3);
792 static int p12MacParse(
793 const NSS_P12_MacData
&macData
,
795 unsigned depth
) // print indent depth
797 if(p12AlgIdParse(macData
.mac
.digestAlgorithm
, NULL
, pinfo
, depth
)) {
802 printDataAsHex(&macData
.mac
.digest
, 20);
805 printDataAsHex(&macData
.macSalt
, 16);
806 const CSSM_DATA
&iter
= macData
.iterations
;
808 if(iter
.Length
> 4) {
810 printf("***Warning: malformed iteraton length (%u)\n",
811 (unsigned)iter
.Length
);
813 unsigned i
= dataToInt(iter
);
815 printf("Iterations = %u\n", i
);
820 const CSSM_DATA
&rawBlob
,
822 unsigned depth
) // print indent depth
824 NSS_P12_DecodedPFX pfx
;
825 memset(&pfx
, 0, sizeof(pfx
));
826 if(pinfo
.mCoder
.decodeItem(rawBlob
, NSS_P12_DecodedPFXTemplate
, &pfx
)) {
827 printf("***Error on top-level decode of NSS_P12_DecodedPFX\n");
831 printf("version = %u\n", (unsigned)dataToInt(pfx
.version
));
832 NSS_P7_DecodedContentInfo
&dci
= pfx
.authSafe
;
835 printf("contentType = %s\n", oidStr(dci
.contentType
, pinfo
.mParser
));
837 printf("type = %s\n", p7ContentInfoTypeStr(dci
.type
));
839 if(nssCompareCssmData(&dci
.contentType
, &CSSMOID_PKCS7_Data
)) {
841 printf("AuthenticatedSafe Length %u {\n",
842 (unsigned)dci
.content
.data
->Length
);
843 rtn
= authSafeParse(*dci
.content
.data
, pinfo
, depth
+3);
848 printf("Not parsing any other content type today.\n");
852 printf("Mac Data {\n");
853 p12MacParse(*pfx
.macData
, pinfo
, depth
+3);
856 if(pinfo
.mPwd
.Data
== NULL
) {
858 printf("=== MAC not verified (no passphrase)===\n");
861 CSSM_RETURN crtn
= p12VerifyMac_app(pfx
, pinfo
.mCspHand
,
862 pinfo
.mPwd
, pinfo
.mCoder
);
865 cssmPerror("p12VerifyMac", crtn
);
867 printf("***MAC verify failure.\n");
870 printf("MAC verifies OK.\n");
879 CSSM_CSP_HANDLE cspHand
,
885 P12Parsed
parsed(coder
);
886 P12ParseInfo
pinfo(coder
,
890 NULL
, // no separate pwd
893 printf("PKCS12 PFX:\n");
894 int rtn
= p12Parse(rawBlob
, pinfo
, 3);
898 P12KnownBlobs
&certs
= pinfo
.mParsed
.mCerts
;
899 if(certs
.mNumBlobs
) {
901 for(unsigned dex
=0; dex
<certs
.mNumBlobs
; dex
++) {
902 printf("Cert %u:\n", dex
);
903 printCert(certs
.mBlobs
[dex
].Data
,
904 certs
.mBlobs
[dex
].Length
, CSSM_FALSE
);
908 P12KnownBlobs
&crls
= pinfo
.mParsed
.mCrls
;
911 for(unsigned dex
=0; dex
<crls
.mNumBlobs
; dex
++) {
912 printf("CRL %u:\n", dex
);
913 printCrl(crls
.mBlobs
[dex
].Data
,
914 crls
.mBlobs
[dex
].Length
, CSSM_FALSE
);