PKI Key Client in Java - dogtagpki/pki GitHub Wiki
The Java Key Client framework provides an easy way to perform the CRUD operations with respect to keys and key requests, on the KRA, over the REST framework.
It allows users to perform GET and POST calls for archiving, retrieving and modifying the keys on the KRA.
A build for x86_64 architectures for Fedora 20 can be found here: http://vakwetu.fedorapeople.org/10.2.0/.
On installing the rpms, the jar files can be found at /usr/share/java/pki
.
Install CA and KRA as described in the Installation Guide.
Following are the jars to be included in the buildpath/classpath of your code to use the client framework.
### Jars provided by Dogtag rpms ### /usr/share/java/pki/pki-certsrv.jar /usr/share/java/pki/pki-tools.jar /usr/share/java/pki/pki-nsutil.jar /usr/share/java/pki/pki-cmsutil.jar ### External Jar files ### /usr/share/java/apache-commons-cli.jar /usr/share/java/apache-commons-logging.jar /usr/share/java/jss/jss4.jar /usr/share/java/jaxb-api.jar /usr/share/java/resteasy/jaxrs-api.jar /usr/share/java/httpcomponents/httpclient.jar /usr/share/java/httpcomponents/httpcore.jar /usr/share/java/resteasy/resteasy-client.jar /usr/share/java/resteasy/resteasy-jackson-provider.jar /usr/share/java/resteasy/resteasy-jaxrs.jar /usr/share/java/commons-httpclient.jar /usr/share/java/commons-io.jar /usr/share/java/commons-codec.jar /usr/share/java/jackson/jackson-core-asl.jar /usr/share/java/jackson/jackson-jaxrs.jar /usr/share/java/jackson/jackson-mapper-asl.jar /usr/share/java/jackson/jackson-mrbean.jar /usr/share/java/jackson/jackson-smile.jar /usr/share/java/jackson/jackson-xc.jar /usr/share/java/jackson-jaxrs-providers/jackson-jaxrs-base.jar /usr/share/java/jackson-jaxrs-providers/jackson-jaxrs-json-provider.jar /usr/share/java/idm-console-base.jar /usr/share/java/idm-console-mcc.jar /usr/share/java/idm-console-nmclf.jar /usr/share/java/jakarta-commons-httpclient.jar /usr/share/java/ldapjdk.jar /usr/share/java/jackson-annotations.jar /usr/share/java/jackson-core.jar /usr/share/java/jackson-databind.jar /usr/share/java/jackson-module-jaxb-annotations.jar /usr/share/java/resteasy/resteasy-jaxb-provider.jar /usr/share/java/resteasy/resteasy-atom-provider.jar /usr/share/java/apache-commons-lang.jar
The client uses JSS (NSS in Java) libraries to interact with the KRA. All interactions with the KRA require
client certificate authentication by a trusted agent.An admin user who is also a trusted agent is created
as part of the installation process. The PKCS12 file containing the cert/private key for this admin user can be found at
~/.dogtag/pki-tomcat/ca_admin_cert.p12
. Since you can only install the subsystems as a root user on your system, you should also
provide access to the file for all other users. You can use commands like certutil
and pk12util
, provided by nss-tools
package,
to create an NSS database, import the p12 file into the database and use it for authentication.
Following are the steps to setup an NSS database and import the cert stored in the p12 file:
root$ cp ~/.dogtag/pki-tomcat/ca_admin_cert.p12 /home/pki-user root$ chown pki-user:pki-user /home/pki-user/ca_admin_cert.p12 pki-user$ cd /tmp; mkdir nssdb pki-user$ certutil -N -d nssdb/ ** Enter the password for the cert database ** pki-user$ pk12util -i ~/ca_admin_cert.p12 -d nssdb/ ** Enter the passwords for the NSS database and the p12 file(default: Secret.123) ** You can view the imported cert by executing: pki-user$ certutil -L -d nssdb/ If you have the CA and KRA already installed, you can test authentication using CLI command (which uses the client internally) pki-user$ pki -d /tmp/nssdb -c <NSS DB Password> -n "<Certificate nickname>" key-find
This database can now be used for authentication.
Some of the functions in the key client require some cryptographic operations like generating a symmetric key, or wrapping a symmetric key with the KRA transport key. There are three possible options here. The method you select will depend on your particular environment.
In this option, an NSS database needs to be set up locally, and used by the Java client for crypto operations.
Since you will already have an NSS database for authentication, this approach takes the least effort to perform all the required crypto operations.
The Java class that implements these operations is NSSCryptoProvider
located in /usr/share/java/certsrv.jar
.
The constructor of NSSCryptoProvider
will initialize the NSS database at the location specified in the ClientConfig
object.
Here is how we pass on the NSSCryptoProvider
to the KeyClient
.
ClientConfig config = new ClientConfig();
config.setServerURI(protocol + "://" + host + ":" + port + "/kra");
config.setCertNickname(clientCertNickname);
config.setCertDatabase(NSS_DB_DIR);
config.setCertPassword(NSS_DB_PWD);
NSSCryptoProvider nss = new NSSCryptoProvider(config);
KRAClient client = new KRAClient(new PKIClient(config, nss));
SystemCertClient systemCertClient = (SystemCertClient) client.getClient("systemcert");
KeyClient keyClient = (KeyClient) client.getClient("key");
// Get transport certificate from KRA and pass it on to the KeyClient
transportCert = systemCertClient.getTransportCert().getEncoded();
transportCert = transportCert.substring(PKIService.HEADER.length(),
transportCert.indexOf(PKIService.TRAILER));
keyClient.setTransportCert(transportCert);
So you first create a ClientConfig
instance storing the details of the server URI, and the NSS database.
Create an NSSCryptoProvider
instance using the ClientConfig object and pass it on to the KRAClient
as part of the PKIClient
instance.
You can access the KeyClient
from the KRAClient
instance. The PKIClient
which has all the configuration details will
take care of the authenticating using the NSS database.
In this option, the crypto operations (generating keys/ wrapping/ unwrapping) would still
be done locally, but not using NSS for the cryptographic library. The NSSCryptoProvider
which does the crypto
operations is an implementation of an abstract base class CryptoProvider
. All the functions declared in the
CryptoProvider
class are used by the key client to perform the crypto operations.
To use something other than NSS then, you would need to subclass CryptoProvider
and implement the
abstract methods. The setup code would then be similar to the code shown above - except that
your subclass would be passed into the constructor for PKIClient
instead of the NSSCryptoUtil
class.
We plan to write an OpenSSLCryptoUtil
at some point soon as well.
In this option, all cryptographic operations are done outside of the Java key client and the relevant encrypted values are passed in when key client calls are made. This is the case when the symmetric keys and wrappings are being done on a separate application, and this application does not interact with the KRA directly.
As shown in the code samples above, the end result is a KeyClient
object which is defined in
KeyClient.java
. Furthermore, in the source code in KRATest.java, there are examples of the invocation
of the functions. You should look at that class to see all the relevant methods and more detailed
description of each method. We will describe the most common use cases below.
There are a few parameters that are worth mentioning though:
-
clientKeyId
: this is a label that is provided by the caller for the stored secret. Secrets can be either active or inactive, but there should only be one active secret per client key id. Attempting to archive or generate another key with the sameclient_key_id
will fail, if the existing key is active. It is possible to modify the status of an existing key using themodifyStatus()
call. Care should be exercised though - it is possible to modify more than key’s status to "active". It is also the responsibility of the caller to maintain uniqueness of aclient_key_id
. If this parameter is in the method definition, then it usually required. Note that there is currently a restriction that theclient_key_id
should not include "/" characters, -
keyId
: this is a unique identifier assigned to the secret by the KRA when it is generated or archived. It is uniquely only to this KRA (and its clones). -
A note about exceptions: In general, if invalid parameters are detected on the client side, an
IllegalArgumentException
is thrown. Server exceptions are thrown asPKIException
objects.
This function takes in some key parameters and returns the key_id
for the generated
(and archived) key. In future, it will also be able to return the generated key at the same
time. We will implement that functionality soon.
String clientKeyId = "Symmetric Key #1234f " + Calendar.getInstance().getTime().toString();
List<String> usages = new ArrayList<String>();
usages.add(SymKeyGenerationRequest.DECRYPT_USAGE);
usages.add(SymKeyGenerationRequest.ENCRYPT_USAGE);
KeyRequestResponse genKeyResponse = keyClient.generateSymmetricKey(clientKeyId,
KeyRequestResource.AES_ALGORITHM,
128, usages, null);
KeyId keyId = genKeyResponse.getKeyId();
Different methods, depending on the type of the secret and how it is passed, are provided by the Java client framework.
String passphrase = "Secret.123";
String clientKeyId = "UUID: 123-45-6789 RKEK " + Calendar.getInstance().getTime().toString();
KeyRequestResponse requestResponse = keyClient.archivePassphrase(clientKeyId, passphrase);
// Print the request information
printRequestInfo(requestResponse.getRequestInfo());
keyId = requestResponse.getKeyId();
The above code archives a secret of passphrase type. The secret is passed directly to the client without any encryption
i.e the crypto operations are done locally. A session key is generated and is used to wrap the secret. The session key is
then wrapped using the public key in the transport cert of the KRA. Both the transWrappedSessionKey
and sessionWrappedPassphrase
are
sent to the KRA, which does the decryption and archives the secret.
A symmetric key can be archived using the archiveSymmetricKey
method.
For the case where encryption is done externally, the method archiveEncryptedData
can be used, to archive the secret.
A secret can also be archived by creating a PKIArchiveOptions
object using the secret
(passphrase/symmetric key) and transport cert of the KRA. This object can be used to archive the secret.
String clientKeyId = "UUID: 123-45-6789 VEK " + Calendar.getInstance().getTime().toString();
SymmetricKey secret = CryptoUtil.generateKey(token, KeyGenAlgorithm.DES3);
byte[] iv = CryptoUtil.getNonceData(8);
byte[] encoded = nss.createPKIArchiveOptions(transportCert, secret, null, KeyGenAlgorithm.DES3, iv);
KeyRequestResponse info = keyClient.archivePKIOptions(clientKeyId, KeyRequestResource.SYMMETRIC_KEY_TYPE,
KeyRequestResource.DES3_ALGORITHM, 0, encoded);
printRequestInfo(info.getRequestInfo());
There are different methods depending on whether the client is doing crypto operations locally or whether the crypto operations are being performed outside of the Dogtag Python client.
This is the case where the crypto operations are done locally (as in section 1 or 2 in Cryptography section).
// get active key for a particular client ID
KeyId keyInfo = keyClient.getActiveKeyInfo(clientKeyId);
KeyId keyId2 = keyInfo.getKeyId();
Key keyData = keyClient.retrieveKey(keyId);
keyData
will contain information about the secret (algorithm etc.) along with the secret
as the attribute "data" since there is no transport key wrapped session key passed in the
retrieveKey
call.
The following code snippet is an example of encryption done externally, i.e. an application or an intermediate server which uses the client API to talk to the KRA.
// get active key for a particular client ID
KeyId keyInfo = keyClient.getActiveKeyInfo(clientKeyId);
KeyId keyId2 = keyInfo.getKeyId();
SymmetricKey sessionKey = crypto.generateSessionKey();
byte[] transWrappedSessionKey = crypto.wrapSessionKeyWithTransportCert(sessionKey, transportCert);
Key keyData = keyClient.retrieveKey(keyId, transWrappedSessionKey);
// executed on the client where session key was generated
byte[] encryptedKey = keyData.getEncryptedData();
byte[] unwrappedKey = crypto.unwrapWithSessionKey(encryptedKey, recoveryKey, KeyRequestResource.DES3
keyData.getNonceData());
// For a passphrase, secret will be the passphrase stored.
// For a symetric key, it is the encoded key string.
String secret = new String(unwrappedKey, "UTF-8");
transWrappedSessionKey
is a 168 bit 3DES symmetric key used as a session key, that has been wrapped by
the public key in the KRA transport certificate. This session key will be decoded on the KRA, and will
be used to wrap the secret. The encrypted secret will be returned in the Key
object.
The code sample above shows how to generate a transWrappedSessionKey
using NSSCryptoUtil(crypto)
to give you an
understanding of how the transWrappedSessionKey
is created and used to retrieve a key.
This is what is done in the client internally, when the method retrieveKey(KeyId keyId)
is called.
In addition to retrieving the key data wrapped in a symmetric key, it can also be retrieved wrapped in a pass-phrase.
The methods retrieveKeyByPassphrase
in the KeyClient
class provides this feature.