+/*
+ * ocspTool - simple OCSP request/response generator and parser
+ */
+#include <Security/Security.h>
+#include <Security/SecAsn1Coder.h>
+#include <Security/ocspTemplates.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <security_cdsa_utils/cuOidParser.h>
+#include <security_cdsa_utils/cuPrintCert.h>
+#include <clAppUtils/CertParser.h>
+#include <clAppUtils/timeStr.h>
+#include <clAppUtils/identPicker.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <security_ocspd/ocspExtensions.h>
+#include <security_ocspd/ocspdUtils.h>
+#include <utilLib/common.h>
+#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; dex<indent; dex++) {
+ printf(" ");
+ }
+}
+
+static void printString(
+ const CSSM_DATA *str)
+{
+ unsigned i;
+ char *cp = (char *)str->Data;
+ for(i=0; i<str->Length; 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; i<len; i++) {
+ printf("%02X ", ((unsigned char *)cp)[i]);
+ }
+ if(more) {
+ printf("...\n");
+ }
+ else {
+ printf("\n");
+ }
+}
+
+static void printTaggedItem(
+ const NSS_TaggedItem &ti)
+{
+ switch(ti.tag) {
+ case BER_TAG_PRINTABLE_STRING:
+ case BER_TAG_T61_STRING:
+ case BER_TAG_IA5_STRING:
+ case BER_TAG_UTC_TIME:
+ case BER_TAG_GENERALIZED_TIME:
+ printString(&ti.item);
+ break;
+ default:
+ printDataAsHex(&ti.item, 0);
+ }
+}
+
+static void printName(
+ const NSS_Name &name,
+ int indent)
+{
+ OidParser parser;
+
+ unsigned numRdns = ocspdArraySize((const void **)name.rdns);
+ for(unsigned rdnDex=0; rdnDex<numRdns; rdnDex++) {
+ NSS_RDN *rdn = name.rdns[rdnDex];
+ unsigned numATVs = ocspdArraySize((const void **)rdn->atvs);
+ for(unsigned atvDex=0; atvDex<numATVs; atvDex++) {
+ NSS_ATV *atv = rdn->atvs[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<OCSPNonce *>(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; dex<numReqs; dex++) {
+ SecAsn1OCSPRequest *req = tbs.requestList[dex];
+ doIndent(indent);
+ printf("Request List Entry %u\n", dex);
+ indent += 2;
+ doIndent(indent);
+ printf("CertID:\n");
+ indent += 2;
+ SecAsn1OCSPCertID *certID = &req->reqCert;
+ dumpCertID(certID, indent);
+ indent -= 2;
+ numExts = ocspdArraySize((const void **)req->extensions);
+ for(unsigned extDex=0; extDex<numExts; extDex++) {
+ doIndent(indent);
+ printf("singleExtension[%u]\n", extDex);
+ printOcspExt(coder, req->extensions[dex], indent + 2);
+ }
+ indent -= 2;
+ }
+
+ numExts = ocspdArraySize((const void **)tbs.requestExtensions);
+ for(unsigned extDex=0; extDex<numExts; extDex++) {
+ doIndent(indent);
+ printf("requestExtension[%u]\n", extDex);
+ printOcspExt(coder, tbs.requestExtensions[extDex], indent + 2);
+ }
+
+ indent -= 2;
+
+ if(signedReq.signature) {
+ printf("SecAsn1OCSPSignature:\n");
+ indent += 2;
+ doIndent(indent);
+ printf("==unparsed for now ==\n");
+ /* ... */
+ indent -= 2;
+ }
+errOut:
+ SecAsn1CoderRelease(coder);
+ return ortn;
+}
+
+static int genOcspResp(
+ CSSM_CL_HANDLE clHand,
+ SecAsn1OCSPCertStatusTag status,
+ CE_CrlReason reason, // for CS_Revoked
+ const unsigned char *subjectCert,
+ unsigned subjectCertLen,
+ const unsigned char *issuerCert,
+ unsigned issuerCertLen,
+ SecIdentityRef signer,
+ unsigned char **outData,
+ unsigned *outDataLen)
+{
+ SecAsn1CoderRef coder;
+ SecAsn1CoderCreate(&coder);
+
+ CSSM_DATA subjectCertData = {subjectCertLen, (uint8 *)subjectCert};
+ CSSM_DATA issuerCertData = {issuerCertLen, (uint8 *)issuerCert};
+ uint8 nonceBytes[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
+ CSSM_DATA nonceData = {8, nonceBytes};
+ CSSM_DATA tbs;
+ CSSM_DATA encoded = {0, NULL};
+ SecAsn1OCSPResponse topResponse;
+ SecAsn1OCSPResponseBytes responseBytes;
+ uint8 responseStatusByte;
+ CSSM_DATA resp = {0, NULL};
+ CSSM_DATA sig = {0, NULL};
+
+ int irtn = genTbsResp(coder, clHand, status, reason,
+ subjectCertData, issuerCertData,
+ 0, // thisUpdate
+ 2600 * 24, // next update
+ &nonceData,
+ tbs);
+ if(irtn) {
+ printf("***Error encoding tbsResp\n");
+ return irtn;
+ }
+
+ /*
+ * That's the TBSResponseData. Sign it.
+ */
+ OSStatus ortn;
+ SecAsn1OCSPBasicResponse basicResp;
+ memset(&basicResp, 0, sizeof(basicResp));
+ ortn = ocspSign(signer, tbs, CSSM_ALGID_SHA1WithRSA, sig);
+ if(ortn) {
+ printf("***Error signing basicResponse.\n");
+ goto errOut;
+ }
+ basicResp.algId.algorithm = CSSMOID_SHA1WithRSA;
+ basicResp.algId.parameters.Data = nullParam;
+ basicResp.algId.parameters.Length = sizeof(nullParam);
+ basicResp.tbsResponseData = tbs;
+ basicResp.sig = sig;
+ /* ASN1 encoder needs to know length in bits */
+ basicResp.sig.Length *= 8;
+ /* no certs for now */
+ /* encode SecAsn1OCSPBasicResponse */
+
+ ortn = SecAsn1EncodeItem(coder, &basicResp, kSecAsn1OCSPBasicResponseTemplate,
+ &encoded);
+ if(ortn) {
+ printf("***Error encoding SecAsn1OCSPBasicResponse\n");
+ }
+
+ /* put that into a SecAsn1OCSPResponse */
+ responseBytes.responseType = CSSMOID_PKIX_OCSP_BASIC;
+ responseBytes.response = encoded;
+ responseStatusByte = RS_Success;
+ topResponse.responseStatus.Data = &responseStatusByte;
+ topResponse.responseStatus.Length = 1;
+ topResponse.responseBytes = &responseBytes;
+ ortn = SecAsn1EncodeItem(coder, &topResponse, kSecAsn1OCSPResponseTemplate,
+ &resp);
+ if(ortn) {
+ printf("***Error encoding SecAsn1OCSPBasicResponse\n");
+ goto errOut;
+ }
+
+ /* TA DA */
+ *outData = (unsigned char *)malloc(resp.Length);
+ *outDataLen = resp.Length;
+ memmove(*outData, resp.Data, resp.Length);
+errOut:
+ SecAsn1CoderRelease(coder);
+ if(sig.Data) {
+ APP_FREE(sig.Data);
+ }
+ return ortn;
+}
+
+/* decode and parse tbsResponseData, sitting in SecAsn1OCSPBasicResponse as an
+ * ASN_ANY */
+static int parseResponseData(
+ SecAsn1CoderRef coder,
+ int indent,
+ const CSSM_DATA &tbsResponseData)
+{
+ SecAsn1OCSPResponseData respData;
+ SecAsn1OCSPResponderID responderID;
+ uint8 tag;
+ const SecAsn1Template *templ;
+ unsigned numExts;
+
+ memset(&respData, 0, sizeof(respData));
+ OSStatus ortn = SecAsn1DecodeData(coder, &tbsResponseData,
+ kSecAsn1OCSPResponseDataTemplate, &respData);
+ if(ortn) {
+ printf("***Error decoding ResponseData\n");
+ return 1;
+ }
+ if(respData.version && respData.version->Data) {
+ 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; dex<numResps; dex++) {
+ SecAsn1OCSPSingleResponse *resp = respData.responses[dex];
+ doIndent(indent);
+ printf("Response %u:\n", dex);
+ indent += 2;
+ doIndent(indent);
+ printf("CertID:\n");
+ dumpCertID(&resp->certID, 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; extDex<numExts; extDex++) {
+ doIndent(indent);
+ printf("singleExtensions[%u]\n", extDex);
+ printOcspExt(coder, resp->singleExtensions[extDex], indent + 2);
+ }
+
+ indent -= 2; // end of resp[dex]
+ }
+
+ numExts = ocspdArraySize((const void **)respData.responseExtensions);
+ for(unsigned extDex=0; extDex<numExts; extDex++) {
+ doIndent(indent);
+ printf("responseExtensions[%u]\n", extDex);
+ printOcspExt(coder, respData.responseExtensions[extDex], indent + 2);
+ }
+ return 0;
+}
+
+static int parseOcspResp(
+ CSSM_CL_HANDLE clHand,
+ unsigned char *inFile,
+ unsigned inFileLen,
+ bool verbose)
+{
+ SecAsn1OCSPResponse topResp;
+ SecAsn1CoderRef coder;
+ OSStatus ortn;
+ int indent = 0;
+ const char *str;
+ SecAsn1OCSPBasicResponse basicResp;
+ unsigned numCerts = 0;
+
+ SecAsn1CoderCreate(&coder);
+ memset(&topResp, 0, sizeof(topResp));
+ ortn = SecAsn1Decode(coder, inFile, inFileLen, kSecAsn1OCSPResponseTemplate,
+ &topResp);
+ if(ortn) {
+ printf("***Error decoding SecAsn1OCSPResponse\n");
+ goto errOut;
+ }
+ printf("OCSPResponse:\n");
+ indent += 2;
+ doIndent(indent);
+ printf("responseStatus: ");
+ if(topResp.responseStatus.Length == 0) {
+ printf("**MALFORMED**\n");
+ }
+ else {
+ switch(topResp.responseStatus.Data[0]) {
+ case RS_Success: str = "RS_Success"; break;
+ case RS_MalformedRequest: str = "RS_MalformedRequest"; break;
+ case RS_InternalError: str = "RS_InternalError"; break;
+ case RS_TryLater: str = "RS_TryLater"; break;
+ case RS_Unused: str = "RS_Unused"; break;
+ case RS_SigRequired: str = "RS_SigRequired"; break;
+ case RS_Unauthorized: str = "RS_Unauthorized"; break;
+ default: str = "MALFORMED (unknown enum)\n"; break;
+ }
+ printf("%s (%u(d))\n", str, topResp.responseStatus.Data[0]);
+ }
+ doIndent(indent);
+ printf("ResponseBytes: ");
+ if(topResp.responseBytes == NULL) {
+ printf("empty\n");
+ goto errOut;
+ }
+ printf("\n");
+ indent += 2;
+ doIndent(indent);
+ printf("responseType: ");
+ if(appCompareCssmData(&topResp.responseBytes->responseType,
+ &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; dex<numCerts; dex++) {
+ printf("+++++++++++++++++++++++++ Cert %u +++++++++++++++++++++++++\n", dex);
+ printCert(basicResp.certs[dex]->Data, 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<CertParser> subject;
+ auto_ptr<CertParser> 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<OCSPRequest> 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;
+}