X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/ocspTool/ocspTool.cpp diff --git a/SecurityTests/clxutils/ocspTool/ocspTool.cpp b/SecurityTests/clxutils/ocspTool/ocspTool.cpp new file mode 100644 index 00000000..153cdaa4 --- /dev/null +++ b/SecurityTests/clxutils/ocspTool/ocspTool.cpp @@ -0,0 +1,1254 @@ +/* + * ocspTool - simple OCSP request/response generator and parser + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ocspUtils.h" +#include "ocspRequest.h" +#include "ocspNetwork.h" +#include "findOcspUrl.h" + +static void usage(char **argv) +{ + printf("Usage: %s cmd [option...]\n", argv[0]); + printf("cmds:\n"); + printf(" g generate request\n"); + printf(" G parse request\n"); + printf(" r generate reply\n"); + printf(" R parse reply\n"); + printf(" p generate OCSP request, post, get reply (use -p and/or -o)\n"); + printf("Options\n"); + printf(" -c cert_file -- for generating request\n"); + printf(" -C issuer_cert_file -- for generating request\n"); + printf(" -i in_file -- for parsing\n"); + printf(" -o out_file -- for generating\n"); + printf(" -s status -- cert status: g(ood)|r(evoked)|u(nknown)\n"); + printf(" -r crlReason -- integer 0..8\n"); + printf(" -k keychain -- keychain containing signing cert\n"); + printf(" -p -- parse reply from post op\n"); + printf(" -u responderURI -- OCSP responder here, not from cert's AIA extension\n"); + printf(" -v -- verbose; e.g., print certs\n"); + exit(1); +} + +void doIndent(int indent) +{ + for(int dex=0; dexData; + for(i=0; iLength; i++) { + printf("%c", *cp++); + } + printf("\n"); +} + +static void printDataAsHex( + const CSSM_DATA *d, + unsigned maxToPrint = 0) // optional, 0 means print it all +{ + unsigned i; + bool more = false; + uint32 len = d->Length; + uint8 *cp = d->Data; + + if((maxToPrint != 0) && (len > maxToPrint)) { + len = maxToPrint; + more = true; + } + for(i=0; iatvs); + for(unsigned atvDex=0; atvDexatvs[atvDex]; + char buf[OID_PARSER_STRING_SIZE]; + parser.oidParse(atv->type.Data, atv->type.Length, buf); + doIndent(indent); + printf("%s : ", buf); + printTaggedItem(atv->value); + } + } +} + +static uint8 nullParam[2] = {5, 0}; + +/* + * Given the following, create a ResponseData (to be signed by caller). + * + * cert status (CS_{Good,Revoked,Unknown}) + * cert being verified + * issuer cert + * this update time + * next update time (optional) + * nonce (optional) + */ +static int genTbsResp( + SecAsn1CoderRef coder, // result in this coder's address space + CSSM_CL_HANDLE clHand, + SecAsn1OCSPCertStatusTag status, + CE_CrlReason reason, // for CS_Revoked + const CSSM_DATA &subjectCert, + const CSSM_DATA &issuerCert, + unsigned thisUpdate, // required, seconds from now + unsigned nextUpdate, // optional, seconds from now, 0 ==> skip + const CSSM_DATA *nonce, // optional + CSSM_DATA &encodedTbs) // allocated in coder space and RETURNED +{ + char *nextUpdStr = NULL; + CSSM_DATA nextUpdateData; + char *thisUpdStr = NULL; + CSSM_DATA *thisUpdateData; + SecAsn1OCSPResponseData responseData; + OCSPNonce *nonceExt = NULL; + char *producedAt = NULL; + SecAsn1OCSPSingleResponse singleResp; + SecAsn1OCSPSingleResponse *respArray[2] = {&singleResp, NULL}; + SecAsn1OCSPResponderID responderID; + NSS_CertExtension *extenArray[2] = {NULL, NULL}; + + /* SingleResponse */ + memset(&singleResp, 0, sizeof(singleResp)); + + /* SingleResponse.CertID */ + SecAsn1OCSPCertID &certId = singleResp.certID; + CertParser parser(clHand); + CertParser issuerParser(clHand); + CSSM_RETURN crtn = parser.initWithData(subjectCert); + if(crtn) { + cssmPerror("CertParser.initWithData for subject cert", crtn); + return -1; + } + crtn = issuerParser.initWithData(issuerCert); + if(crtn) { + cssmPerror("CertParser.initWithData for issuer", crtn); + return -1; + } + + /* algId refers to the hash we'll perform in issuer name and key */ + certId.algId.algorithm = CSSMOID_SHA1; + certId.algId.parameters.Data = nullParam; + certId.algId.parameters.Length = sizeof(nullParam); + + /* SHA1(issuerName) */ + CSSM_DATA issuerName = {0, NULL}; + issuerName.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1IssuerNameStd, + issuerName.Length); + if(issuerName.Data == NULL) { + printf("***Error fetching issuer name. Aborting.\n"); + return 1; + } + uint8 issuerNameHash[CC_SHA1_DIGEST_LENGTH]; + ocspdSha1(issuerName.Data, issuerName.Length, issuerNameHash); + + /* SHA1(issuer public key) */ + CSSM_KEY_PTR pubKey = NULL; + CSSM_SIZE pubKeyLen = sizeof(CSSM_KEY); + pubKey = (CSSM_KEY_PTR)issuerParser.fieldForOid(CSSMOID_CSSMKeyStruct, pubKeyLen); + if(pubKey == NULL) { + printf("***Error fetching public key from issuer cert. Aborting.\n"); + return 1; + } + uint8 pubKeyHash[CC_SHA1_DIGEST_LENGTH]; + ocspdSha1(pubKey->KeyData.Data, pubKey->KeyData.Length, pubKeyHash); + + /* serial number */ + CSSM_DATA serialNum = {0, NULL}; + serialNum.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1SerialNumber, + serialNum.Length); + if(serialNum.Data == NULL) { + printf("***Error fetching serial number. Aborting.\n"); + return 1; + } + + /* build the CertID from those components */ + certId.issuerNameHash.Data = issuerNameHash; + certId.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH; + certId.issuerPubKeyHash.Data = pubKeyHash; + certId.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH; + certId.serialNumber = serialNum; + + /* SingleResponse.CertStatus - to be encoded on its own */ + SecAsn1OCSPCertStatus certStatus; + memset(&certStatus, 0, sizeof(certStatus)); + SecAsn1OCSPRevokedInfo revokedInfo; + char *revokedAt = NULL; + CSSM_DATA reasonData; + OSStatus ortn; + + if(status == CS_Revoked) { + /* cook up SecAsn1OCSPRevokedInfo */ + certStatus.revokedInfo = &revokedInfo; + revokedAt = appTimeAtNowPlus(-3600, TIME_GEN); + revokedInfo.revocationTime.Data = (uint8 *)revokedAt; + revokedInfo.revocationTime.Length = strlen(revokedAt); + uint8 theReason = reason; + reasonData.Data = &theReason; + reasonData.Length = 1; + revokedInfo.revocationReason = &reasonData; + ortn = SecAsn1EncodeItem(coder, &certStatus, + kSecAsn1OCSPCertStatusRevokedTemplate, + &singleResp.certStatus); + } + else { + ortn = SecAsn1EncodeItem(coder, &certStatus, + kSecAsn1OCSPCertStatusGoodTemplate, + &singleResp.certStatus); + } + if(ortn) { + printf("***Error encoding certStatus\n"); + goto errOut; + } + + /* SingleResponse.thisUpdate */ + thisUpdStr = appTimeAtNowPlus(thisUpdate, TIME_GEN); + thisUpdateData = &singleResp.thisUpdate; + thisUpdateData->Data = (uint8 *)thisUpdStr; + thisUpdateData->Length = strlen(thisUpdStr); + + /* SingleResponse.nextUpdate, optional */ + if(nextUpdate) { + nextUpdStr = appTimeAtNowPlus(nextUpdate, TIME_GEN); + nextUpdateData.Data = (uint8 *)nextUpdStr; + nextUpdateData.Length = strlen(nextUpdStr); + singleResp.nextUpdate = &nextUpdateData; + } + + /* Single Extensions - none for now */ + + /* Now up to ResponseData */ + memset(&responseData, 0, sizeof(responseData)); + + /* skip version */ + + /* + * ResponseData.responderID: KeyHash (of signer); we already got this for CertID. + * WE have to encode this one separately and drop it in as an ASN_ANY. + */ + responderID.byKey = certId.issuerPubKeyHash; + ortn = SecAsn1EncodeItem(coder, &responderID, + kSecAsn1OCSPResponderIDAsKeyTemplate, + &responseData.responderID); + if(ortn) { + printf("***Error encoding responderID\n"); + goto errOut; + } + + /* ResponseData.producedAt = now */ + producedAt = appTimeAtNowPlus(0, TIME_GEN); + responseData.producedAt.Data = (uint8 *)producedAt; + responseData.producedAt.Length = strlen(producedAt); + + /* ResponseData.responses - one of 'em */ + responseData.responses = respArray; + + /* ResponseData.responseExtensions - optionally one, nonce */ + if(nonce) { + nonceExt = new OCSPNonce(coder, false, *nonce); + extenArray[0] = nonceExt->nssExt(); + responseData.responseExtensions = extenArray; + } + else { + responseData.responseExtensions = NULL; + } + + /* encode it */ + encodedTbs.Data = NULL; + encodedTbs.Length = 0; + ortn = SecAsn1EncodeItem(coder, &responseData, + kSecAsn1OCSPResponseDataTemplate, + &encodedTbs); + if(ortn) { + printf("***Error encoding SecAsn1OCSPResponseData\n"); + } +errOut: + /* free resources */ + if(revokedAt) { + CSSM_FREE(revokedAt); + } + if(thisUpdStr) { + CSSM_FREE(thisUpdStr); + } + if(nextUpdStr) { + CSSM_FREE(nextUpdStr); + } + if(nonceExt) { + delete nonceExt; + } + return ortn; +} + +static int genOcspReq( + CSSM_CL_HANDLE clHand, + const unsigned char *certFile, + unsigned certFileLen, + const unsigned char *issuerCertFile, + unsigned issuerCertFileLen, + unsigned char **outFile, // RETURNED + unsigned *outFileLen) // RETURNED +{ + CertParser parser(clHand); + CertParser issuerParser(clHand); + CSSM_DATA certData = {certFileLen, (uint8 *)certFile}; + CSSM_RETURN crtn; + crtn = parser.initWithData(certData); + if(crtn) { + cssmPerror("CertParser.initWithData for subject cert", crtn); + return -1; + } + certData.Data = (uint8 *)issuerCertFile; + certData.Length = issuerCertFileLen; + crtn = issuerParser.initWithData(certData); + if(crtn) { + cssmPerror("CertParser.initWithData for issuer", crtn); + return -1; + } + + /* + * One single request, no extensions + */ + SecAsn1OCSPRequest singleReq; + memset(&singleReq, 0, sizeof(singleReq)); + SecAsn1OCSPCertID &certId = singleReq.reqCert; + + /* algId refers to the hash we'll perform in issuer name and key */ + certId.algId.algorithm = CSSMOID_SHA1; + certId.algId.parameters.Data = nullParam; + certId.algId.parameters.Length = sizeof(nullParam); + + /* SHA1(issuerName) */ + CSSM_DATA issuerName = {0, NULL}; + issuerName.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1IssuerNameStd, + issuerName.Length); + if(issuerName.Data == NULL) { + printf("***Error fetching issuer name. Aborting.\n"); + return 1; + } + uint8 issuerNameHash[CC_SHA1_DIGEST_LENGTH]; + ocspdSha1(issuerName.Data, issuerName.Length, issuerNameHash); + + /* SHA1(issuer public key) */ + CSSM_KEY_PTR pubKey = NULL; + CSSM_SIZE pubKeyLen = sizeof(CSSM_KEY); + pubKey = (CSSM_KEY_PTR)issuerParser.fieldForOid(CSSMOID_CSSMKeyStruct, pubKeyLen); + if(pubKey == NULL) { + printf("***Error fetching public key from issuer cert. Aborting.\n"); + return 1; + } + uint8 pubKeyHash[CC_SHA1_DIGEST_LENGTH]; + ocspdSha1(pubKey->KeyData.Data, pubKey->KeyData.Length, pubKeyHash); + + /* serial number */ + CSSM_DATA serialNum = {0, NULL}; + serialNum.Data = (uint8 *)parser.fieldForOid(CSSMOID_X509V1SerialNumber, + serialNum.Length); + if(serialNum.Data == NULL) { + printf("***Error fetching serial number. Aborting.\n"); + return 1; + } + + /* build the CertID from those components */ + certId.issuerNameHash.Data = issuerNameHash; + certId.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH; + certId.issuerPubKeyHash.Data = pubKeyHash; + certId.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH; + certId.serialNumber = serialNum; + + /* + * Build top level request with one entry in requestList, no signature, + * one extension (a nonce) + */ + SecAsn1OCSPSignedRequest signedReq; + SecAsn1OCSPRequest *reqArray[2] = { &singleReq, NULL }; + SecAsn1OCSPTbsRequest &tbs = signedReq.tbsRequest; + memset(&signedReq, 0, sizeof(signedReq)); + uint8 version = 0; + CSSM_DATA vers = {1, &version}; + tbs.version = &vers; + tbs.requestList = reqArray; + + /* one extension - the nonce */ + SecAsn1CoderRef coder; + SecAsn1CoderCreate(&coder); + uint8 nonceBytes[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; + CSSM_DATA nonceData = {8, nonceBytes}; + OCSPNonce *nonce = new OCSPNonce(coder, false, nonceData); + NSS_CertExtension *extenArray[2] = {nonce->nssExt(), NULL}; + tbs.requestExtensions = extenArray; + + /* Encode */ + OSStatus ortn; + CSSM_DATA encoded = {0, NULL}; + ortn = SecAsn1EncodeItem(coder, &signedReq, kSecAsn1OCSPSignedRequestTemplate, + &encoded); + if(ortn) { + printf("***Error encoding SecAsn1OCSPSignedRequest\n"); + } + else { + *outFile = (unsigned char *)malloc(encoded.Length); + *outFileLen = encoded.Length; + memmove(*outFile, encoded.Data, encoded.Length); + } + SecAsn1CoderRelease(coder); + return (int)ortn; +} + +static void dumpCertID( + SecAsn1OCSPCertID *certID, + int indent) +{ + doIndent(indent); + printf("algId : "); + printDataAsHex(&certID->algId.algorithm); + + doIndent(indent); + printf("issuerNameHash : "); + printDataAsHex(&certID->issuerNameHash); + + doIndent(indent); + printf("issuerPubKeyHash : "); + printDataAsHex(&certID->issuerPubKeyHash); + + doIndent(indent); + printf("serialNumber : "); + printDataAsHex(&certID->serialNumber); +} + +static void printCritical( + int indent, + OCSPExtension *ocspExt) +{ + doIndent(indent); + printf("Critical : %s\n", ocspExt->critical() ? "true" : "false"); +} + +static void printOcspExt( + SecAsn1CoderRef coder, + NSS_CertExtension *nssExt, + int indent) +{ + OCSPExtension *ocspExt = NULL; + try { + ocspExt = OCSPExtension::createFromNSS(coder, *nssExt); + } + catch(...) { + doIndent(indent); + printf("***Error thrown parsing extension\n"); + return; + } + switch(ocspExt->tag()) { + case OET_Unknown: + doIndent(indent); + printf("Extension type: Unknown\n"); + printCritical(indent, ocspExt); + return; + case OET_Nonce: + { + doIndent(indent); + printf("Extension type : Nonce\n"); + printCritical(indent, ocspExt); + doIndent(indent); + OCSPNonce *nonce = dynamic_cast(ocspExt); + if(nonce == NULL) { + printf("***dynamic_cast failure in OCSPNonce!\n"); + return; + } + printf("nonce value : "); + printDataAsHex(&nonce->nonce()); + break; + } + case OET_CrlReference: + doIndent(indent); + printf("Extension type : CrlReference"); + printCritical(indent, ocspExt); + /* TBD */ + return; + case OET_AcceptResponse: + doIndent(indent); + printf("Extension type : AcceptResponse"); + printCritical(indent, ocspExt); + /* TBD */ + return; + case OET_ArchiveCutoff: + doIndent(indent); + printf("Extension type : ArchiveCutoff"); + printCritical(indent, ocspExt); + /* TBD */ + return; + case OET_ServiceLocator: + doIndent(indent); + printf("Extension type : ServiceLocator"); + printCritical(indent, ocspExt); + /* TBD */ + return; + default: + /* this code is out of sync with ocspExtensions.{h,cpp} */ + doIndent(indent); + printf("Extension type : unrecognized - code sync error"); + printCritical(indent, ocspExt); + return; + + } +} + +static int parseOcspReq( + CSSM_CL_HANDLE clHand, + unsigned char *inFile, + unsigned inFileLen, + bool verbose) +{ + SecAsn1CoderRef coder; + SecAsn1OCSPSignedRequest signedReq; + SecAsn1OCSPTbsRequest &tbs = signedReq.tbsRequest; + OSStatus ortn; + int indent; + unsigned numExts; + unsigned numReqs; + + SecAsn1CoderCreate(&coder); + memset(&signedReq, 0, sizeof(signedReq)); + + ortn = SecAsn1Decode(coder, inFile, inFileLen, kSecAsn1OCSPSignedRequestTemplate, + &signedReq); + if(ortn) { + printf("***Error decoding SecAsn1OCSPSignedRequest\n"); + goto errOut; + } + printf("SecAsn1OCSPSignedRequest:\n"); + + printf("SecAsn1OCSPTbsRequest:\n"); + indent = 2; + if(tbs.version) { + doIndent(indent); + printf("Version : "); + printDataAsHex(tbs.version); + } + if(tbs.requestorName) { + doIndent(indent); + printf("NSS_GeneralName found; print it later maybe\n"); + } + numReqs = ocspdArraySize((const void **)tbs.requestList); + for(unsigned dex=0; dexreqCert; + dumpCertID(certID, indent); + indent -= 2; + numExts = ocspdArraySize((const void **)req->extensions); + for(unsigned extDex=0; extDexextensions[dex], indent + 2); + } + indent -= 2; + } + + numExts = ocspdArraySize((const void **)tbs.requestExtensions); + for(unsigned extDex=0; extDexData) { + doIndent(indent); + printf("version: %u\n", respData.version->Data[0]); + } + doIndent(indent); + printf("ResponderID:\n"); + indent += 2; + memset(&responderID, 0, sizeof(responderID)); + if(respData.responderID.Data == NULL) { + doIndent(indent); + printf("***Malformed(empty)***\n"); + return 1; + } + + /* lame-o choice processing */ + tag = respData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK; + switch(tag) { + case RIT_Name: + templ = kSecAsn1OCSPResponderIDAsNameTemplate; + break; + case RIT_Key: + templ = kSecAsn1OCSPResponderIDAsKeyTemplate; + break; + default: + doIndent(indent); + printf("**Unknown tag for ResponderID (%u)\n", tag); + return 1; + } + ortn = SecAsn1DecodeData(coder, &respData.responderID, templ, &responderID); + if(ortn) { + doIndent(indent); + printf("***Error decoding ResponderID\n"); + return 1; + } + doIndent(indent); + switch(tag) { + case RIT_Name: + printf("byName:\n"); + printName((NSS_Name &)responderID.byName, indent + 2); + break; + case RIT_Key: + printf("byKey : "); + printDataAsHex(&responderID.byKey); + break; + } + indent -= 2; // end of ResponderID + + doIndent(indent); + printf("producedAt: "); + printString(&respData.producedAt); + unsigned numResps = ocspdArraySize((const void **)respData.responses); + doIndent(indent); + printf("Num responses: %u\n", numResps); + for(unsigned dex=0; dexcertID, indent + 2); + + doIndent(indent); + printf("certStatus: "); + /* lame-o choice processing */ + tag = resp->certStatus.Data[0] & SEC_ASN1_TAGNUM_MASK; + switch(tag) { + case CS_Good: + printf("Good\n"); + break; + case CS_Unknown: + printf("Unknown\n"); + break; + default: + printf("**MALFORMED cert status tag (%u)\n", tag); + break; + case CS_Revoked: + { + printf("Revoked\n"); + doIndent(indent); + SecAsn1OCSPCertStatus certStatus; + memset(&certStatus, 0, sizeof(certStatus)); + ortn = SecAsn1DecodeData(coder, &resp->certStatus, + kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus); + if(ortn) { + doIndent(indent); + printf("***error parsing RevokedInfo\n"); + break; + } + if(certStatus.revokedInfo == NULL) { + doIndent(indent); + printf("***GAK! Malformed (empty) revokedInfo\n");break; + } + printf("RevokedIndfo:\n"); + indent += 2; + doIndent(indent); + printf("revocationTime: "); + printString(&certStatus.revokedInfo->revocationTime); + if(certStatus.revokedInfo->revocationReason) { + doIndent(indent); + printf("reason: %u\n", + certStatus.revokedInfo->revocationReason->Data[0]); + } + indent -= 2; // end of RevokedInfo + break; + } + } /* switch cert status tag */ + + doIndent(indent); + printf("thisUpdate: "); + printString(&resp->thisUpdate); + + if(resp->nextUpdate) { + doIndent(indent); + printf("nextUpdate: "); + printString(resp->nextUpdate); + } + + numExts = ocspdArraySize((const void **)resp->singleExtensions); + for(unsigned extDex=0; extDexsingleExtensions[extDex], indent + 2); + } + + indent -= 2; // end of resp[dex] + } + + numExts = ocspdArraySize((const void **)respData.responseExtensions); + for(unsigned extDex=0; extDexresponseType, + &CSSMOID_PKIX_OCSP_BASIC)) { + str = "ocsp-basic"; + } + else { + str = "Unknown type\n"; + } + printf("%s\n", str); + + /* decode the BasicOCSPResponse */ + memset(&basicResp, 0, sizeof(basicResp)); + ortn = SecAsn1DecodeData(coder, &topResp.responseBytes->response, + kSecAsn1OCSPBasicResponseTemplate, &basicResp); + if(ortn) { + printf("***Error decoding BasicOCSPResponse\n"); + goto errOut; + } + + doIndent(indent); + printf("BasicOCSPResponse:\n"); + indent += 2; + doIndent(indent); + printf("ResponseData:\n"); + parseResponseData(coder, indent + 2, basicResp.tbsResponseData); + doIndent(indent); + printf("sig: "); + printDataAsHex(&basicResp.sig, 8); + numCerts = ocspdArraySize((const void **)basicResp.certs); + doIndent(indent); + printf("Num Certs: %u\n", numCerts); + + if(verbose) { + for(unsigned dex=0; dexData, basicResp.certs[dex]->Length, + CSSM_FALSE); + printf("+++++++++++++++++++++++ End Cert %u +++++++++++++++++++++++\n", dex); + } + } + indent -= 2; // end of BasicOCSPResponse + indent -= 2; // end of ResponseBytes + indent -= 2; // end of OCSPResponse +errOut: + SecAsn1CoderRelease(coder); + return ortn; +} + +static int postOcspReq( + CSSM_CL_HANDLE clHand, + const unsigned char *certFile, + unsigned certFileLen, + const unsigned char *issuerCertFile, + unsigned issuerCertFileLen, + const char *responderURI, + bool doParse, + bool verbose, + unsigned char **outFile, // RETURNED + unsigned *outFileLen) // RETURNED +{ + auto_ptr subject; + auto_ptr issuer; + CSSM_DATA uriData = {0, NULL}; + CSSM_DATA *url = NULL; + + try { + CSSM_DATA cdata = {certFileLen, (uint8 *)certFile}; + subject.reset(new CertParser(clHand, cdata)); + } + catch(...) { + printf("***Error parsing subject cert. Aborting.\n"); + return -1; + } + try { + CSSM_DATA cdata = {issuerCertFileLen, (uint8 *)issuerCertFile}; + issuer.reset(new CertParser(clHand, cdata)); + } + catch(...) { + printf("***Error parsing issuer cert. Aborting.\n"); + return -1; + } + + SecAsn1CoderRef coder; + SecAsn1CoderCreate(&coder); + /* subsequent errors to errOut: */ + int ourRtn = 0; + const CSSM_DATA *derReq = NULL; + auto_ptr ocspReq; + + if(responderURI != NULL) { + uriData.Data = (uint8 *)responderURI; + uriData.Length = strlen(responderURI); + url = &uriData; + } + else { + /* get OCSP URL from subject cert */ + url = ocspUrlFromCert(*subject, coder); + if(url == NULL) { + printf("Sorry, no can do.\n"); + ourRtn = -1; + goto errOut; + } + } + + /* create DER-encoded OCSP request for subject */ + try { + ocspReq.reset(new OCSPRequest(*subject, *issuer, false)); + derReq = ocspReq->encode(); + } + catch(...) { + printf("***Error creating OCSP request. Aborting.\n"); + ourRtn = -1; + goto errOut; + } + + /* do it */ + CSSM_DATA ocspResp; + CSSM_RETURN crtn; + crtn = ocspdHttpPost(coder, *url, *derReq, ocspResp); + if(crtn) { + printf("***Error fetching OCSP response***\n"); + cssmPerror("ocspdHttpPost", crtn); + ourRtn = -1; + goto errOut; + } + *outFile = ocspResp.Data; + *outFileLen = ocspResp.Length; + if(doParse) { + parseOcspResp(clHand, ocspResp.Data, ocspResp.Length, verbose); + } + /* copy out */ + *outFile = (unsigned char *)malloc(ocspResp.Length); + *outFileLen = ocspResp.Length; + memmove(*outFile, ocspResp.Data, ocspResp.Length); + +errOut: + SecAsn1CoderRelease(coder); + return ourRtn; +} + +typedef enum { + op_genReq, + op_parseReq, + op_genReply, + op_parseResp, + op_post +} ocspOp; + +int main(int argc, char **argv) +{ + if(argc < 2) { + usage(argv); + } + ocspOp op; + switch(argv[1][0]) { + case 'g': op = op_genReq; break; + case 'G': op = op_parseReq; break; + case 'r': op = op_genReply; break; + case 'R': op = op_parseResp; break; + case 'p': op = op_post; break; + default: usage(argv); + } + + /* user defined vars */ + char *inFile = NULL; + char *outFile = NULL; + char *inCertName = NULL; + char *issuerCertName = NULL; + SecAsn1OCSPCertStatusTag certStatus = CS_Good; + CE_CrlReason crlReason = CE_CR_Unspecified; + char *kcName = NULL; + bool verbose = false; + bool doParse = false; + const char *responderURI = NULL; + + extern int optind; + optind = 2; + extern char *optarg; + int arg; + while ((arg = getopt(argc, argv, "c:C:i:o:s:r:k:phvu:")) != -1) { + switch (arg) { + case 'c': + inCertName = optarg; + break; + case 'C': + issuerCertName = optarg; + break; + case 'i': + inFile = optarg; + break; + case 'o': + outFile = optarg; + break; + case 's': + switch(optarg[0]) { + case 'g': + certStatus = CS_Good; + break; + case 'r': + certStatus = CS_Revoked; + break; + case 'u': + certStatus = CS_Unknown; + break; + default: + printf("***Unrecognized certStatus***\n"); + usage(argv); + } + break; + case 'r': + crlReason = atoi(optarg); + break; + case 'k': + kcName = optarg; + break; + case 'v': + verbose = 1; + break; + case 'p': + doParse = true; + break; + case 'u': + responderURI = optarg; + break; + default: + case '?': + usage(argv); + } + } + if(optind != argc) { + /* this happens if you give getopt() an arg which doesn't start with '-' */ + usage(argv); + } + + unsigned char *certData = NULL; + unsigned certDataLen = 0; + unsigned char *issuerCertData = NULL; + unsigned issuerCertDataLen = 0; + unsigned char *inData = NULL; + unsigned inDataLen = 0; + unsigned char *outData = NULL; + unsigned outDataLen = 0; + SecKeychainRef kcRef = NULL; + OSStatus ortn; + + if(inCertName) { + if(readFile(inCertName, &certData, &certDataLen)) { + printf("***Error reading cert file %s. Aborting.\n", inCertName); + exit(1); + } + } + if(issuerCertName) { + if(readFile(issuerCertName, &issuerCertData, &issuerCertDataLen)) { + printf("***Error reading cert file %s. Aborting.\n", issuerCertName); + exit(1); + } + } + if(inFile) { + if(readFile(inFile, &inData, &inDataLen)) { + printf("***Error reading input file %s. Aborting.\n", inFile); + exit(1); + } + } + if(kcName) { + ortn = SecKeychainOpen(kcName, &kcRef); + if(ortn) { + cssmPerror("SecKeychainOpen", ortn); + return ortn; + } + } + CSSM_CL_HANDLE clHand = cuClStartup(); + + switch(op) { + case op_genReq: + ortn = genOcspReq(clHand, certData, certDataLen, + issuerCertData, issuerCertDataLen, + &outData, &outDataLen); + break; + case op_parseReq: + ortn = parseOcspReq(clHand, inData, inDataLen, verbose); + break; + case op_genReply: + { + SecIdentityRef idRef = NULL; + ortn = sslSimpleIdentPicker(kcRef, &idRef); + if(ortn) { + printf("***Error choosing identity. Aborting.\n"); + exit(1); + } + ortn = genOcspResp(clHand, certStatus, crlReason, + certData, certDataLen, issuerCertData, issuerCertDataLen, + idRef, &outData, &outDataLen); + CFRelease(idRef); + break; + } + case op_parseResp: + ortn = parseOcspResp(clHand, inData, inDataLen, verbose); + break; + case op_post: + ortn = postOcspReq(clHand, certData, certDataLen, + issuerCertData, issuerCertDataLen, responderURI, + doParse, verbose, + &outData, &outDataLen); + break; + default: + printf("Op %s is not yet implemented.\n", argv[1]); + exit(1); + } + + if(ortn == 0) { + if(outData != NULL) { + if(outFile== NULL) { + printf("...generated %u bytes but no place to write it.\n", outDataLen); + } + else { + ortn = writeFile(outFile, outData, outDataLen); + if(ortn) { + printf("***Error writing output to %s.\n", outFile); + } + else { + printf("...wrote %u bytes to %s\n", outDataLen, outFile); + } + } + } + } + return ortn; +}