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/DER-encoded SEQUENCE.)The cdk::Chain class provides several methods for creating, parsing, and manipulating PKCS #7 PDUs for the purpose of certificate transport. For example, a certificate set can be sorted into an ordered certificate path (discarding unrelated certificates). Root and leaf (end-user) certificates can also be identified and extracted from the set.
The first example in this section shows how individual certificates may be extracted from a PKCS #7 PDU:
cdk::str strPKCS7; // contains the PKCS #7 PDU to be parsed // parse the PKCS #7 PDU into a new Chain object cdk::Chain chn; // buffer for the certificate chain cdk::parsep7(strPKCS7, chn); // nonzero return value indicates error // extract the first two certificates in the chain cdk::str strCertA, strCertB; // buffers for the individual certificates strCertA = chn.index(0); strCertB = chn.index(1); // empty string indicates no more certificates
A PKCS #7 PDU containing a chain of one or more certificates may be created as follows:
cdk::str strCertA = // a sample base64-encoded 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 strCertB = // another base64-encoded 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 (as these are) if (strCertA[0] != 0x30) strCertA = strCertA.tobin64(); if (strCertB[0] != 0x30) strCertB = strCertB.tobin64(); // add the binary ASN.1 encoded certificates to a new Chain object cdk::Chain chn; // output buffer for certificate chain chn.add(strCertA); chn.add(strCertB); // construct a PKCS #7 PDU containing the certificate chain cdk::str strPKCS7 = cdk::makep7(chn); if (+strPKCS7 == 0) return -1; // empty string indicates error
PKCS #7 (CMS Version 1.5) is specified in RFC 2315 but has been obsoleted by RFC 3852.
cdk::Key key; // contains private key to be transported char *pwd = "abcdefgh"; // user password for the PDU // extract the algorithm ID and private key from the Key object cdk::str strAlgID, strPrv; strAlgID = key.asn1parameters(1,0); strPrv = key.asn1private(); // create a TDES-encrypted PKCS #8 PDU containing that data cdk::str strPKCS8 = makep8(strAlgID, strPrv, pwd); if (+strPKCS8 == 0) return -1; // empty string indicates error
Using the CDK to decrypt and parse an encrypted PKCS #8 private key is also easy. This example shows how to parse the PKCS #8 PDU created above and load its private key into a cdk::Key object:
cdk::str strPKCS8; // PKCS #8 PDU to be decrypted and parsed cdk::str strAlgID, strPrv; // buffers for algorithm ID and private key char *pwd = "abcdefgh"; // user password for the PDU // use pwd to decrypt the PKCS #8 PDU, then parse it cdk::parsep8(strPKCS8, pwd, strAlgID, strPrv); // nonzero return value indicates error // insert the algorithm ID and private key into a new Key object cdk::Key key; key.loadoid(strAlgID); // nonzero return value indicates error key.loadprv(strPrv); // nonzero return value indicates error
cdk::Key key; // key to be included in the request cdk::DName dn; // subject DN to be included in the request dn.cname = "John Doe"; dn.org = "XYZ Corp."; dn.country = "US"; // extract the algorithm ID and *public key* from the Key object cdk::str strPub, strAlgID; strAlgID = key.asn1parameters(1,0); strPub = key.asn1public(); // create a request body with that public key and subject DN cdk::str tbsP10 = makep10raw(dn.toasn1(), strAlgID, strPub, ""); // create the PKCS10 PDU by self-signing with the private key cdk::str strP10; key.asn1sign(tbsP10, num(cdk::getrand2(80)), strP10); // nonzero return value indicates error // parsing a PKCS #10 PDU also validates its signature cdk::str strDN; // output buffer for subject DN cdk::str strAttr; // output buffer for attributes parsep10(strPKCS10, strDN, strAlgID, strPub, strAttr); // nonzero return value indicates parsing or validation error
PKCS #10 is specified in RFC 2986.
You can parse a CRMF PDU as follows:
cdk::asn aDN, aOID, aPub, aAttr; // convert request PDU to binary and parse cdk::str req = cdk::asn(request).tobin(); cdk::parse_crmf(req, aDN, aOID, aPub, aAttr); cdk::parsedname(aDN, dn, 2); // convert DN to text cdk::Key key; key.loadoid(aOID); key.loadpub(aPub); keysize = key.bits(); if (key.isRSA()) keytype = 1; else if (key.isDH()) keytype = 2; else if (key.isEC()) keytype = 3; else keytype = -1; // unknown key type
CRMF is specified in RFC 4211 which obsoleted RFC 2511.
cdk::str strP12 = // a sample base64-encoded PKCS #12 PDU "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" "hpvt5t+Kn4IECCYQiCMLyK+cAgIEAA=="; // decode the PKCS #12 PDU if it is base64-encode (as this one is) if (strP12[0] != 0x30) strP12 = strP12.tobin64(); cdk::Chain chn; // output buffer for a certificate chain cdk::str crl; // output buffer for a certificate chain cdk::str algID; // output buffer for the algorithm ID cdk::str prvKey; // output buffer for the private key char *password = "abcdefgh"; // user's passsword for this PDU // parse the PKCS #12 PDU and decrypt its private key cdk::parsep12(strP12, password, chn, algID, prvKey, crl); // nonzero return value indicates parsing or password error // locate end user's certificate int nEndUserCert = chn.find(Chain::user); // return value of -1 indicates end-user certificate not found // load the certificate into a new Cert object cdk::Cert cert; // output buffer for certificate cert.load(chn.index(nEndUserCert)); // load the end-user key pair into a new Key object cdk::Key key; key.loadoid(cert.subject_oid); key.loadpub(cert.subject_pub); key.loadprv(prvKey); // the CDK can also be used to create a PKCS #12 // first parameter can either be the end user's certificate or an entire chain cdk::str strP12 = makep12(chn.certs, k1.asn1parameters(1,0), k1.asn1private(), password, "", ""); if (+strP12 == 0) return -1; // empty string indicates error
int CMSEncrypt(const cdk::str &recips, const cdk::str &msg, cdk::str &p7m) { /* input parameters: recips: a concatenated list of recipient certificates msg: the message to be encrypted p7m: an output buffer for the ciphertext */ // obtain an random key and IV str key = getrand2(16), iv = getrand2(16); // instantiate and initialize a CMS1 object cdk::CMS1 cms; cms.enveloped = true; cms.recip_cer = recips; // create the encrypted body and DER-encode it as per PKCS #7 if (cms.encrypt(key,iv,msg,1)) return -1; p7m = cms.make(); return 0; }
Note that the length of the key determines the version of AES that is employed by cdk::CMS1::encrypt(): 16 bytes tranlates into AES-128, 24 into AES-192, and 32 into AES-256. For TDES encryption, simply omit the fourth parameter in the cdk::CMS1::encrypt() call, in effect letting it default to 0.
NOTE: If the input data is properly MIME-encoded, the p7m PDU output by CMSEncrypt() can itself be MIME-encoded to produce a true S/MIME message. The use of CMS in S/MIME is specified in RFC 3851.
int CMSDecrypt(cdk::str &p7m, cdk::str &cert, cdk::str &p8, cdk::str &pwd, cdk::str &msg) { /* input parameters: p7m: the ciphertext to tbe decrypted cert: the user's certificate p8: the user's password-encrypted private key pwd: the user's password msg: an output buffer for the plaintext */ // instantiate a CMS1 object and load the ciphertext data cdk::CMS1 cms; cms.load(p7m); // this parses out recipient info // obtain user's key type and private key from a PKCS #8 PDU cdk::asn oid, prv; cdk::parsep8(p8, pwd, oid, prv); // return error if user is not a recipient cdk::Chain chn(cert); cdk::asn cmsCert, cmsPDU; if ( chn.findmatch(cms.recips, cmsPDU, cmsCert) != 0 ) return -1; // otherwise decrypt PDU cms.decrypt(cmsPDU, oid, prv); msg = cms.data; return 0; }
int CMSSign(cdk::str &cert, cdk::str &p8, cdk::str pwd, cdk::str &msg, cdk::str &p7s) { /* input parameters: cert: the user's certificate p8: the user's password-encrypted private key pwd: the user's password msg: the message to tbe signed p7s: an output buffer for the signed CMS PDU */ // obtain user's key type and private key from a PKCS #8 PDU cdk::asn oid, prv; cdk::parsep8(p8, pwd, oid, prv); // set up a Key object with the user's private key cdk::Key key; key.loadoid(oid); key.loadprv(prv); key.genpub(); // instantiate and initialize a CMS1 object CMS1 c; c.enveloped = false; // sign, don't encrypt c.data = msg; c.chn.certs = cert; // create the signed body and DER-encode it as per PKCS #7 PRNG r; Signer s; if ( s.make(msg, cert, key, num(r.gens(20)), c.signers) ) return -1; p7s = c.make(); return 0; }
int CMSVerify(cdk::str &p7s, cdk::str &msg, cdk::str &cert) { /* input parameters: p7s: the SignedData PDU to be validated msg: the message data that was signed cert: output buffer for the signer's certificate returns 0 if signature is valid */ CMS1 cms; // instantiate a CMS1 object // load PDU and return error if it can't be parsed if ( cms.load(p7s) ) return -1; // return an error if it's EnvelopedData (or handle it!) if ( cms.isEnveloped() ) return -1; // parse out signer info and cert; abort if not found in PDU or invalid asn sInfo, aCert=cert; if ( cms.chn.findmatch(cms.signers,sInfo,aCert) != 0 ) return -2; Signer s; if (s.load(sInfo)) return -3; // re-hash data and compare with message digest in PDU if (HASH(SHA,cms.data,1) != s.messageDigest1) return -4; cdk::TimeT date = s.signingTime; int i = checksignhash(cert,num(s.messageDigest2),s.signature1); int err = 0; if (i) err = ((i == CDK_CERT_EXPIRED) ? -5 : -6); return err; }
See RFC 3852 for more information on CMS message formats. The original description of PKCS #7 is in RFC 2315.
// Generate a 192-bit session key according to PKCS #12 cdk::str strSession = cdk::genkeyp12("password", cdk::getrand2(8), 24, 1024, 1); // Generate a 16-byte IV according to PKCS #12 cdk::str strIV = cdk::genkeyp12("password", cdk::getrand2(8), 16, 1024, 2); // Generate a 192-bit MAC key according to PKCS #12 cdk::str strMAC = cdk::genkeyp12("password", cdk::getrand2(8), 24, 1024, 3); // Generate a 192-bit session key according to PKCS #5 cdk::str strSession2 = cdk::genkeyp5("password", cdk::getrand2(8), 512, 24);
In order to recreate the session key you need the password, salt, and iterations used to begin with. In general the salt and iterations are passed along with the encrypted data so that the recipient need only enter the password to decrypt the data.
PKCS #5 is specified in RFC 2898.
The next topic is Pseudorandom Numbers.
| ISC Cryptographic Development Kit - User's Guide | |
| Questions? E-mail ISC technical support | |
| Copyright© 2002-2006 Information Security Corp. All rights reserved. |