Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-8.cpp
#include <stdio.h>
#include <stdlib.h>
#include <memory> // nothrow
#include "p6skc.h"
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "p6gencerts.h"
#include "p6genkeys.h"
#include "cconsolestream.h"
using namespace P6R;
P6DECLARE_CID( p6KMIPClient );
P6DECLARE_CID( p6EntropySource );
P6DECLARE_CID( p6Random );
P6DECLARE_IID( p6IRunningIface );
P6DECLARE_CID( p6Cert );
P6DECLARE_CID( p6GenCerts );
P6DECLARE_CID( p6GenKeys );
P6DECLARE_CID( p6Sign );
P6DECLARE_CID( p6SymmetricCrypto );
P6DECLARE_CID( p6CryptoKey );
P6DECLARE_CID( p6Dir );
P6DECLARE_CID( p6UnbufferedFile );
P6DECLARE_CID( p6Keystore );
namespace {
class CKmipExample8
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream *pStreamDebug );
CKmipExample8()
: m_port(0), m_compatMask(0), m_pHostName(NULL)
{ }
~CKmipExample8()
{
if (NULL != m_pHostName) m_cpStr->wstrfree( m_pHostName );
if (NULL != m_cpStoreInit) m_cpStoreInit->close();
}
protected:
// -> connection related
P6CLASSMETHOD createSession( p6IKMIPClient* pClient, P6BOOL bWithCredentials );
P6CLASSMETHOD setPreferences( P6KMIP_PREF* pPrefs, P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy );
// -> managed object manipulation functions
P6CLASSMETHOD getAsymmetricKey( p6IKMIPClient* pClient, P6NCSTR keyId, P6UINT32& pubOrPriv, p6ICryptoKey** pKey );
P6CLASSMETHOD destroyObject( p6IKMIPClient* pClient, P6NCSTR objectId, P6BOOL bFree );
// -> function used often to extract a unique id returned by the server
P6CLASSMETHOD extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId );
P6R::P6BOOL isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id );
// -> supports crypto
P6CLASSMETHOD getRNG(P6R::p6IRandom **ppRandom);
P6CLASSMETHOD getIGenKeys( p6IGenKeys** ppGenKeys );
P6CLASSMETHOD getGenCerts( p6IGenCerts** ppIface );
// -> keystore related
P6CLASSMETHOD savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize );
P6CLASSMETHOD saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath );
P6CLASSMETHOD saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath );
P6CLASSMETHOD createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** pInit, p6IKeystore** pKeystore );
P6CLASSMETHOD loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey );
P6CLASSMETHOD loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert );
P6CLASSMETHOD createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey );
p6ComPtr<p6ISafeString> m_cpStr; // -> generic string library
p6ComPtr<p6ISymmetricCrypto> m_cpCrypto; // -> used to encrypt the contents of the keystore
p6ComPtr<p6ICryptoKey> m_cpSignKey; // -> keystore related
p6ComPtr<p6IRandom> m_cpRandom; // -> crypto related
p6ComPtr<p6IKeystore> m_cpKeystore; // -> the KMIP client requires that a properly initialzed keystore is setup
p6ComPtr<p6IKeystoreInit> m_cpStoreInit; // -> keystore related
P6UINT32 m_port; // -> KMIP server port to connect to
P6UINT32 m_compatMask; // -> compatibility mask: 0 use TTLV message encoding, 2 use XML message encoding, 4 use JSON message encoding
P6WCHAR* m_pHostName; // -> IP address or FQDN of KMIP server to connect to
};
// Create the key file (supplied by the server vendor) into a p6ICryptoKey object for SSL
//
P6CLASSMETHODIMPL CKmipExample8::createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey )
{
P6ERR err = eOk;
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6CryptoKey, VALIDATECOMPTR( p6ICryptoKeyInit, cpKeyInit ))))
{
if (P6SUCCEEDED( err = cpKeyInit->initialize( P6CKF_NONE, m_cpRandom )))
{
if (P6SUCCEEDED( err = cpKeyInit->loadPKCS8Key( keyBuffer.pString, (P6UINT32) keyBuffer.length, (P6UINT32) keySize )))
{
err = cpKeyInit->queryInterface( VALIDATEIF( p6ICryptoKey, ppNewKey ));
}
}
}
return err;
}
// Load the KMIP server vendor supplied certificate file for SSL into a p6ICert object
//
P6CLASSMETHODIMPL CKmipExample8::loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert )
{
P6ERR err = eOk;
if (P6FAILED( err = p6CreateCryptoInstance( CID_p6Cert, VALIDATECOMPTR( p6ICertInit, cpInit )))) return err;
if (P6FAILED( err = cpInit->initialize( P6CERT_NOFLAGS ))) return err;
if (P6SUCCEEDED( err = cpInit->loadCert( pPath ))) {
err = cpInit->queryInterface( VALIDATEIF( p6ICert, ppNewCert ));
}
return err;
}
// Load the PEM private key file (supplied by the server vendor) for SSL into a p6ICryptoKey object
//
P6CLASSMETHODIMPL CKmipExample8::loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey )
{
P6BSTR keyBuffer = {NULL, 0};
P6SIZE cBytesRead = 0;
P6ERR err = eNotFound;
if (NULL == pwszFilename) return eInvalidArg;
if (P6SUCCEEDED( err = p6CreateInstance( NULL, CID_p6UnbufferedFile, VALIDATECOMPTR( p6IUnbufferedFile, cpFile ))))
{
if (P6SUCCEEDED( err = cpFile->initialize()))
{
if (P6SUCCEEDED( err = cpFile->open( pwszFilename, P6FILE_READ|P6FILE_OPENEXISTING,P6FP_DEFAULT )))
{
P6FILEINFO info;
if ( P6SUCCEEDED( err = cpFile->stat( &info )))
{
if (info.m_cSize)
{
P6CHAR* pBuffer = new (std::nothrow) P6CHAR[(P6UINT32)info.m_cSize];
if ( pBuffer )
{
if (P6SUCCEEDED( err = cpFile->read( pBuffer,(P6UINT32)info.m_cSize, &cBytesRead )))
{
if (cBytesRead == info.m_cSize)
{
keyBuffer.length = cBytesRead;
keyBuffer.pString = (P6UCHAR*)pBuffer;
err = createPKCS8Key( keyBuffer, keySize, ppNewKey );
}
}
}
else err = eIoError;
delete [] pBuffer;
}
else err = eNoMemory;
}
else {
err = eTooSmall;
}
cpFile->close();
}
}
}
return err;
}
// The contents of the keystore is protected by a key
P6CLASSMETHODIMPL CKmipExample8::getIGenKeys( p6IGenKeys** ppGenKeys )
{
P6ERR err = eOk;
*ppGenKeys = NULL;
// Create an instance of the p6IGenKeys interface and then initilize it for use
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6GenKeys, VALIDATECOMPTR( p6IGenKeys, cpGenKeys ))))
{
if (P6SUCCEEDED( err = cpGenKeys->initialize( P6GENKEY_NOFLAGS, m_cpRandom )))
{
// On success, return an addref'd pointer to the interface, following P6COM reference counting rules.
// The detach method, transfers ownership of the interface to ppGenKeys without unneeded reference counting operations.
//
cpGenKeys.detach(ppGenKeys);
}
}
return err;
}
// We generate a certificate to go with the public / private key pair
P6CLASSMETHODIMPL CKmipExample8::getGenCerts( p6IGenCerts** ppIface )
{
P6ERR err = eOk;
*ppIface = NULL;
err = p6CreateCryptoInstance( CID_p6GenCerts, VALIDATEIF( p6IGenCerts, ppIface ));
if (P6SUCCEEDED( err ))
{
err = (*ppIface)->initialize( P6GENCERTS_NOFLAGS );
if (P6FAILED(err))
{
(*ppIface)->release();
*ppIface = NULL;
}
}
return err;
}
// Load in the root certificate we need to connect to the KMIP server into our new store
P6CLASSMETHODIMPL CKmipExample8::saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath )
{
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6WCHAR szHash[20] = {0};
P6SIZE nHash = 0;
P6ERR err = eOk;
p6ComPtr<p6ICert> cpNewCert;
P6ARG args[2];
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if ( P6SUCCEEDED( err = loadCertificateFromFile( confPath, cpNewCert.addressof() )))
{
// SSL library expects a specific namespace depending on how the cert is used
// We store the root cert by the has of its subject which during the SSL protocol is what OpenSSL looks for
if (P6SUCCEEDED( err = cpNewCert->getSubjectHash( &nHash )))
{
P6AI_SIZE_HEXL( args, nHash );
if (P6SUCCEEDED( err = m_cpStr->formatStringW( szHash, P6CNTOF(szHash), NULL, P6TEXT("%1$"), args, 1 )))
err = pKeystore->setCertificate( pNamespace, szHash, cpNewCert, NULL );
}
}
return err;
}
// KMIP requires both server and cliuent certificates, we store the certificate under the host name of the KMIP server
P6CLASSMETHODIMPL CKmipExample8::saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath )
{
p6ComPtr<p6ICert> cpNewCert;
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if (P6SUCCEEDED( err = loadCertificateFromFile( confPath, cpNewCert.addressof() )))
{
err = pKeystore->setCertificate( pNamespace, pName, cpNewCert, NULL );
}
return err;
}
// Load in the keys we need to connect to the KMIP server into our new store, we store the private key under the host name of the KMIP server
P6CLASSMETHODIMPL CKmipExample8::savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize )
{
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if (P6SUCCEEDED( err = loadKeyFromFile( confPath, keySize, cpNewKey.addressof() )))
{
err = pKeystore->setKey( pNamespace, pName, cpNewKey, NULL, 0, NULL );
}
return err;
}
// Create a keystore and then load it with the information we need to connect to the KMIP server.
// P6R's SSL looks into the keystore for certificates and the private key when it starts a connection to the KMIP server.
P6CLASSMETHODIMPL CKmipExample8::createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** ppInit, p6IKeystore** ppKeystore )
{
P6ERR err = eOk;
*ppInit = NULL;
*ppKeystore = NULL;
// Create the keystore and fill it with vendor provided SSL certificates
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Keystore, VALIDATEIF( p6IKeystoreInit, ppInit )))) return err;
err = (*ppInit)->queryInterface( VALIDATEIF( p6IKeystore, ppKeystore ));
if (P6FAILED( err = (*ppInit)->initialize( P6KEYSTORE_NOFLAGS, m_cpCrypto, SH_SHA256, m_cpSignKey )))
{
if (NULL != (*ppKeystore)) (*ppKeystore)->release();
(*ppKeystore) = NULL;
(*ppInit)->release();
(*ppInit) = NULL;
return err;
}
/*
* The first parameter of openSigned() is the file path where to create and access keystore databases.
* If NULL, then the keystore location will default to the P6R database directory (i.e., the "db" sub-directory).
* If the SKC is installed in a read-only directory then the first parameter will need to be set to
* an existing read/write directory.
*/
if (P6FAILED( err = (*ppInit)->openSigned( NULL, pKeystoreName )))
{
if (NULL != (*ppKeystore)) (*ppKeystore)->release();
(*ppKeystore) = NULL;
(*ppInit)->release();
(*ppInit) = NULL;
return err;
}
if (P6FAILED( err = saveRootCertInStore( (*ppKeystore), P6TEXT("p6r.trustedroot"), rootPEM ))) return err;
if (P6FAILED( err = saveClientCertInStore( (*ppKeystore), P6TEXT("p6r.clientauth"), m_pHostName, certPEM ))) return err;
if (P6FAILED( err = savePrivKeyInStore( (*ppKeystore), P6TEXT("p6r.clientauth"), m_pHostName, privPEM, privSize ))) return err;
return eOk;
}
// Key generation requires an entropy source
P6CLASSMETHODIMPL CKmipExample8::getRNG( p6IRandom** ppRandom )
{
P6ERR err = eOk;
p6ComPtr<p6IRunningIface> cpRIT( IID_p6IRunningIface, &err );
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6EntropySource, VALIDATECOMPTR( p6IEntropySource, cpSource ))))
{
if (P6SUCCEEDED( err = cpSource->initialize( P6ENTROPY_HIGH )))
{
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6Random, VALIDATECOMPTR( p6IRandomInit, cpInit ))))
{
if (P6SUCCEEDED( err = cpInit->initialize( P6RAND_NOFLAGS, cpSource ))) {
err = cpInit.queryInterface( VALIDATEIF( p6IRandom, ppRandom ));
}
}
}
}
return err;
}
// Replace the host name of "fqdn.com" to the fully qualified domain name of the server you wish to connect to.
P6R::P6ERR CKmipExample8::initialize()
{
P6ERR err = eOk;
P6WCHAR dbPath[P6MAXPATH+20] = {0};
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6ISafeString, m_cpStr )))) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Dir, VALIDATECOMPTR( p6IDir, cpDir )))) return err;
if (P6FAILED( err = cpDir->initialize())) return err;
if (P6FAILED( err = getRNG( m_cpRandom.addressof()))) return err;
// -> right now configuration is hard coded, but could read out of a config file later
// m_compatMask is set to zero for TTLV message encoding, set to 2 for XML message encoding, and set to 4 for JSON message encoding
//
m_port = 5696;
m_compatMask = 0;
if (P6FAILED( err = m_cpStr->wstrdup( P6TEXT("fqdn.com"), &m_pHostName ))) return err;
// -> create the keys required to encrypt the contents of the keystore
if (P6SUCCEEDED( err = getIGenKeys( cpGenKey.addressof() )))
{
err = cpGenKey->genSymmetricKey( m_cpSignKey.addressof(), 256, P6FALSE );
if (P6SUCCEEDED( err )) {
err = cpGenKey->genSymmetricKey( cpKey.addressof(), 256, P6FALSE );
}
}
if (P6SUCCEEDED( err )) err = p6CreateCryptoInstance( CID_p6SymmetricCrypto, VALIDATECOMPTR( p6ISymmetricCrypto, m_cpCrypto ));
if (P6SUCCEEDED( err )) err = m_cpCrypto->initialize( P6SYM_NOPADDING, CIPHER_AES_CFB );
if (P6SUCCEEDED( err )) err = m_cpCrypto->setKey( cpKey );
// -> initialize the keystore with keys provided to us by the server vendor
dbPath[0] = P6CHAR('\0');
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( dbPath, P6CNTOF(dbPath), P6TEXT("/db/KMIP12_keystore"), NULL ))) return err;
cpDir->unlink( dbPath );
dbPath[0] = P6CHAR('\0');
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( dbPath, P6CNTOF(dbPath), P6TEXT("/db/KMIP12_keystore.sig"), NULL ))) return err;
cpDir->unlink( dbPath );
return createKeystore( P6TEXT("KMIP12_keystore"), P6TEXT("RootCert.pem"), P6TEXT("ClientCert.pem"), P6TEXT("ClientPrivate.pem"), 2048, m_cpStoreInit.addressof(), m_cpKeystore.addressof());
}
// The client creates an SSL connection to the KMIP server and then makes its requests
// Configure the client on how it is supposed to behave (e.g., protocol version, use TTLV or XML or JSON, how to encode keys PKCSXXX).
//
P6CLASSMETHODIMPL CKmipExample8::setPreferences( P6KMIP_PREF* pPrefs, P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy )
{
m_cpStr->setMem( pPrefs, 0, sizeof( P6KMIP_PREF ));
pPrefs->pVersion = "1.2"; pPrefs->pSubdirectory = pLogDir;
pPrefs->asynchronous = asynchronous; pPrefs->maxBufferSize = maxBufferSize;
pPrefs->connectTimeout = connectTimeout; pPrefs->sendTimeout = sendTimeout;
pPrefs->receiveTimeout = receiveTimeout; pPrefs->initialBufCount = initialBufCount;
pPrefs->growBufsBy = growBufsBy; pPrefs->privateKeyEncoding = privateKeyEncoding;
pPrefs->publicKeyEncoding = publicKeyEncoding; pPrefs->symmetricKeyEncoding = symmetricKeyEncoding;
pPrefs->compatibility1 = m_compatMask;
return eOk;
}
// We just hard code the credentials (i.e., name and password) for a test if needed
P6CLASSMETHODIMPL CKmipExample8::createSession( p6IKMIPClient* pClient, P6BOOL bWithCredentials )
{
P6KMIP_CREDENTIAL credential;
P6ERR err = eOk;
// -> integration testing setups are often not production quality, some use IP addresses instead of FQDN and thus a non-secure setup
if (P6FAILED( err = pClient->setSSLOptions( NULL, (P6SSF_METHOD_TLS1 | P6SSF_SECURE_CLIENT | P6SSF_SECURE_CLIENT_AUTH | P6SSF_LOG_X509SUBJECTLOOKUPS | P6SSF_VRFY_DISABLEHOSTMATCH)))) return err;
m_cpStr->setMem( &credential, 0, sizeof( P6KMIP_CREDENTIAL ));
credential.type = 1;
credential.value.password.userName.pString = "Fred";
credential.value.password.userName.length = 4;
credential.value.password.password.pString = "password1";
credential.value.password.password.length = 9;
return pClient->open( m_pHostName, m_port, (bWithCredentials ? &credential : NULL));
}
// Just return the very first unique identifier from the enumerator
// Some calls can result in multiple unique identifiers returned at the same time (e.g., locate object)
P6CLASSMETHODIMPL CKmipExample8::extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId )
{
P6CHAR* pGUID = NULL;
P6NCSTR buffer = { NULL, 0 };
P6ERR err = eOk;
pUniqueId->pString = NULL;
pUniqueId->length = 0;
err = pEnum->reset();
err = pEnum->next( &buffer );
if ( 0 < buffer.length )
{
if (NULL == (pGUID = new (std::nothrow) P6CHAR[buffer.length + 2])) return eNoMemory;
pGUID[0] = 0;
buffer.pString = pGUID;
buffer.length += 2;
if (P6FAILED( err = pEnum->next( &buffer ))) return err;
pUniqueId->pString = buffer.pString;
pUniqueId->length = buffer.length;
}
else err = eFail;
return err;
}
// Verify the string returned in pUniqueId matches that in the id parameter
P6R::P6BOOL CKmipExample8::isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id )
{
P6NCSTR enumStr = {NULL, 0};
P6INT32 match = -1;
P6ERR err = eOk;
if (NULL != pUniqueId)
{
if (P6FAILED( err = extractUniqueId( pUniqueId, &enumStr ))) return P6FALSE;
if (enumStr.length == id.length)
{
err = m_cpStr->strcmp( enumStr.pString, id.pString, id.length, &match );
delete [] enumStr.pString;
}
}
return (0 == match);
}
// When trying to destroy a key this can fail if the key is in an active state. Then we must retry first revoking the key.
//
P6CLASSMETHODIMPL CKmipExample8::destroyObject( p6IKMIPClient* pClient, P6NCSTR objectId, P6BOOL bFree )
{
P6KMIP_RESULT resultCodes;
p6IKMIPStr* pUniqueId = NULL;
P6BOOL bMatch = P6FALSE;
P6ERR err = eOk;
if (NULL != objectId.pString)
{
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
err = pClient->destroyObject( objectId, NULL, &pUniqueId, &resultCodes );
if ( P6FAILED( err )) {
printf("\ncall to destroyObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error - destroy object\n");
}
if (NULL != pUniqueId)
{
bMatch = isEqualId( pUniqueId, objectId );
if (!bMatch) {
printf("\nKMIP server to destroy object with wrong object uniqueId\n");
}
if (bFree) delete [] objectId.pString;
pUniqueId->release();
}
// -> did we fail trying to destroy the object?
if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) return eFail;
}
return err;
}
// Can we understand the format of an asymmetric key returned to us?
P6CLASSMETHODIMPL CKmipExample8::getAsymmetricKey( p6IKMIPClient* pClient, P6NCSTR keyId, P6UINT32& pubOrPriv, p6ICryptoKey** pKey )
{
P6KMIP_RESULT resultCodes;
P6KMIP_GETPARAMS getParams;
P6KMIP_MANAGED managedObj;
P6BOOL bMatch = P6FALSE;
P6INT32 cryptoAlg = 0;
P6INT32 cryptoLength = 0;
P6ERR err = eOk;
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &getParams, 0, sizeof( P6KMIP_GETPARAMS ));
m_cpStr->setMem( &managedObj, 0, sizeof( P6KMIP_MANAGED ));
*pKey = NULL;
getParams.uniqueId.pString = keyId.pString;
getParams.uniqueId.length = keyId.length;
err = pClient->getObject( getParams, &managedObj, &resultCodes );
if ( P6FAILED( err )) {
printf("\ncall to getObject has failed %x\n", err );
return err;
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error - get object\n");
return eFail;
}
if (NULL != managedObj.pUniqueId)
{
// -> if the key returned is not what we asked for then error
bMatch = isEqualId( managedObj.pUniqueId, keyId );
// -> we can return the meta data if from this function or pull it out of the pKey object (also stored in there)
pubOrPriv = managedObj.type;
switch ( pubOrPriv ) {
cryptoAlg = managedObj.value.publicKey.cryptoAlgorithm;
cryptoLength = managedObj.value.publicKey.cryptoAlgorithm;
(*pKey) = managedObj.value.publicKey.value.pKey;
if (NULL != managedObj.value.publicKey.pAttributes) managedObj.value.publicKey.pAttributes->release();
break;
cryptoAlg = managedObj.value.privateKey.cryptoAlgorithm;
cryptoLength = managedObj.value.privateKey.cryptoAlgorithm;
(*pKey) = managedObj.value.privateKey.value.pKey;
if (NULL != managedObj.value.privateKey.pAttributes) managedObj.value.privateKey.pAttributes->release();
break;
default:
err = eNotImplemented;
break;
}
}
managedObj.pUniqueId->release();
return err;
}
// Asymmtric keys have a different set of attributes to set than symmetric keys expected by a KMIP server.
// Main client function
// Identical to the KMIP TC_81_12 interop test case.
//
P6CLASSMETHODIMPL CKmipExample8::run( p6IDataStream *pStreamDebug )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_LOCATEPARAMS locateParams;
P6KMIP_PAIRPARAMS keyParams;
P6KMIP_TEMPLATEATTRIBUTE privateAttrib;
P6KMIP_ATTRIBUTE commonList[3];
P6KMIP_ATTRIBUTE privateList[3];
P6KMIP_ATTRIBUTE publicList[3];
P6NCSTR privId = {NULL, 0};
P6NCSTR pubId = {NULL, 0};
p6IKMIPStr* pUniqueId = NULL;
p6ICryptoKey* pKey1 = NULL;
p6ICryptoKey* pKey2 = NULL;
P6BOOL bMatch = P6FALSE;
P6UINT32 pubOrPriv = 0;
P6ERR err = eOk;
// [1] Initialize, "TC_81_12" directory created in the configured log directory to hold all logs for this example
// -> the KMIP client is just a component, the using code can create any number of them as each is independent of all others
//
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPClient, VALIDATECOMPTR( p6IKMIPClient, cpClient )))) return err;
setPreferences( &preferences, P6TEXT("TC_81_12"), 0, 0, 0, 0, 60000, 30000, 30000, 120000, 2, 2 );
err = cpClient->initialize( (P6KMIPFLG_TRACE_MSGS | P6KMIPFLG_TRACE_FORMATKMIPXML), m_cpKeystore, preferences );
// [2] Open a connection to the KMIP server via SSL
if (P6FAILED( err = createSession( cpClient, P6FALSE ))) return err;
// [3] Provide the details the KMIP server requires so it can create the key pair for us
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &privKey, 0, sizeof( P6KMIP_NEWOBJECT ));
m_cpStr->setMem( &pubKey, 0, sizeof( P6KMIP_NEWOBJECT ));
m_cpStr->setMem( &keyParams, 0, sizeof( P6KMIP_PAIRPARAMS ));
m_cpStr->setMem( &commonAttrib, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
m_cpStr->setMem( &privateAttrib, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
m_cpStr->setMem( &publicAttrib, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
commonList[0].index = 0;
commonList[0].value.cryptoAlgorithm = KMIP_RSA;
commonList[1].type = KMIP_ATTRIB_CRYPTOLENGTH;
commonList[1].index = 0;
commonList[1].value.cryptoLength = 1024;
commonAttrib.attribCount = 2;
commonAttrib.pAttributeList = commonList;
privateList[0].type = KMIP_ATTRIB_NAME;
privateList[0].index = 0;
privateList[0].value.name.type = KMIP_NAME_TEXTSTR;
privateList[0].value.name.value.pString = "TC-81-12-privatekey1";
privateList[0].value.name.value.length = 20;
privateList[1].index = 0;
privateAttrib.attribCount = 2;
privateAttrib.pAttributeList = privateList;
publicList[0].type = KMIP_ATTRIB_NAME;
publicList[0].index = 0;
publicList[0].value.name.type = KMIP_NAME_TEXTSTR;
publicList[0].value.name.value.pString = "TC-81-12-publickey1";
publicList[0].value.name.value.length = 19;
publicList[1].index = 0;
publicAttrib.attribCount = 2;
publicAttrib.pAttributeList = publicList;
keyParams.pCommonAttributes = &commonAttrib;
keyParams.pPrivateKeyAttributes = &privateAttrib;
keyParams.pPublicKeyAttributes = &publicAttrib;
if (P6FAILED( err = cpClient->createKeyPairObjects( keyParams, &privKey, &pubKey, &resultCodes ))) {
printf("\ncall to createKeyPairObjects has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
// [4] Extract the unique ids assigned by the KMIP server for each key
if (NULL != privKey.pUniqueId) {
err = extractUniqueId( privKey.pUniqueId, &privId );
privKey.pUniqueId->release();
}
if (NULL != pubKey.pUniqueId) {
err = extractUniqueId( pubKey.pUniqueId, &pubId );
pubKey.pUniqueId->release();
}
// [5] Search for the keys by unique id
// -> when looking for a public key we use the private key's link to show they are linked together
if (NULL != pubId.pString)
{
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &locateParams, 0, sizeof( P6KMIP_LOCATEPARAMS ));
commonList[0].type = KMIP_ATTRIB_OBJECTTYPE;
commonList[0].index = 0;
commonList[0].value.objectType = KMIP_OBJECT_PUBLICKEY; // -> search for a public key
commonList[1].type = KMIP_ATTRIB_LINK;
commonList[1].index = 0;
commonList[1].value.link.linkType = KMIP_LINK_PRIVATEKEY; // -> a public key linked to this private key
commonList[1].value.link.linkObjectId = privId;
locateParams.attribCount = 2;
locateParams.pAttributeList = commonList;
pUniqueId = NULL;
if ( P6FAILED(err = cpClient->locateObject( locateParams, &pUniqueId, &resultCodes ))) {
printf("\ncall to locateObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != pUniqueId) {
// -> did we find the public key using the private key's link?
bMatch = isEqualId( pUniqueId, pubId );
pUniqueId->release();
}
}
if (NULL != privId.pString)
{
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &locateParams, 0, sizeof( P6KMIP_LOCATEPARAMS ));
commonList[0].type = KMIP_ATTRIB_OBJECTTYPE;
commonList[0].index = 0;
commonList[0].value.objectType = KMIP_OBJECT_PRIVATEKEY; // -> search for a private key
commonList[1].type = KMIP_ATTRIB_LINK;
commonList[1].index = 0;
commonList[1].value.link.linkType = KMIP_LINK_PUBLICKEY; // -> a private key linked to this public key
commonList[1].value.link.linkObjectId = pubId;
locateParams.attribCount = 2;
locateParams.pAttributeList = commonList;
pUniqueId = NULL;
if ( P6FAILED( err = cpClient->locateObject( locateParams, &pUniqueId, &resultCodes ))) {
printf("\ncall to locateObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != pUniqueId) {
// -> did we find the public key using the private key's link?
bMatch = isEqualId( pUniqueId, pubId );
pUniqueId->release();
}
}
// [7] Retrive the public / private keys from the KMIP server
// -> application then does what it wants with the keys
err = getAsymmetricKey( cpClient, pubId, pubOrPriv, &pKey1 ); // -> should retrieve the public key from the KMIP server
err = getAsymmetricKey( cpClient, privId, pubOrPriv, &pKey2 ); // -> should retrieve the private key from the KMIP server
// -> to extract the key material from a p6ICryptoKey object, see p6cryptokey.h
// err = pKey1->getRSAPublicKey( P6BSTR* pN, P6BSTR* pE );
// err = pKey2->getRSAPrivateKey( P6BSTR* pN, P6BSTR* pD, P6BSTR* pE, P6BSTR* pP, P6BSTR* pQ, P6BSTR* pPrimeExpP, P6BSTR* pPrimeExpQ, P6BSTR* pCRT );
// OR err = pKey2->getKeyPKCS8( P6UCHAR* pBuffer, P6UINT32 cBuffer, P6BOOL wantPEM, P6UINT32* pWritten ); // -> for private key only
// OR err = pKey2->getKeyPKCS1( P6UCHAR* pBuffer, P6UINT32 cBuffer, P6BOOL wantPEM, P6UINT32* pWritten ); // -> for public or private key
// [8] Finished clean up remove the keys off of the KMIP server
destroyObject( cpClient, privId, P6TRUE );
destroyObject( cpClient, pubId, P6TRUE );
err = cpClient->close();
if (pKey1) pKey1->release();
if (pKey2) pKey2->release();
return err;
}
P6VOID KMIP_TC_81_12( p6IDataStream* pDataStream )
{
CKmipExample8 example;
P6CHAR szTmp[32];
P6ERR err = eOk;
if (P6SUCCEEDED( err = example.initialize())) {
err = example.run(pDataStream);
}
printf( "KMIP client result: [ %s ]\n", p6ErrToStr(err, &szTmp[0], P6CHARCNT(szTmp)) );
}
} // namespace
// p6InitializeLoader() must be called before anything else can run.
//
int main(int argc,char *argv[])
{
P6ERR err = eOk;
if ( P6SUCCEEDED( err = P6EXAMPLES::CConsoleStream::createInstance( NULL, VALIDATECOMPTR( p6IDataStream, cpDataStream ))))
{
if ( P6SUCCEEDED( err = p6InitializeLoader( cpDataStream, 9, P6SCLF_ALLLOG )))
{
KMIP_TC_81_12( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}