]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/ocspTool/ocspTool.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / ocspTool / ocspTool.cpp
diff --git a/SecurityTests/clxutils/ocspTool/ocspTool.cpp b/SecurityTests/clxutils/ocspTool/ocspTool.cpp
new file mode 100644 (file)
index 0000000..153cdaa
--- /dev/null
@@ -0,0 +1,1254 @@
+/* 
+ * 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;
+}