Handling Public and Private Keys

This section contains examples showing how to manage public and private keys using the cdk::Key class. Samples are provided for the following procedures:

These days public keys are typically presented as ASN.1 encoded X.509 certificates (see RFC 3280) or, with additional information, in password protected PKCS #12 files or "protocol data units" (PDUs) (see RFC 2437). PKCS #8 PDUs are also used for the storage and transport of private keys.

The first step in using such key formats with CDK functions is to load the certificate or PKCS #8/12 PDU into an object of the appropriate CDK class (cdk::Cert or cdk::Key). Once the PDU is parsed, its components can be accessed separately or used in a wide variety of public and private key operations. The code fragments below illustrate several of the most popular operations.

Note:
The certificates and CRLs 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 certificate and/or CRL 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- or DER-encoded SEQUENCE.)

Similarly, all raw key components in the code fragments below are represented as strings of hex digits (in ASCII). They must be converted back into binary form before being loaded into a Key object. The function used for this purpose is cdk::hex().

Processing ASN.1 Encoded Keys

Parsing X.509 Certificates

If you have an ASN.1 encoded certificate and wish to obtain the subject public key and algorithm identifier (the key type, possibly with specified parameters) from it, you may follow this example:

cdk::str strCertificate =    // a sample base64-encoded certificate
  "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 is base64-encoded (this one is!)
if (strCertificate[0] != 0x30) strCertificate = strCertificate.tobin64(); 

// load binary ASN.1 encoded certificate data into a Cert object 
cdk::Cert c1;
c1.load(strCertificate);     // nonzero return value indicates parsing error

// now individual certificate components may be accessed
cdk::str strPub, strAlgID;   // output buffers for:
strPub = c1.subject_pub;     //     subjectPublicKey and
strAlgID = c1.subject_oid;   //     subject AlgorithmIdentifier

Parsing PKCS #12 PDUs

To obtain the leaf certificate and private key from a PKCS #12 PDU, you simply provide the owner's password and call parsep12() as illustrated in this example:

cdk::str strP12 =            // a sample base64-encoded PKCS #12 PDU
  "MIIGQgIBAzCCBggGCSqGSIb3DQEHAaCCBfkEggX1MIIF8TCCAt8GCSqGSIb3DQEH"
  "BqCCAtAwggLMAgEAMIICxQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIPtPP"
  "DiTkLIQCAgQAgIICmCAJ/jf5UOduGWpozCah9dLjcJdG5xb3FlHBaN1c35VDOGK3"
  "1BZUQDvGsYttBASLD+wXY3i/MnEAwOX6c4EfL17JQtIJfE7mCAukaqUbJtLtQsvJ"
  "7JMMwfUhRkEWQA60rJV2/CaL2bewy1MIFrduf02ImTNbB20059/ewn8Ya7cXZlrH"
  "bjWhv0OjeYWvBstBRCZkz2w0Yyu9F8KGVS0HHDuiBUMU0gMBnp7COUumzZ9hzYGi"
  "GNz0VivzX86cL/wiNvSyTpbZFsOw0+NxLdG1NlSbwbFu2Ay9uQI9xh886odnsLsn"
  "u9fTpkFFlBTEwyawP8UUEjMNiXo+SVX93xGi7wfoe9HmpXBsvQ4suPzqs8j8SJ8b"
  "9pOKF40eb48GxSYUbPJ6kfBan45Doe7ypkApTpejAebVLz00u5yH+A3HiJm+Okk2"
  "Hzq+/Rh/vcY2+jjimZfV0qVVPLyDKTSWLgo7D9L4dCeeWU3VCdAbeyUKnx2V8Av9"
  "qU0q8qeDLGaXGyxZ24G7UQzaaUIIOn0wUFSjupGFctMPXz3/QvkUQgSuLKRhMWnk"
  "e6UNA2N0y5gMMmcfNh6n226m6t7lCce3KXRcCAZr0IJMpdldO7tft0v2dqo6reuE"
  "x3bv5I3ink0SJSz0xZr92p9dcqcO067PIZ3TjQaLyziYJUPvyo53Z7udPB8qTUYn"
  "v9U+3Ovjh2eGQ/5KvFathlAPav+Styn4w5LIKPDiyxXfrADeELEYOgV9AauDVaiC"
  "vrWESXbgTbCaMcYTrWDH4tWlhXLtBbTSlemAVClUa1BvSGRvAEJiF+46S1oESwQx"
  "ZTw0uBtmiuVtccRrzhS4YvxAgsBUqstd3B45RCdi8v0uECAzEYBkfdiW7ZlfxQKa"
  "q2s4oC0wggMKBgkqhkiG9w0BBwGgggL7BIIC9zCCAvMwggLvBgsqhkiG9w0BDAoB"
  "AqCCAqYwggKiMBwGCiqGSIb3DQEMAQMwDgQIL5knnoi6f/8CAgQABIICgCHYGIzu"
  "7kzK8eynvtCUzMxyrJ/k/BHnEDJ70TADRxfgn/8GylDGvxblJZaaa+W5XF0pIf7/"
  "C7ZbBRzoqzhkK2IbH1jKGtFOfP81kxpw9pLW2A01AT0rT62uEDleP1EbPKFgehBm"
  "FPp/F0y06UbewfluW6nqky5feqmTQJWBWv8ctRsH2JeOxoI4MkEPOoOYUeViPM3w"
  "YIzon6GxiNiUP+7l4Eh+WhM2ViKGRr/m02wWvgL9mnkj/aOqvPN+o7i6Up6+oVRz"
  "sWfUyQip5V3n+J7y1an+lS3nZDjxCXy0f4O9DyNV/96KMrQDFCbzRVBQNqttChuK"
  "yqsai+HQH5+xywiwCSQEfMnVElZUB2O+nCZ8Dd3PbXuZ0bl3getc7YfgOhAI8+hX"
  "HXKDzwmGl9VYwctAzqd/LLVG+sg4zJBkideLB8fW9ciE4LOsa6QeVGNSLwn61lGJ"
  "AMxIiIK19xFmnvqyeBhF1fWmPJhcnkUaWNfbmSco+u/156B5vKu6cBO4i/+/oG3M"
  "lOAyMV+jPB1YGOo8p1K5PiC0LODHKpbinK+SM3NYx1RbxEn+8rNQcOURvE0cchxs"
  "C/dqV00w/9lfA797mElW5ToOYFJot3G5NWRxJ6tf1myrMJ7VUSsJFN1aOUPj+AL5"
  "U5Dgh715z5ji+ExHLolJabsCkTd76137NoNPR8/MvWhRY94qdJABU55kyjjJPlJU"
  "wN0kE/At+wjSCXkI589qnuSJHr37X3UtUbTeK/r0QiL6ennxIkwUDqsiHR/ZAEy1"
  "Wdwjh9ij4Y9ogYB7aePtlZSqfO5FGHbgGs8EvOmFwotXM5mYCaXHMZE4We7KBto8"
  "Clcx/7TxvVH0OgUxNjAPBgkqhkiG9w0BCRQxAh4AMCMGCSqGSIb3DQEJFTEWBBQw"
  "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMCEwCQYFKw4DAhoFAAQUf8HYKKKVoyHkyeOX"
  "hpvt5t+Kn4IECCYQiCMLyK+cAgIEAA==";

// decode the PDU if it is base64-encoded (this one is!)
if (strP12[0] != 0x30) strP12 = strP12.tobin64();

// parse the PDU
cdk::Chain chn;              // buffer for PDU's certificate chain
cdk::str p12oid, strPrv;     // buffers for algID and private key
cdk::str crl;                // buffer for any CRLs
cdk::parsep12(strP12, "password", chn, p12oid, strPrv, crl);
                   // a nonzero return value indicates a parsing error

// find end-user's certificate in the chain
int nEndUserCert = chn.find(Chain::user);
if (nEndUserCert == -1) nEndUserCert = 0;   // leaf certificate not found

// and load it into a new Cert object
cdk::Cert c1;
c1.load(chn.index(nEndUserCert));

// now we can access individual certificate components
cdk::str strPub, strAlgID;   // output buffers for:
strPub = c1.subject_pub;     //     subjectPublicKey and
strAlgID = c1.subject_oid;   //     subject AlgorithmIdentifier

Loading ASN.1 Encoded Keys

Once you have obtained a subject's algorithm identifier and key(s) you can load them into a cdk::Key object to perform cryptographic operations (see Wrapping With a Public Key, Unwrapping With a Private Key, Signing, and Validating).

For encryption and signature validation, only the public key is required:

// load an ASN.1 encoded public key into a new Key object
cdk::Key key;
key.loadoid(strAlgID);       // a nonzero return value indicates an error
key.loadpub(strPub);         // a nonzero return value indicates an error

For decryption and signing, you'll need the entire key pair:

// load an ASN.1 encoded key pair into a new Key object
cdk::Key key;
key.loadoid(strAlgID);       // a nonzero return value indicates an error
key.loadprv(strPrv);         // a nonzero return value indicates an error
key.loadpub(strPub);         // a nonzero return value indicates an error

Note:
Loading a private key clears the Key's public components, so you must always load a private key before loading its corresponding public key. Alternately, you load the private key and have the CDK recompute the public components.
To load a private key into a Key object and recompute the Key's public components:

// load an ASN.1 encoded private key and recompute the public key
cdk::Key key;
key.loadoid(strAlgID);       // a nonzero return value indicates an error
key.loadprv(strPrv);         // a nonzero return value indicates an error
key.genpub();                // a nonzero return value indicates an error

To obtain ASN.1 encoded key information from an existing Key object:

cdk::Key key;                       // assume this contains a complete key pair
cdk::str strAlgID, strPub, strPrv;  // output buffers for requested components
strAlgID = key.asn1parameters(1,0); // get the key's algorithm identifier
strPub = key.asn1public();          // get the public key
strPrv = key.asn1private();         // get the (unprotected) private key


Processing Raw Key Components

In the event that your key components are not ASN.1 encoded you can still use them with the CDK. The following examples illustrate the handling of so-called 'raw' RSA, DSA, and ECDSA key components.

RSA

In the samples below we refer to the public modulus and exponent as the public key components (usually denoted n and e), while the two prime factors of the modulus (p and q) comprise the private key.

To load raw RSA public key components into a Key object:

cdk::str strRSAPUB = hex(     // a sample public modulus
  "D025F3A723231FA23CE4CE011A595787A06707AEA61443E15FAEDD6D59634E44"
  "BA5CE36695C4D441160F3A2FB9CC757542F60804E94A58B610722EBFDE3DBF08"
  "3F6FDD88AE6F282BF215542C929AF95F0A8FD44CC669A6CB72C9B920305E3B4D"
  "13AEADE6A53CAC1BD9E9BC4037A6D4F18E6B94430DBEABF1697121418A2A6AC7"
  );

// load the binary modulus with the popular public exponent 65537 (F4)
cdk::Key key;
key.RSALoadPublic(num(65537), num(strRSAPUB));
                             // a nonzero return value indicates an error

To load raw RSA key pair components into a Key object, load the private components followed by the public ones, then call setup() to compute the intermediate internal private key components (such as Chinese Remainder Theorem coefficients):

cdk::str strRSAP = hex(                // a prime
  "D0A1379B3EBFA301DC59196F18593C45E519287A23297589109F4B3C50D7B0DF"
  "729D299BC6F8E9EF9066971FB444AC06613FC8D63795BE9AD0BEAF5501193B31"
  );
cdk::str strRSAQ = hex(               // another prime
  "FF68BF231FF2B3704AEDE04EECB51E50CA698EFD50A1379B3EBFA301DC59196F"
  "18593C45E519287A23297589109F4B3C50D7B0DF729D299BC6F8E9EF90669777"
  );
cdk::str strRSAPUB = hex(             // the product
  "D025F3A723231FA23CE4CE011A595787A06707AEA61443E15FAEDD6D59634E44"
  "BA5CE36695C4D441160F3A2FB9CC757542F60804E94A58B610722EBFDE3DBF08"
  "3F6FDD88AE6F282BF215542C929AF95F0A8FD44CC669A6CB72C9B920305E3B4D"
  "13AEADE6A53CAC1BD9E9BC4037A6D4F18E6B94430DBEABF1697121418A2A6AC7"
  );

// load the prime factors of the modulus as the private key
cdk::Key key;
key.rsai.loadpriv(num(strRSAP), num(strRSAQ));
                           // a nonzero return value indicates an error
// load the modulus with the exponent 65537 (F4) as the public key
key.RSALoadPublic(num(65537), num(strRSAPUB));
                           // a nonzero return value indicates an error

key.rsai.setup();       	// computes internal RSA private key values

To load raw RSA private key components and reconstruct the corresponding public key components:

cdk::str strRSAP = hex(                // a prime
    "D0A1379B3EBFA301DC59196F18593C45E519287A23297589109F4B3C50D7B0DF"
    "729D299BC6F8E9EF9066971FB444AC06613FC8D63795BE9AD0BEAF5501193B31"
  );
cdk::str strRSAQ = hex(               // another prime
    "FF68BF231FF2B3704AEDE04EECB51E50CA698EFD50A1379B3EBFA301DC59196F"
    "18593C45E519287A23297589109F4B3C50D7B0DF729D299BC6F8E9EF90669777"
  );

// load the prime factors of the modulus as the private key
cdk::Key key;
key.rsai.loadpriv(num(strRSAP), num(strRSAQ));
                             // a nonzero return value indicates an error
// specify a public exponent and recompute internal RSA private key values
key.rsai.expo = num(65537);  // 65537 is a popular choice
key.rsai.setup();       // a nonzero return value indicates an error

// now regenerate the public components
key.rsai.genpub();           // a nonzero return value indicates an error

To obtain raw key components from a Key object containing a complete RSA key pair:

cdk::Key key;                    // assume that this contains a complete key pair
num p, q, pq, expo               // ouput buffers for key components
key.RSAGetRawPublic(expo, pq);   // access the public exponent and modulus
p = key.rsai.p;                  // access the first prime factor
q = key.rsai.q;                  // access the second prime factor

DSA

Loading raw key components is somewhat more complicated for DSA than it is for RSA because you must explicitly provide the DSA group parameters. The CDK comes with a number of predefined DSA parameter sets in parms.c and using one of them is easiest if you are generating and using keys for a standalone application. You may, however, use the CDK to generate your own parameters or import existing parameter sets from another source as an ASN.1 encoded algorithm identifier.

The code fragments in this section illustrate how to generate a new DSA parameter set (using the CDK's DSA_GenerateParameters() function) and then ASN.1 encode it. If you already have a set of raw DSA parameters, you can follow this example to obtain an ASN.1 encoded representation -- just skip the parameter generation step:

// generate a new 1024-bit DSA parameter set
int nNP = 1024;    // requested size of p in bits; denoted by L in FIPS 186-2
int H = 2;         // base for g (default value is NIST's 2; ISC uses 7)
cdk::num p, q, g;  // output buffers for DSA parameters
int start = 0;     // starting value of counter
int counter = 0;   // output buffer for final iteration count
cdk::str seed = cdk::getrand2(20);  // initial random 20-byte seed value
PRNG pi;           // SHA-1 based PRNG to use if we need more seed values

// retry until we find a suitable seed
while ( 
  DSA_GenerateParameters(seed,160,nNP,q,p,g,counter,start,H) == CDK_INVALID_SEED
  )
    strSeed = pi.gens(20);

// obtain hexadecimal representations of p,q,g
cdk::str strQ = q.tostr().tohex(1);
cdk::str strP = p.tostr().tohex(1);
cdk::str strG = g.tostr().tohex(1);

// populate a GroupData object with these parameters
cdk::GroupData cd;                              
cd.deg = 1;                  // flag for SHA-1 (for ECDSA, degree of field extension)
cd.seed = "";                // seed is not encoded with parameters
cd.order = strQ.c_str();     // q
cd.p = strP.c_str();         // p
cd.a = 0;                    // only for ECDSA (1st polynomial coefficient)
cd.b = 0;                    // only for ECDSA (2nd polynomial coefficient)
cd.Gx = strG.c_str();        // g (for ECDSA, x coordinate of EC base point)
cd.Gy = 0;                   // only for ECDSA (y coordinate of base point)
cd.start = 0;
cd.genseed = H;

// get an ASN.1 encoded algorithm ID for these parameters
cdk::str strAlgID = cd.oid();

If you want to use one of the predefined DSA parameter sets provided by ISC in parms.c, you can quickly get its algorithm ID without explicitly constructing a GroupData object. For example, the ASN.1 encoded algorithm ID for ISC's 1024-bit DSA parameters can be obtained as follows:

cdk::str strAlgID = DSA_Parms[ISCDSA1024].oid();

Once you have the ASN.1 encoded algorithm ID for a DSA key, you can load its raw public key component like this:

cdk::str strDLPUB = hex(                // a sample public key
    "19131871D75B1612A819F29D78D1B0D7346F7AA77BB62A859BFD6C5675DA9D21"
    "2D3A36EF1672EF660B8C7C255CC0EC74858FBA33F44C06699630A76B030EE333"
  );

cdk::Key key;
key.loadoid(DSA_Parms[FIPSEXAMPLE].oid());       // load algorithm ID
key.DLLoadPublic(num(strDLPUB));       // load public key value

To load raw DSA public and private key components:

cdk::str strDLPRV = hex("2070b3223dba372fde1c0ffc7b2e3b498b260614");
cdk::str strDLPUB = hex(
    "19131871D75B1612A819F29D78D1B0D7346F7AA77BB62A859BFD6C5675DA9D21"
    "2D3A36EF1672EF660B8C7C255CC0EC74858FBA33F44C06699630A76B030EE333"
  );

cdk::Key key;
key.loadoid(DSA_Parms[FIPSEXAMPLE].oid());       // load algorithm ID
key.loadprivate(num(strDLPRV));        // load private key value
key.DLLoadPublic(num(strDLPUB));       // load public key value

To load raw DSA private key components and generate the corresponding public key:

cdk::str strDLPRV = hex("2070b3223dba372fde1c0ffc7b2e3b498b260614");
cdk::str strDLPUB = hex(
    "19131871D75B1612A819F29D78D1B0D7346F7AA77BB62A859BFD6C5675DA9D21"
    "2D3A36EF1672EF660B8C7C255CC0EC74858FBA33F44C06699630A76B030EE333"
  );

cdk::Key key;
key.loadoid(DSA_Parms[FIPSEXAMPLE].oid());       // load algorithm ID
key.loadprivate(num(strDLPRV));        // load private key value
key.genpub();

To extract raw key components from a Key object containing a complete DSA or ECDSA key pair:

cdk::Key key;                // assume this contains a (EC)DSA key pair
num priv, pub, y;            // output buffers for raw components

// get an ASN.1 encoded algorithm ID containing the (EC)DSA parameters
cdk::str strAlgID = key.asn1parameters(1,0);
key.GetRawPrivate(priv);     // get raw private key
key.DLGetRawPublic(pub, y);  // y is only used for ECDSA; ignore for DSA

ECDSA

As it was for DSA, loading raw keys is more complicated for ECDSA than for RSA because you must explicitly provide an ECDSA parameter set specifying the elliptic curve. The CDK comes with a number of predefined ECDSA parameters in parms.c, including all of the prime and binary curves published by NIST in FIPS 186-2, and these are easiest to use if you are generating and using ECDSA keys in an application that only needs to interoperate with other applications that use these same curves.

Note:
The CDK does not support binary elliptic curve operations that use an optimal normal basis representation for the underlying field elements, so parameter sets for those NIST examples that use ONB representations are omitted.
If you want to generate your own ECDSA parameters or have parameters from another source, you must obtain an appropriate ASN.1 encoded algorithm identifier and use it to initialize your Key objects. ANS.1 encoded algorithm identifiers may be created by filling out a cdk::GroupData object and calling its oid() method.

The following code fragment shows how to create an algorithm identifier specifying the elliptic curve labeled 'P-192' by NIST:

// create an ASN.1 encoded algorithm ID for NIST Curve P-192
cdk::GroupData cd;
cd.deg = 0;
cd.a = "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC";
cd.seed = "0x3045AE6FC8422F64ED579528D38120EAE12196D5";
cd.order = "0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831";
cd.p = "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF";
cd.b = "0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1";
cd.Gx = "0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012";
cd.Gy = "0x7192B95FFC8DA78631011ED6B24CDD573F977A11E794811";
cd.start = 0;
cd.genseed = 0;
cdk::str strECDSAP192 = cd.oid();

The following code fragment shows how to create an algorithm identifier specifying the Koblitz curve labeled 'K-163' by NIST:

// create an ASN.1 encoded algorithm ID for NIST Curve K-163
cdk::GroupData cd;
cd.deg = 163;
cd.a = "0x1";
cd.seed = "";
cd.order = "0x04000000000000000000020108a2e0cc0d99f8a5ef";
cd.p = "0x0800000000000000000000000000000000000000c9";
cd.b = "0x1";
cd.Gx = "0x02fe13c0537bbc11acaa07d793de4e6d5e5c94eee8";
cd.Gy = "0x0289070fb05d38ff58321f2e800536d538ccdaa3d9";
cd.start = 0;
cd.genseed = 0;
cdk::str strECDSAK163 = cd.oid();

Of course, since the CDK provides predefined parameter sets for these two curves, it isn't necessary to explicitly construct the algorithm IDs yourself! You can simply use the oid() method after picking out the appropriate element of the NIST_Curves[] array. Here are two examples:

strECDSAP192 = NIST_Curves[NISTP192].oid();

strECDSAK163 = NIST_Curves[NISTK163].oid();

Once you have an ASN.1 encoded algorithm identifier for your key type you can load raw ECDSA private key components into a Key object and then generate the corresponding public key components as follows:

cdk::str strECPRV = cdk::hex("AA8A7935B06BC5CA9749BBA4053A999667C0FDA287B759E1");
cdk::Key k9;
k9.loadoid(NIST_Curves[0].oid());
                             // a nonzero return value indicates an error
k9.loadprivate(num(strECPRV));
k9.genpub();                 // a nonzero return value indicates an error

Note:
No direct mechanism is provided for loading a raw ECDSA public key into the CDK. The recommended approach to loading an ECDSA public key is to ASN.1 encode the raw key and call the load method for ASN.1 encoded keys. If you have a need to load raw keys directly, contact ISC and appropriate code can be provided.

Generating Public and Private Keys

The simple examples in this section illustrate the creation of new RSA, DSA, and ECDSA key pairs.

RSA Key Generation

To generate a new RSA key pair, you must specify a seed value for the prime number search required to find the private factors of the modulus. You may also optionally override the default key size of 1024 bits, specify a public exponent other than 65537, and request more than two prime factors.

// generate a 1024-bit RSA key with 2 primes and a public exponent of 65537
cdk::Key key;
key.RSAkeygen(cdk::getrand2(80));      // a nonzero return value indicates an error

DSA/DH Key Generation

For DSA keys, you must supply a random seed value and specify an algorithm identifier (containing specifications for the group parameters). As shown here, it is simplest to use one of the predefined parameter sets in DSA_Parms[], letting the oid() method provide its ASN.1 encoded algorithm identifier.

cdk::Key key;
key.DLkeygen(cdk::getrand2(80), DSA_Parms[ISCDSA1024].oid());
                   // a nonzero return value indicates an error

ECDSA/ECDH Key Generation

For ECDSA keys, you must specify an algorithm identifier (containing specifications for the elliptic curve parameters). As shown here, it is simplest to use one of the NIST_Curves, letting its oid() method provide the required ASN.1 encoded algorithm identifier.

cdk::Key key;
key.DLkeygen(cdk::getrand2(80), NIST_Curves[NISTP384].oid());
                   // a nonzero return value indicates an error


Processing Digital Signatures

Signing

Given a Key object containing a private key, you can obtain a digital signature over a message digest in the following manner:

cdk::Key key;                // contains signer's private key
cdk::Signature sig;          // output buffer for signature
                             // sample SHA-1 digest to be signed
cdk::str digest = hex("A9993E364706816ABA3E25717850C26C9CD0D89D");
num seed = 0;                // we may need a random seed value

// a random seed is only required for DSA and ECDSA, not RSA
if (!key.isRSA()) num seed = num(cdk::getrand2(80));

key.Sign(num(digest), seed, sig);

To obtain an ASN.1 encoded digital signature from a cdk::Signature object, you may use the toasn1() function:

cdk::Signature sig;          // contains signature to be extracted

// get an ASN1. encoded represention of the signature
cdk::str strSignature = sig.toasn1();

There are two ways to extract a digital signature in "raw" form. The first option is to use the toraw() method like this:

cdk::Signature sig;          // contains signature to be extracted

// get a raw (binary) represention of the signature
cdk::str strSignature = sig.toraw();

The second method is to explicitly extract the signature components as shown here:

cdk::Key key;                // contains signer's private key
cdk::Signature sig;          // contains signature to be extracted

// get the raw signature component(s)
num r,s;
if (!key.isRSA())
   r = sig.r;                // we only need r for DSA and ECDSA
s = sig.s;

// we can convert r and s to hex-encoded strings as follows:
cdk::str strR, strS;
if (!k1.isRSA()) strR = r.tostring().tohex();
strS = s.tostring().tohex(); 

Validating

An ASN.1 encoded RSA, DSA or ECDSA digital signature can be loaded into a Signature object and validated against a purported signer's public key as follows:

cdk::Key key;                // contains purported signer's public key
cdk::str digest;             // contains message digest input
cdk::str strASN1Signature;   // contains the ASN.1 encoded signature

// load the ASN.1 signature into a new Signature object
cdk::Signature sig;
sig.load(strASN1Signature);  // nonzero return value indicates error

// and attempt to validate it
int i = key.SignCheck(num(digest), sig);     // returns zero if valid
printf("Signature is %s", i ? "INVALID!" : "valid");

To verify a signature represented as raw signature components (possibly obtained using the toraw() method described above), use the following:

cdk::Key key;                // contains purported signer's public key
cdk::Signature sig;          // contains signature to be verified
cdk::str digest;             // contains message digest

// explicitly load the relevant signature components
if (!key.isRSA())            // we only need r for DSA and ECDSA
  sig.r = r;
sig.s = s;

int i = key.SignCheck(num(digest), sig);     // returns zero if valid
printf("Signature is %s", i ? "INVALID!" : "valid");


Handling Symmetric Keys

Wrapping With a Public Key

Suppose you have a recipient's public key and wish to send him some encrypted data. The scheme most commonly used today is to encrypt the data with a random session key and send the resulting ciphertext to the recipient along with the session key encrypted, or "wrapped," with his public key. The following code fragment illustrates this scheme:

cdk::Key pubKey;                       // this contains recipient's public key
cdk::str sessionKey;                   // this contains the random session key

// wrap the session key
cdk::str wrappedKey;                   // output buffer for the wrapped key
pubKey.Encrypt(sessionKey, wrappedKey);// nonzero return value indicates error

sessionKey can be used with AES or TDES to encrypt the data, then the ciphertext and wrappedKey are transmitted to the recipient.

Unwrapping With a Private Key

Once the recipient has received the ciphertext and wrappedKey produced by the above example, he proceeds to decrypt, or "unwrap," the session key using his private key, then uses it to decrypt the data. The following code fragment illustrates this:

cdk::Key prvKey;                       // this contains recipient's private key
cdk::str wrappedKey;                   // wrapped key received from sender 

// unwrap the session key
cdk::str sessionKey;                   // output buffer for session key
prvKey.Decrypt(wrappedKey, sessionKey);// nonzero return value indicates error

The recipient now loads the unwrapped sessionKey into the appropriate cipher object (AES, DES, etc.) and decrypts the data.


Diffie-Hellman Key Agreement

Diffie-Hellman key agreement allows each of two communicating parties to privately derive a shared secret after simply exchanging their public keys. Subsequent communications can be made confidential by encrypting messages using a symmetric key derived, in a previously agreed upon manner, from the shared secret. Since it is infeasible for an eavesdropper to compute the shared secret, communications between the two parties remain secure.

The code fragment below illustrates how to implement the DH protocol using a single CDK function. It applies equally well to DSA keys and ECDSA keys, but cannot be used with RSA keys.

Note:
Using DSA keys (both based on the same parameter set!) in this example yields Diffie-Hellman in a prime-order subgroup of the multiplicative group of nonzero elements in a field of integers modulo a large prime. This use conforms with ANSI X9.42 as well as RFC 2631, section 2.1.1.

Using ECDSA keys (both based on the same underlying elliptic curve and choice of base point!) yields Elliptic Curve Diffie-Hellman in the common elliptic curve group. This use conforms with ANSI X9.63.
At this time the CDK does not provide a particular key derivation function (KDF), i.e., a function that turns the shared DH secret into a key for a particular symmetric cipher. RFC 2631, section 2.1.2 suggests one possible KDF using SHA-1 and the programmer will have no trouble implementing that algorithm using the CDK's SHA class.

// assume that Ann has her own key pair in k1 and Bob's public key in k2 
cdk::Key k1, k2;
// she computes:
cdk::Point secretA = k2.pub * k1.getPrivate();

// assume that Bob has his own key pair in k3 and Ann's public key in k4
cdk::Key k3, k4;
// he computes:
cdk::Point secretB = k4.pub * k3.getPrivate();

// now, secretA = secretB, so Ann and Bob share a secret no eavesdropper
// with only their public keys could compute


For information concerning the objects/methods used here see:


The next topic is Handling PKCS PDUs (including CMS).


ISC Cryptographic Development Kit - User's Guide
ISC website
Questions? E-mail ISC technical support
Copyright© 2002-2006 Information Security Corp. All rights reserved.