Handling PKCS PDUs (including CMS)

The CDK provides methods for processing the popular PDUs ("protocol data units") defined by several PKCS standards documents and related RFCs. Among the supported data formats are:

Note:
The certificates and other PDUs in all of the examples below are represented as base64-encoded octet strings so that they could be included as string literals in the source code. If you are loading a PDU from a file and are unsure whether the data is base64-encoded or not, you should test for the encoding wrapper and, if one is present, remove it before attempting to load the data.

To decode a (possibly base64-encoded) PDU stored in a cdk::str object x, use a statement of the form:
    if(x[0]!=0x30) x = x.tobin64()
as shown below. (Note that the predicate (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.)

PKCS #7 Certificate Sets

PKCS #7 PDUs are often used to transport a set of certificates, an often unordered collection of possibly unrelated X.509 certificates.

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.


PKCS #8 Private Keys

PKCS #8 support in the CDK is limited to the creation and parsing of password-protected private key packets. The code below shows how to extract a private key from a Key object and put it into a PBE-protected PKCS #8 PDU that is TDES-encrypted with a user-supplied password:

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


PKCS #10 Certificate Requests

The CDK can also handle PKCS #10 certificate requests. The following code shows how to create and sign a PKCS #10 PDU and then how to parse it.

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.


PKIX Certificate Request Message Format (CRMF)

The CRMF is a newer alternative to the certificate request syntax specified by PKCS #10.

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.


PKCS #12 Key Transport Packages

The next code fragment shows how to parse a PKCS #12 "PFX" PDU and then recreate it.

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


IETF Cryptographic Message Syntax (CMS)

Creating an EnvelopedData PDU

To create an AES-encrypted CMS EnvelopedData PDU (following PKCS #7), you may use class CMS1 as follows:

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.

Decrypting an EnvelopedData PDU

Given an encrypted CMS PDU (EnvelopedData) and a user's certificate and private key (in the form of a password-encrypted PKCS #8 PDU), one might go about decrypting the PDU as follows:

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;
}

Creating a SignedData PDU

The following code shows how use a CMS1 and Signer object to create an opaque CMS SignedData PDU:

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;
}

Validating a SignedData PDU

A CMS SignedData PDU can be verified as follows:

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.


Password Based Encryption (PKCS #5 PBE)

The CDK provides support for generating session keys from passwords based on PKCS #5 and PKCS #12. cdk::genkeyp12() generates a session key of the requested length based on a password according to the PKCS #12 standard. cdk:genkeyp5() generates a session key of the requested length based on a password according to PKCS #5. Their use is shown below:

// 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
ISC website
Questions? E-mail ISC technical support
Copyright© 2002-2006 Information Security Corp. All rights reserved.