x, use a statement of the form:if(x[0]!=0x30) x = x.tobin64() (x[0]!=0x30) is true when the PDU begins with the character 'M' as it does when it is base64-encoded, whereas all binary ASN.1 encoded PDUs under consideration here start with the tag '\x30' for an ASN.1 BER- or DER-encoded SEQUENCE.)
// put a sample base64-encoded X.509 certificate into a CDK string object cdk::str strCertificate = "MIICJzCCAZCgAwIBAgIBGDANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJVUzEM" "MAoGA1UEChMDSVNDMSMwIQYDVQQDExpJU0MgQ0RLIFNhbXBsZSBDZXJ0aWZpY2F0" "ZTAeFw0wMzA3MTcwMDAwMDBaFw0wNDA3MTcwMDAwMDBaMEAxCzAJBgNVBAYTAlVT" "MQwwCgYDVQQKEwNJU0MxIzAhBgNVBAMTGklTQyBDREsgU2FtcGxlIENlcnRpZmlj" "YXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9GQTkukn+153rATR8dh2H" "m8ixF7f7Y7bI0VFJnJAQCKqta4/IhFwQIK5F2Gn8j9tITBiXCF7F6XSvaF8bivN1" "0zR0pvI11NflEm2kwh7Yw0jZJB17Y3FHg183qYegmm/UwqX5zKUa4xw+cE8XSEqU" "uwjg0roBMGhAMzFEihHzLwIDAQABozEwLzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB" "/wQEAwIAYDAPBgNVHQ4ECHJzYS0xMDI0MA0GCSqGSIb3DQEBBQUAA4GBALWGxxo5" "5ScpLfECnqEUixFwrzftQGD2ISda7EWp/d7k23fOXgHC7Za18OpvlBUZ3sC2Fg4f" "infRHd2J4mXONk5OEdjhJILd58GErcCECg4J2uJPz77/zk+giiXldQEPtG+YOaAb" "ZC2SFbdfyYDKiSPhgzdy0/b4cElf4+VzegRM"; // decode the certificate if it's base64-encoded if (strCertificate[0] != 0x30) strCertificate = strCertificate.tobin64(); // instantiate a Cert object and load the certificate string cdk::Cert c1; c1.load(strCertificate); // non-zero return value indicates error
// assume certificate has been loaded into c1 as above printf("Subject public key: %s\n", c1.subject_pub.tohex().c_str()); printf("Subject algorithm ID: %s\n", c1.subject_oid.tohex().c_str()); printf("notBefore: %f\n", c1.notBefore); printf("notAfter: %f\n", c1.notAfter);
// assume certificate has been loaded into c1 as in first example above for (int j = 0; ; j++) { cdk::str oid, value; int i = c1.getext(j, oid, value); if ( (i != 0) && (i != CDK_EXTENSION_CRITICAL) ) { break; // no more extensions; we're done } if (i == CDK_EXTENSION_CRITICAL) { printf("(critical) "); } printf("Extension OID: %s, Value: %s\n", oid.tohex().c_str(), value.tohex().c_str()); }
// assume certificate has been loaded into c1 as in first example above cdk::str strSubjectDN, strIssuerDN; // output strings // convert subject and issuer DNs to ASCII text and print them cdk::parsedname(c1.subject, strSubjectDN, 2); // see function reference for sort order info cdk::parsedname(c1.issuer, strIssuerDN, 2); printf("SubjectDN: %s\n", strSubjectDN.c_str()); printf("IssuerDN: %s\n", strIssuerDN.c_str());
// generate a 1024-bit RSA key pair based on a 40-byte random seed cdk::Key k; // instantiate a new Key object k.RSAkeygen(cdk::getrand2(40), 1024); // non-zero return value indicates error // build the new tbsCertificate (i.e., the certificate body) cdk::Cert c; // instantiate a new Cert object c.version = V3; // X.509 version 3 c.serial = cdk::num(HASH_SHA(c.subject_pub, 1)); // one way to produce a unique serial number c.issuer_oid = k.asn1parameters(0,1); // issuer signature algorithm ID c.issuer = c.subject; // issuer DN c.notBefore = time(NULL); // validity period c.notAfter = time(NULL) + 31536000; // (one year) cdk::DName dn; // create subject DN dn.cname = "John Doe"; dn.org = "XYZ Corp."; dn.country = "US"; c.subject = dn.toasn1(); c.subject_oid = k.asn1parameters(1,0); // subject key algorithm ID (RSA) c.subject_pub = k.asn1public(); // subject public key cdk::str body = c.makebody(); // ASN.1 encode the body of the certificate // sign the tbsCertificate to produce a self-signed certificate cdk::str cert; // output buffer for the certificate cdk::num r; // random seed required for DSA/ECDSA, ignored for RSA if (k.isRSA()) k.SetPadding(cdk::RSA::pkcs1); // specify padding mechanism for RSA, or else { PRNG rand; // supply random seed if non-deterministic r = (num) rand.gens(20); } k.asn1sign(body,r,cert); // sign the certificate; non-zero indicates error
See RFC 3280 for details.
Cert caCert; // assume caCert and caKey have already been initalized Key caKey; k.hashtype = hSHA1; // specify desired hash function // build the new tbsCertList (i.e., the CRL body) cdk::CRL c; // instantiate a new CRL object c.oid = caCert.subject_oid; // one could also use k.asn1parameters(0,1); c.issuer = caCert.subject; c.thisUpdate = cdk::timegmt(); c.nextUpdate = cdk::timegmt() + 365.25 * 24 * 60 * 60; // create list of revoked certificates and reason codes (see below) cdk::str crllist = ""; crllist += mkCRLEntry("0x0000001", c.thisUpdate, CRL::certificateHold, CRL::reject); crllist += mkCRLEntry("0x0000002", c.thisUpdate, CRL::cessationOfOperation); crllist += mkCRLEntry("0x0000003", c.thisUpdate); c.list = crllist; // gather together desired extensions (see below) cdk::str crlexts = ""; crlexts += asn1::mkAuthKeyIDExt(caCert); // required for conformance with RFC 3280 crlexts += asn1::mkCRLNumberExt(2163); c.extra = crlexts; cdk::str body = c.makebody(); // put it all together as a tbsCertList // sign the body to produce the CRL cdk::str crl; // output buffer for the CRL cdk::num r; // random seed required for DSA/ECDSA, ignored for RSA if (k.isRSA()) k.SetPadding(cdk::RSA::pkcs1); // specify padding mechanism for RSA, or else { PRNG rand; // supply random seed if non-deterministic r = (num) rand.gens(20); } k.asn1sign(body,r,crl); // sign the CRL; non-zero indicates error
The following auxiliary function can be used to create the individual entries in the certificate revocation list (i.e., the items in the sequence of revokedCertificates):
cdk::str mkCRLEntry(const cdk::str &serialno, cdk::TimeT revdate, CRL::Reasons reason, CRL::Instructions instruction) { str x,y = ""; cdk::TimeT t = revdate; // cdkdatetounix(revdate); num serial(hex(serialno)); x = asn::integer(serial) + asn::date(t); if ( reason == 6 && instruction > 0 ) { cdk::str strIns = ""; switch (instruction) { case CRL::callissuer: strIns = Obj*CRL_hold_callissuer; break; case CRL::reject: strIns = Obj*CRL_hold_reject; break; case CRL::pickuptoken: strIns = Obj*CRL_hold_pickuptoken; break; case none: default: strIns = Obj*CRL_hold_none; break; } y = Seq*(Obj*id_ce_holdInstructionCode+Oct*(strIns)); } if ( reason >= 0 ) x += Seq& Seq*(Obj*id_ce_cRLReasons+Oct*(x0A*single(reason))) + y; return Seq*x; }
while the following functions can be used to create the extensions:
cdk::str mkAuthKeyIDExt(const cdk::Cert &cert) { // compute SHA-1 hash of subject_pub cdk::str subPub = cert.subject_pub; cdk::str hash = HASH(SHA,strSubPub, 1); cdk::str oid; oid = Obj*id_ce_authorityKeyIdentifier; return Seq*(oid + Oct*(Seq*(x80*hash + xA1*(xA4*(Seq*(cert.subject))) + x82*bigend(cert.serial)))); } cdk::str mkCRLNumberExt(int crlNumber) { cdk::str oid = Obj*id_ce_cRLNumber; return Seq*(oid + Oct*(asn::integer(crlNumber))); }
See RFC 3280 for details.
cdk::str issuerCert = // the purported issuer certificate "MIICwjCCAiugAwIBAgIBATANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQGEwJVUzEL" "MAkGA1UECBMCSUwxETAPBgNVBAcTCE9hayBQYXJrMQwwCgYDVQQKEwNJU0MxEDAO" "BgNVBAsTB0RlbW8gQ0ExHDAaBgNVBAMTE0NlcnRpZmljYXRlIE1hbmFnZXIwHhcN" "MDMwNjI0MDUwMDAwWhcNMDUwNjI0MDUwMDAwWjBrMQswCQYDVQQGEwJVUzELMAkG" "A1UECBMCSUwxETAPBgNVBAcTCE9hayBQYXJrMQwwCgYDVQQKEwNJU0MxEDAOBgNV" "BAsTB0RlbW8gQ0ExHDAaBgNVBAMTE0NlcnRpZmljYXRlIE1hbmFnZXIwgZ8wDQYJ" "KoZIhvcNAQEBBQADgY0AMIGJAoGBAMPYg8Up8s3CAJICbNyW/CTxbj4yzaA8ZbcX" "2t969MysICe0EI2/Z2xuTXuhY6hjFi6B9d/+yVLB17zHqpEpSbafCQMZvGc1pe7u" "bcf+4wFtt5yRo7WRz6y/bMUnpCB+TlT++w8ZKTLwXNNLgAAIRC8WtawKx/c7aCJo" "lekN65VXAgMBAAGjdjB0MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD" "AQH/MB0GA1UdDgQWBBSUr9rVPeS8n/o4spfRv3Kp+4PGCTAfBgNVHSMEGDAWgBSU" "r9rVPeS8n/o4spfRv3Kp+4PGCTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEF" "BQADgYEAnsGoKxDrTn+BGZNxUTjbVwv6Lkk9xr2R2y68JiUY48fGgu5IzO9QSsl7" "UmqLAZCryOY08lNxduVXyiwRHSt8088v+6qvCgAjhxTZIn8EFOECom6tKTV9Hp6h" "dU+z5aFhfSgJRKL0SzTrBg8P2/LIqMMotKBoQWqjnB+aP2d4jgk="; cdk::str subjectCert = // the subject certificate "MIID2DCCA0GgAwIBAgIBFzANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQGEwJVUzEL" "MAkGA1UECBMCSUwxETAPBgNVBAcTCE9hayBQYXJrMQwwCgYDVQQKEwNJU0MxEDAO" "BgNVBAsTB0RlbW8gQ0ExHDAaBgNVBAMTE0NlcnRpZmljYXRlIE1hbmFnZXIwHhcN" "MDMwNjI3MTk0ODU3WhcNMDQwNjI2MTk0ODU3WjBJMREwDwYDVQQMEwhNaXggQ2Fz" "ZTEOMAwGA1UEAxMFVGVTVDMxJDAiBgkqhkiG9w0BCQEWFXRlc3QzQGluZm9zZWNj" "b3JwLmNvbTCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCT6JZdr9nf7P0AtGa2j5Dq" "aK9dyf7ZFSeNGzoTdHHmVZbDf+0MeCn/j4Mx+BonAEOOzcwJRH3Dl8aF85cpT3Ir" "zEhK7fKL7SWqqzXTWmXbH9YsnXulWET+sflAHmcTQJM+5DxU5NxFlADXrWEki4Oi" "Ykg1sx//LZWVpbkLJ25E+QIVALtd/kLeEVwA217xsDiDRNbBVIWtAoGAaSefjrq4" "Do8k8BhGIZZjbEwiVGTwEz4PLyHnu+0dhT8G65SahoUKMKm1ss6oMOqtAOe99h68" "gcxB3rr/G1v9nyqksRMpsTw9xpURJ/vIa001M4I7+XpY3i/x+cmzF3ujSU7V4AA4" "Pb1qaZ1XiVSPzvUHNo5L5m7rDwh1c4y8/MUDgYQAAoGAAJevjq95fOwS9l3Cr1xq" "KIftJsWoT16u37VhiRfGEq+/JEkDjBkC22S10E7Iyv7p75Lv617N+2pOYQRWtcxp" "U169I60gxNvW6c2eLXHA+d9vKvaIqjmsYP9K/poqMJ2sdK50OQJxId4xOWiN9KLp" "BicXOLjcp1VHE1MaNST1fQWjgZUwgZIwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1Ud" "DwEB/wQEAwIF4DAYBgNVHREEETAPgQ1hbGljZUBpc2MuY29tMB8GA1UdIwQYMBaA" "FJSv2tU95Lyf+jiyl9G/cqn7g8YJMDIGA1UdCQQrMCkwEgYJYIZIAWUCAgFEMQUW" "A0FCQzATBgpghkgBhvhCAwEEMQUWA1ImRDANBgkqhkiG9w0BAQUFAAOBgQCq778P" "0yNeutqWu7pomHJ8yWZ7ZC2w8njvOLLJoECaaP5j9xt6hRdyCqIfzx3hVWxv1VOb" "dHgEfNViMFPaURYRJWbfX0Gn7+DJ9Lluby6prXPm0rTIZ4GdRCBA58dhn7ct5itc" "YorKfy79SaeumDvsjIvbe7iR6WO5rDxuVChaBA=="; // decode the certificates if they are base64-encoded if (issuerCert[0] != 0x30) issuerCert = issuerCert.tobin64(); if (subjectCert[0] != 0x30) subjectCert = subjectCert.tobin64(); // check the signature in subjectCert using public key in issuerCert int i = cdk::checkcert(issuerCert, subjectCert); // checkcert() returns 0 if certificate is valid, non-zero otherwise printf("Certificate is %s\n", i ? "INVALID!" : "valid");
The following code shows how to test a certificate against a CRL.
cdk::str strCertificate = // a base64-encoded certificate "MIIChjCCAe+gAwIBAgIBNjANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEY" "MBYGA1UEChMPVS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsT" "B1Rlc3RpbmcxFTATBgNVBAMTDENBMS1JQy4wNC4wMTAeFw05ODAxMDExMjAxMDBa" "Fw00ODAxMDExMjAxMDBaMGAxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9VLlMuIEdv" "dmVybm1lbnQxDDAKBgNVBAsTA0RvRDEQMA4GA1UECxMHVGVzdGluZzEXMBUGA1UE" "AxMOVXNlcjEtSUMuMDQuMDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM2d" "GkraKGdIi6EXxAu6/ekMqDloX5YSVBGh4Hp2faujr1u4j8Lp8afqjngRxFUpTqGb" "qH0ETgm4cVPXmc9rUvUzYTMdxTUmIZ+iW+ULZEvzNB712kxRPCD2kDFN2fH2ai8m" "iXr434w+weLm8VQN4jJGo4nswhSs2w1gsUmWyn/ZAgMBAAGjUjBQMA4GA1UdDwEB" "/wQEAwIF4DAWBgNVHSAEDzANMAsGCWCGSAFlAwEwATARBgNVHQ4ECgQITsLx/sO1" "edwwEwYDVR0jBAwwCoAIbMuZ73onuZswDQYJKoZIhvcNAQEFBQADgYEAeKft0RM8" "/b3zQodaKrTdWiFyLg5fzoOsTecSfdFPXoqz9J5ejLVkvJevSmfXJrIUhKXySzsQ" "i+GazuTh/hvWjwUTIvmupi+EiFudnMpXCro8bgi48+NkepNjXvjsSmOfzlrK3Sxt" "pH5dqonL6LHjGyg+Xp0Nor1m5g1rLHyrcEk="; cdk::str strCRL = // a base64-encoded CRL "MIIBSzCBtQIBATANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UE" "ChMPVS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsTB1Rlc3Rp" "bmcxFTATBgNVBAMTDENBMS1JQy4wNC4wMRcNOTkwMTAxMTIwMTAwWhcNNDgwMTAx" "MTIwMTAwWqAjMCEwCgYDVR0UBAMCAQEwEwYDVR0jBAwwCoAIbMuZ73onuZswDQYJ" "KoZIhvcNAQEFBQADgYEAMk6DRztz1AyFnFr1KAlbjLLwxtQplf2eIc//zUkDFVUH" "tX5TrEC/ijUaItjdkOoPGQfpnL0w8xwyqWndMh593QPCqIJTtv/iACoiJNZ90ZJS" "0adcdZ+AEmQpa0Zv0e1JOqRrPoAfTq4HrOfRvhBwhvKQNtTExupW/EBudznKC6Q="; // decode the certificate or CRL if either is base64-encoded if (strCertificate[0] != 0x30) strCertificate = strCertificate.tobin64(); if (strCRL[0] != 0x30) strCRL = strCRL.tobin64(); cdk::CRL crl; // instantiate a CRL object and load the crl.load(strCRL); // ASN.1 encoded CRL (test return code for error) // check to see if the certificate has been revoked cdk::Cert::Time tDate; // output buffer for revocation date (if any) int nReason; // reason for revocation (if any) int i = crl.isRevoked(strCertificate, tDate, nReason); if (i) printf("Certificate has been revoked: reason code = %d\n", nReason); else printf("Certificate does not appear on this CRL\n");
int OCSP_CheckCert(const str &caCert, const str &subCert, const str &svrURL, const str &svrCert) { /* issue an OCSP status request parameters: caCert issuer certificate subCert subject certificate whose validity is to be tested svrURL URL of OCSP responder svrCert responder certificate (for authentication of response) returns: 0 if certificate is valid 1 if certificate has been revoked 2 if certificate status is unknown (responder not authoritative for this CA) 3 protocol error 4 signature on response is invalid (reponse cannot be trusted) */ // build the OCSP request cdk::str nonce = cdk::getrand2(16); cdk::str req = cdk::make_ocsp_req(caCert, subCert, nonce); // query the server CAmHttpSocket http; http.m_lpszContentType = "Content-Type: application/ocsp-request \r\n"; std::string sResponse = http.GetPage(svrURL.c_str(), true, req.c_str(), req.length()); cdk::str res = str_(sResponse); // check the response (and its signature) cdk::asn sinfo, certs, dn; cdk::TimeT revtime; int i = check_ocsp(req, res, sinfo, revtime, certs, dn); if ( (i == 0) || (i == 1) ) // if OK or revoked, check responder's signature { if ( check_signinfo(svrCert, sinfo) != 0 ) i = 4; //signature is invalid! } return i; }
OCSP is specified in RFC 2560. The CAmHttpSocket class used here for the HTTP POST operation is available from the Code Project.
The next topic is Handling Public and Private Keys.
| ISC Cryptographic Development Kit - User's Guide | |
| Questions? E-mail ISC technical support | |
| Copyright© 2002-2006 Information Security Corp. All rights reserved. |