/* * grunt-quality p12 parse tool. * * The PFX ripper in this file uses, and always will use, the * app-space reference PBE and crypto routines in p12Crypto.{h,cpp} * and p12pbe.{h,cpp} in this directory. */ #include "SecNssCoder.h" #include #include #include #include #include #include #include #include "p12Crypto.h" #include #include "pkcs12Parsed.h" #include #include #include #include #include #include /* * The stuff which gets passed around to all parse modules */ class P12ParseInfo { public: P12ParseInfo(SecNssCoder &coder, CSSM_CSP_HANDLE cspHand, OidParser &parser, /* NULL means don't verify MAC, don't decrypt */ CFStringRef macPwd, /* if this second pwd is absent, use macPwd for both */ CFStringRef encrPwd, P12Parsed &parsed) // destination : mCoder(coder), mCspHand(cspHand), mParser(parser), mParsed(parsed) { pwdToUcode(macPwd, mPwd); pwdToUcode(encrPwd, mEncrPwd); } ~P12ParseInfo() {} void pwdToUcode(CFStringRef str, CSSM_DATA &pwd); SecNssCoder &mCoder; CSSM_CSP_HANDLE mCspHand; OidParser &mParser; CSSM_DATA mPwd; // unicode, double null terminated CSSM_DATA mEncrPwd; P12Parsed &mParsed; // destination }; void P12ParseInfo::pwdToUcode( CFStringRef str, CSSM_DATA &pwd) { if(str == NULL) { pwd.Data = NULL; pwd.Length = 0; return; } CFIndex len = CFStringGetLength(str); mCoder.allocItem(pwd, (len * sizeof(UniChar)) + 2); uint8 *cp = pwd.Data; for(CFIndex dex=0; dex> 8; *cp++ = uc & 0xff; } *cp++ = 0; *cp++ = 0; } static void doIndent(unsigned depth) { for(unsigned i=0; iLength; uint8 *cp = d->Data; if((maxToPrint != 0) && (len > maxToPrint)) { len = maxToPrint; more = true; } for(i=0; i sizeof(uint32)) { printf("***Bad formatting for DER integer\n"); len = sizeof(uint32); } uint32 rtn = 0; uint8 *cp = cdata.Data; for(uint32 i=0; iiterations); /* go */ CSSM_RETURN crtn = p12Decrypt_app(pinfo.mCspHand, edata.contentInfo.encrContent, keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, padding, mode, iterCount, pbep->salt, pinfo.mPwd, pinfo.mCoder, ptext); #if WRITE_DECRYPT_TEXT if(crtn == 0) { char fname[100]; sprintf(fname, "decrypt%d.der", ctr++); writeFile(fname, ptext.Data, ptext.Length); printf("...wrote %u bytes to %s\n", (unsigned)ptext.Length, fname); } #endif return crtn ? 1 : 0; } /* * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12. * Decode the alg params as a NSS_P12_PBE_Params and parse and * return the result if the pbeParams is non-NULL. */ static int p12AlgIdParse( const CSSM_X509_ALGORITHM_IDENTIFIER &algId, NSS_P12_PBE_Params *pbeParams, // optional P12ParseInfo &pinfo, unsigned depth) // print indent depth { doIndent(depth); printf("encrAlg = %s\n", oidStr(algId.algorithm, pinfo.mParser)); const CSSM_DATA ¶m = algId.parameters; if(pbeParams == NULL) { /* alg params are uninterpreted */ doIndent(depth); printf("Alg Params : "); printDataAsHex(¶m); return 0; } if(param.Length == 0) { printf("===warning: no alg parameters, this is not optional\n"); return 0; } memset(pbeParams, 0, sizeof(*pbeParams)); if(pinfo.mCoder.decodeItem(param, NSS_P12_PBE_ParamsTemplate, pbeParams)) { printf("***Error decoding NSS_P12_PBE_Params\n"); return 1; } doIndent(depth); printf("Salt : "); printDataAsHex(&pbeParams->salt); doIndent(depth); if(pbeParams->iterations.Length > 4) { printf("warning: iterations greater than max int\n"); doIndent(depth); printf("Iterations : "); printDataAsHex(&pbeParams->iterations); } else { printf("Iterations : %u\n", (unsigned)dataToInt(pbeParams->iterations)); } return 0; } /* * Parse a NSS_P7_EncryptedData - specifically in the context * of a P12 in password privacy mode. (The latter assumption is * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters * format). */ static int encryptedDataParse( const NSS_P7_EncryptedData &edata, P12ParseInfo &pinfo, NSS_P12_PBE_Params *pbep, // optional, RETURNED unsigned depth) // print indent depth { doIndent(depth); printf("version = %u\n", (unsigned)dataToInt(edata.version)); const NSS_P7_EncrContentInfo &ci = edata.contentInfo; doIndent(depth); printf("contentType = %s\n", oidStr(ci.contentType, pinfo.mParser)); /* * Parse the alg ID, safe PBE params for when we do the * key unwrap */ const CSSM_X509_ALGORITHM_IDENTIFIER &algId = ci.encrAlg; if(p12AlgIdParse(algId, pbep, pinfo, depth)) { return 1; } doIndent(depth); printf("encrContent : "); printDataAsHex(&ci.encrContent, 12); return 0; } static int attrParse( const NSS_Attribute *attr, P12ParseInfo &pinfo, unsigned depth) { doIndent(depth); printf("attrType : %s\n", oidStr(attr->attrType, pinfo.mParser)); unsigned numVals = nssArraySize((const void **)attr->attrValue); doIndent(depth); printf("numValues = %u\n", numVals); for(unsigned dex=0; dexattrType, &CSSMOID_PKCS9_FriendlyName)) { /* BMP string (UniCode) */ CSSM_DATA ustr; if(pinfo.mCoder.decodeItem(*attr->attrValue[dex], kSecAsn1BMPStringTemplate, &ustr)) { printf("***Error decoding BMP string\n"); continue; } printDataAsUnichars(ustr); } else if(nssCompareCssmData(&attr->attrType, &CSSMOID_PKCS9_LocalKeyId)) { /* Octet string */ CSSM_DATA ostr; if(pinfo.mCoder.decodeItem(*attr->attrValue[dex], kSecAsn1ObjectIDTemplate, &ostr)) { printf("***Error decoding LocalKeyId string\n"); continue; } printDataAsHex(&ostr, 16); } else { printDataAsHex(attr->attrValue[dex], 8); } } return 0; } /* * ShroudedKeyBag parser w/decrypt */ static int shroudedKeyBagParse( const NSS_P12_ShroudedKeyBag *keyBag, P12ParseInfo &pinfo, unsigned depth) { const CSSM_X509_ALGORITHM_IDENTIFIER &algId = keyBag->algorithm; NSS_P12_PBE_Params pbep; if(p12AlgIdParse(algId, &pbep, pinfo, depth)) { return 1; } if(pinfo.mPwd.Data == NULL) { doIndent(depth); printf("=== Key not decrypted (no passphrase)===\n"); return 0; } /* * Prepare for decryption */ CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 uint32 keySizeInBits; uint32 blockSizeInBytes; // for IV, optional CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. #if IMPORT_EXPORT_COMPLETE PKCS_Which pkcs; bool found = pkcsOidToParams(&algId.algorithm, keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, padding, mode, pkcs); #else bool found = pkcsOidToParams(&algId.algorithm, keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, padding, mode); #endif if(!found) { printf("***ShroudedKeyBag encrAlg not understood\n"); return 1; } unsigned iterCount = dataToInt(pbep.iterations); CSSM_DATA berPrivKey; /* decrypt, result is BER encoded private key */ CSSM_RETURN crtn = p12Decrypt_app(pinfo.mCspHand, keyBag->encryptedData, keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, padding, mode, iterCount, pbep.salt, pinfo.mPwd, pinfo.mCoder, berPrivKey); if(crtn) { doIndent(depth); printf("***Error decrypting private key\n"); return 1; } /* decode */ NSS_PrivateKeyInfo privKey; memset(&privKey, 0, sizeof(privKey)); if(pinfo.mCoder.decodeItem(berPrivKey, kSecAsn1PrivateKeyInfoTemplate, &privKey)) { doIndent(depth); printf("***Error decoding decrypted private key\n"); return 1; } /* * in P12 library, we'd convert the result into a CSSM_KEY * or a SecItem... */ CSSM_X509_ALGORITHM_IDENTIFIER &privAlg = privKey.algorithm; doIndent(depth); printf("Priv Key Alg : %s\n", oidStr(privAlg.algorithm, pinfo.mParser)); doIndent(depth); printf("Priv Key Blob : "); printDataAsHex(&privKey.privateKey, 16); unsigned numAttrs = nssArraySize((const void**)privKey.attributes); if(numAttrs) { doIndent(depth+3); printf("numAttrs = %u\n", numAttrs); for(unsigned i=0; itype) { case CT_X509: doIndent(depth); printf("X509 cert found, size %u\n", (unsigned)certBag->certValue.Length); pinfo.mParsed.mCerts.addBlob(certBag->certValue); break; default: doIndent(depth); printf("Unknown cert type found\n"); P12UnknownBlob *uk = new P12UnknownBlob(certBag->certValue, certBag->bagType); pinfo.mParsed.mUnknown.addBlob(uk); } return 0; } /* * CrlBag parser */ static int crlBagParse( const NSS_P12_CrlBag *crlBag, P12ParseInfo &pinfo, unsigned depth) { /* fixe - we really need to store the attrs along with the crl here! */ switch(crlBag->type) { case CRT_X509: doIndent(depth); printf("X509 CRL found, size %u\n", (unsigned)crlBag->crlValue.Length); pinfo.mParsed.mCrls.addBlob(crlBag->crlValue); break; default: doIndent(depth); printf("Unknown CRL type found\n"); P12UnknownBlob *uk = new P12UnknownBlob(crlBag->crlValue, crlBag->bagType); pinfo.mParsed.mUnknown.addBlob(uk); } return 0; } /* * Parse an encoded NSS_P12_SafeContents. This could be either * present as plaintext in an AuthSafe or decrypted. */ static int safeContentsParse( const CSSM_DATA &contentsBlob, P12ParseInfo &pinfo, unsigned depth) // print indent depth { NSS_P12_SafeContents sc; memset(&sc, 0, sizeof(sc)); if(pinfo.mCoder.decodeItem(contentsBlob, NSS_P12_SafeContentsTemplate, &sc)) { printf("***Error decoding SafeContents\n"); return 1; } unsigned numBags = nssArraySize((const void **)sc.bags); doIndent(depth); printf("SafeContents num bags %u\n", numBags); int rtn = 0; for(unsigned dex=0; dexbagId, pinfo.mParser)); doIndent(depth+3); printf("type = %s\n", p12BagTypeStr(bag->type)); unsigned numAttrs = nssArraySize((const void**)bag->bagAttrs); if(numAttrs) { doIndent(depth+3); printf("numAttrs = %u\n", numAttrs); for(unsigned i=0; ibagAttrs[i], pinfo, depth+6); } } /* * Now break out to individual bag type * * This hacked line breaks when we have a real key bag defined */ unsigned defaultLen = (unsigned)bag->bagValue.keyBag->Length; switch(bag->type) { case BT_KeyBag: doIndent(depth+3); printf("KeyBag: size %u\n", defaultLen); break; case BT_ShroudedKeyBag: doIndent(depth+3); printf("ShroudedKeyBag:\n"); rtn = shroudedKeyBagParse(bag->bagValue.shroudedKeyBag, pinfo, depth+6); break; case BT_CertBag: doIndent(depth+3); printf("CertBag:\n"); rtn = certBagParse(bag->bagValue.certBag, pinfo, depth+6); break; case BT_CrlBag: doIndent(depth+3); printf("CrlBag:\n"); rtn = crlBagParse(bag->bagValue.crlBag, pinfo, depth+6); break; case BT_SecretBag: doIndent(depth+3); printf("SecretBag: size %u\n", defaultLen); break; case BT_SafeContentsBag: doIndent(depth+3); printf("SafeContentsBag: size %u\n", defaultLen); break; default: doIndent(depth+3); printf("===Warning: unknownBagType (%u)\n", (unsigned)bag->type); break; } if(rtn) { break; } } return rtn; } /* * Parse a ContentInfo in the context of (i.e., as an element of) * an element in a AuthenticatedSafe */ static int authSafeElementParse( const NSS_P7_DecodedContentInfo *info, P12ParseInfo &pinfo, unsigned depth) // print indent depth { char oidStr[OID_PARSER_STRING_SIZE]; pinfo.mParser.oidParse(info->contentType.Data, info->contentType.Length, oidStr); doIndent(depth); printf("contentType = %s\n", oidStr); doIndent(depth); printf("type = %s\n", p7ContentInfoTypeStr(info->type)); int rtn = 0; switch(info->type) { case CT_Data: /* unencrypted SafeContents */ doIndent(depth); printf("raw size: %u\n", (unsigned)info->content.data->Length); doIndent(depth); printf("Plaintext SafeContents:\n"); rtn = safeContentsParse(*info->content.data, pinfo, depth+3); break; case CT_EncryptedData: { doIndent(depth); printf("EncryptedData:\n"); NSS_P12_PBE_Params pbep; rtn = encryptedDataParse(*info->content.encryptData, pinfo, &pbep, depth+3); if(rtn) { break; } if(pinfo.mPwd.Data == NULL) { doIndent(depth+3); printf("=== Contents not decrypted (no passphrase)===\n"); } else { /* * Decrypt contents to get a SafeContents and * then parse that. */ CSSM_DATA ptext = {0, NULL}; rtn = encryptedDataDecrypt(*info->content.encryptData, pinfo, &pbep, ptext); doIndent(depth); if(rtn) { printf("***Error decrypting CT_EncryptedData\n"); break; } printf("Decrypted SafeContents {\n"); rtn = safeContentsParse(ptext, pinfo, depth+3); doIndent(depth); printf("}\n"); } break; } default: /* the rest map to an ASN_ANY/CSSM_DATA for now */ doIndent(depth+3); printf("size of %u is all we know today\n", (unsigned)info->content.data->Length); rtn = 0; break; } return rtn; } /* * Parse an encoded NSS_P12_AuthenticatedSafe */ static int authSafeParse( const CSSM_DATA authSafeBlob, P12ParseInfo &pinfo, unsigned depth) // print indent depth { NSS_P12_AuthenticatedSafe authSafe; memset(&authSafe, 0, sizeof(authSafe)); if(pinfo.mCoder.decodeItem(authSafeBlob, NSS_P12_AuthenticatedSafeTemplate, &authSafe)) { printf("***Error decoding authSafe\n"); return 1; } unsigned numInfos = nssArraySize((const void **)authSafe.info); doIndent(depth); printf("authSafe numInfos %u\n", numInfos); int rtn = 0; for(unsigned dex=0; dex 4) { doIndent(depth); printf("***Warning: malformed iteraton length (%u)\n", (unsigned)iter.Length); } unsigned i = dataToInt(iter); doIndent(depth); printf("Iterations = %u\n", i); return 0; } static int p12Parse( const CSSM_DATA &rawBlob, P12ParseInfo &pinfo, unsigned depth) // print indent depth { NSS_P12_DecodedPFX pfx; memset(&pfx, 0, sizeof(pfx)); if(pinfo.mCoder.decodeItem(rawBlob, NSS_P12_DecodedPFXTemplate, &pfx)) { printf("***Error on top-level decode of NSS_P12_DecodedPFX\n"); return 1; } doIndent(depth); printf("version = %u\n", (unsigned)dataToInt(pfx.version)); NSS_P7_DecodedContentInfo &dci = pfx.authSafe; doIndent(depth); printf("contentType = %s\n", oidStr(dci.contentType, pinfo.mParser)); doIndent(depth); printf("type = %s\n", p7ContentInfoTypeStr(dci.type)); int rtn = 0; if(nssCompareCssmData(&dci.contentType, &CSSMOID_PKCS7_Data)) { doIndent(depth); printf("AuthenticatedSafe Length %u {\n", (unsigned)dci.content.data->Length); rtn = authSafeParse(*dci.content.data, pinfo, depth+3); doIndent(depth); printf("}\n"); } else { printf("Not parsing any other content type today.\n"); } if(pfx.macData) { doIndent(depth); printf("Mac Data {\n"); p12MacParse(*pfx.macData, pinfo, depth+3); doIndent(depth); printf("}\n"); if(pinfo.mPwd.Data == NULL) { doIndent(depth); printf("=== MAC not verified (no passphrase)===\n"); } else { CSSM_RETURN crtn = p12VerifyMac_app(pfx, pinfo.mCspHand, pinfo.mPwd, pinfo.mCoder); doIndent(depth); if(crtn) { cssmPerror("p12VerifyMac", crtn); doIndent(depth); printf("***MAC verify failure.\n"); } else { printf("MAC verifies OK.\n"); } } } return 0; } int p12ParseTop( CSSM_DATA &rawBlob, CSSM_CSP_HANDLE cspHand, CFStringRef pwd, bool verbose) { SecNssCoder coder; OidParser parser; P12Parsed parsed(coder); P12ParseInfo pinfo(coder, cspHand, parser, pwd, NULL, // no separate pwd parsed); printf("PKCS12 PFX:\n"); int rtn = p12Parse(rawBlob, pinfo, 3); /* find anything? */ if(verbose) { P12KnownBlobs &certs = pinfo.mParsed.mCerts; if(certs.mNumBlobs) { printf("\n\n"); for(unsigned dex=0; dex