Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-2.cpp
#if defined( P6_LINUX ) || defined( P6_SOLARIS )
#include <unistd.h>
#else
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <memory> // nothrow
#include "p6com.h"
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6regex.h" // Narrow string regular expression interfaces
#include "p6wregex.h" // Wide string regular expression interfaces
#include "p6split.h" // Narrow split and and explode using regex
#include "p6wsplit.h" // Wide split and and explode using wregex
#include "p6jsonreader.h" // Streaming JSON parser interfaces
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6sax2contenthandler.h" // SAX2 content handler definitions
#include "p6sax2xmlreader.h" // Definitions for the main SAX2 interface
#include "p6sax2errorhandler.h" // SAX2 error handler definitions
#include "p6domxml.h" // DOM Parser interface definitions
#include "p6xmlnode.h" // XML and JSON node definitions
#include "p6xpathexpression.h" // XPATH 2.0 expression compilation interface definitions
#include "p6domnodeset.h" // DOM node enumeration interface definitions
#include "p6domnodesetsort.h" // DOM Node sorting interface definitions
#include "p6keystore.h"
#include "p6kmip.h"
#include "p6kmipclient.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( p6Time );
P6DECLARE_CID( p6IoBufferFactory );
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 CKmipExample2
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream *pStreamDebug );
CKmipExample2()
: m_port(0), m_compatMask(0), m_pHostName(NULL)
{ }
~CKmipExample2()
{
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, const P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy );
// -> common actions moved into functions
P6CLASSMETHOD getSymmetricKey( p6IKMIPClient* pClient, P6NCSTR keyId, P6UINT32 cipher, P6UINT32 keyLength, p6ICryptoKey** pKey );
P6CLASSMETHOD verifyKeyProperties( P6KMIP_MANAGED managedObj, P6NCSTR keyId, P6UINT32 cipher, P6UINT32 keyLength, p6ICryptoKey** pKey );
P6CLASSMETHOD extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId );
P6R::P6BOOL isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id );
P6R::P6BOOL isIdContainedIn( p6IKMIPStr* pUniqueId, P6NCSTR id );
// -> supports crypto
P6CLASSMETHOD getRNG(P6R::p6IRandom **ppRandom);
P6CLASSMETHOD getIGenKeys( p6IGenKeys** ppGenKeys );
// -> keystore related
P6CLASSMETHOD keystoreAddRootCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszCertificateFile );
P6CLASSMETHOD keystoreAddClientCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszHostname, const P6WCHAR* pszPrivateKeyFile, const P6WCHAR* pszCertificateFile );
P6CLASSMETHOD createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, p6IKeystoreInit** pInit, p6IKeystore** pKeystore );
P6CLASSMETHOD createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey );
// -> supports asynchronous mode
P6CLASSMETHOD asynchClient( P6NCSTR keyId );
P6CLASSMETHOD extractAsynchHandle( p6IKMIPBinary* pAsynch, P6BCSTR* pAsynchCorrelation );
P6CLASSMETHOD pollForResult( p6IKMIPClient* pClient, P6BCSTR asynchCorrelation, p6IKMIPResult** pResult, P6UINT32 maxSleep );
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 CKmipExample2::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;
}
// The contents of the keystore is protected by a key
P6CLASSMETHODIMPL CKmipExample2::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;
}
P6CLASSMETHODIMPL CKmipExample2::keystoreAddRootCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszCertificateFile )
{
P6WCHAR certPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (!pKeystore || !pszCertificateFile) return eInvalidArg;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, certPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), pszCertificateFile, NULL ))) return err;
p6ComPtr<p6IKeystoreSSL> cpSSLHelp( pKeystore, IID_p6IKeystoreSSL, &err );
if (P6SUCCEEDED( err )) {
err = cpSSLHelp->importTrustedRootCertFromPEMFile( certPath, NULL );
}
return err;
}
P6CLASSMETHODIMPL CKmipExample2::keystoreAddClientCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszHostname, const P6WCHAR* pszPrivateKeyFile, const P6WCHAR* pszCertificateFile )
{
P6WCHAR certPath[P6MAXPATH+2] = {0};
P6WCHAR keyPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (!pKeystore || !pszHostname || !pszPrivateKeyFile || !pszCertificateFile ) return eInvalidArg;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, keyPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( keyPath, P6CNTOF(keyPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( keyPath, P6CNTOF(keyPath), pszPrivateKeyFile, NULL ))) return err;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, certPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), pszCertificateFile, NULL ))) return err;
p6ComPtr<p6IKeystoreSSL> cpSSLHelp( pKeystore, IID_p6IKeystoreSSL, &err );
if (P6SUCCEEDED( err )) {
err = cpSSLHelp->importCredentialsPEM( P6TRUE, pszHostname, keyPath, certPath, NULL, 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 CKmipExample2::createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, p6IKeystoreInit** pInit, p6IKeystore** pKeystore )
{
P6ERR err = eOk;
*pInit = NULL;
*pKeystore = NULL;
// Create the keystore and fill it with vendor provided SSL certificates
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Keystore, VALIDATEIF( p6IKeystoreInit, pInit )))) return err;
err = (*pInit)->queryInterface( VALIDATEIF( p6IKeystore, pKeystore ));
if (P6FAILED( err = (*pInit)->initialize( P6KEYSTORE_NOFLAGS, m_cpCrypto, SH_SHA256, m_cpSignKey )))
{
if (NULL != (*pKeystore)) (*pKeystore)->release();
(*pKeystore) = NULL;
(*pInit)->release();
(*pInit) = 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 = (*pInit)->openSigned( NULL, pKeystoreName )))
{
if (NULL != (*pKeystore)) (*pKeystore)->release();
(*pKeystore) = NULL;
(*pInit)->release();
(*pInit) = NULL;
return err;
}
if (P6FAILED( err = keystoreAddRootCertFromFile( (*pKeystore), rootPEM ))) return err;
if (P6FAILED( err = keystoreAddClientCertFromFile( (*pKeystore), m_pHostName, privPEM, certPEM ))) return err;
return eOk;
}
// Key generation requires an entropy source
P6CLASSMETHODIMPL CKmipExample2::getRNG(P6R::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 CKmipExample2::initialize()
{
P6ERR err = eOk;
P6WCHAR dbPath[P6MAXPATH+200] = {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"), 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 CKmipExample2::setPreferences( P6KMIP_PREF* pPrefs, const 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 CKmipExample2::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 CKmipExample2::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;
}
// Extract the binary Asynchronous Correlation Value so that we can use it in a poll.
P6CLASSMETHODIMPL CKmipExample2::extractAsynchHandle( p6IKMIPBinary* pAsynch, P6BCSTR* pAsynchCorrelation )
{
P6UCHAR* pHandle = NULL;
P6BSTR buffer = { NULL, 0 };
P6ERR err = eOk;
pAsynchCorrelation->pString = NULL;
pAsynchCorrelation->length = 0;
err = pAsynch->reset();
err = pAsynch->next( &buffer );
if ( 0 < buffer.length )
{
if (NULL == (pHandle = new(std::nothrow) P6UCHAR[buffer.length + 2])) return eNoMemory;
pHandle[0] = 0;
buffer.pString = pHandle;
buffer.length += 2;
if (P6FAILED( err = pAsynch->next( &buffer ))) return err;
pAsynchCorrelation->pString = buffer.pString;
pAsynchCorrelation->length = buffer.length;
}
else err = eFail;
pAsynch->release();
return err;
}
// Verify the string returned in pUniqueId matches that in the id parameter
P6R::P6BOOL CKmipExample2::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);
}
// What if multiple unique Ids are returned (e.g., locate object can do that)? We want to see if the id parameter is one of them
P6R::P6BOOL CKmipExample2::isIdContainedIn( p6IKMIPStr* pUniqueId, P6NCSTR id )
{
P6CHAR tempName[300];
P6NCSTR buffer = { tempName, 300 };
P6INT32 match = -1;
P6ERR err = eOk;
if (NULL != pUniqueId)
{
err = pUniqueId->reset();
while( P6SUCCEEDED( err = pUniqueId->next( &buffer )))
{
match = -1;
if (buffer.length == id.length)
{
if (P6SUCCEEDED( err = m_cpStr->strcmp( buffer.pString, id.pString, id.length, &match ))) {
if (0 == match) { pUniqueId->release(); return P6TRUE; }
}
}
buffer.length = 300;
}
pUniqueId->release();
}
return P6FALSE;
}
// Ask the KMIP server to return the key material matching the unique Key identifier
P6CLASSMETHODIMPL CKmipExample2::getSymmetricKey( p6IKMIPClient* pClient, P6NCSTR keyId, P6UINT32 cipher, P6UINT32 keyLength, p6ICryptoKey** pKey )
{
P6KMIP_RESULT resultCodes;
P6KMIP_GETPARAMS getParams;
P6KMIP_MANAGED managedObj;
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;
pClient->getObject( getParams, &managedObj, &resultCodes );
return verifyKeyProperties( managedObj, keyId, cipher, keyLength, pKey );
}
// Verify that the returned key is as we expected
P6CLASSMETHODIMPL CKmipExample2::verifyKeyProperties( P6KMIP_MANAGED managedObj, P6NCSTR keyId, P6UINT32 cipher, P6UINT32 keyLength, p6ICryptoKey** pKey )
{
P6ERR err = eOk;
if (NULL != managedObj.pUniqueId)
{
if (KMIP_OBJECT_SYMMETRICKEY == managedObj.type)
{
if (cipher != managedObj.value.symmetricKey.cryptoAlgorithm) {
printf("\nunexpected key type returned\n");
}
if (keyLength != managedObj.value.symmetricKey.cryptoLength) {
printf("\nunexpected key length returned\n");
}
(*pKey) = managedObj.value.symmetricKey.value.pKey;
if (NULL != (*pKey))
{
P6CRYPTOKEYCLASS keyClass;
P6CRYPTOKEYTYPE keyType;
P6UUID Guid;
P6INT32 keySize = 0;
P6INT32 version = 0;
err = (*pKey)->getInfo( &keyClass, &keyType, &Guid, &keySize, &version );
if (keyClass != CKC_SYMMETRIC) {
printf("\nkey meta-data class not as expected\n");
}
if (keyType != CKT_SYM) {
printf("\nkey meta-data type not as expected\n");
}
if (((P6UINT32)keySize) != keyLength) {
printf("\nkey meta-data length not as exoected\n");
}
}
}
}
return err;
}
// When performing a KMIP operation there is a flag that indicates if its ok for the server to perform that operation asynchronously.
// If so the KMIP server just returns back a handle to the client so the client can poll for its completion. The client of course
// can go off and do anything else and periodically check for completion. In this test we just poll until it is complete.
//
// Key / certificate generation can take a while so we have to come back later and verify that its over.
// We need to sleep and wake up periodically to see if the server action is done. Or we could save the pending operation on a queue and
// perform other actions between the queries to the server testing to see if the operation has completed.
P6CLASSMETHODIMPL CKmipExample2::pollForResult( p6IKMIPClient* pClient, P6BCSTR asynchCorrelation, p6IKMIPResult** pResult, P6UINT32 maxSleep )
{
P6KMIP_BATCHRESULT batchResponse;
P6UINT32 maxWait = 0;
P6ERR err = eOk;
err = pClient->poll( asynchCorrelation, NULL, cpQuery.addressof() );
if (eNotSupported == err) err = pClient->poll( asynchCorrelation, NULL, cpQuery.addressof() );
m_cpStr->setMem( &batchResponse, 0, sizeof( P6KMIP_BATCHRESULT ));
err = cpQuery->next( &batchResponse );
// printf( "\npoll result 2 %d %d\n", batchResponse.result.resultStatus, err );
while( KMIP_RESULT_PENDING == batchResponse.result.resultStatus && maxSleep > maxWait && P6SUCCEEDED( err ))
{
#if defined( P6_LINUX ) || defined( P6_SOLARIS )
sleep( 2 );
#else
Sleep( 2000 );
#endif
if (P6FAILED( err = pClient->poll( asynchCorrelation, NULL, cpQuery.addressofWithRelease() ))) return err;
m_cpStr->setMem( &batchResponse, 0, sizeof( P6KMIP_BATCHRESULT ));
err = cpQuery->next( &batchResponse );
// printf( "\npoll result 3 %d %d\n", batchResponse.result.resultStatus, err );
maxWait++;
}
if (maxSleep < maxWait) return ePending;
cpQuery->reset();
cpQuery.detach( pResult );
return err;
}
// This example follows the TC_32_12 interop test case which uses two KMIP clients to perform the test.
// This function creates and runs the 2nd client.
//
P6CLASSMETHODIMPL CKmipExample2::asynchClient( P6NCSTR keyId )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_LOCATEPARAMS locateParams;
P6KMIP_BATCHRESULT batchResponse;
P6KMIP_ATTRIBUTE attributeList[6];
P6BCSTR asynchCorr = {NULL, 0};
P6BOOL bMatch = P6FALSE;
P6UINT32 cancelResult = 0;
P6UINT32 number = 0;
P6ERR err = eOk;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPClient, VALIDATECOMPTR( p6IKMIPClient, cpClient )))) return err;
// -> ** the asynch flag is set here **
setPreferences( &preferences, P6TEXT("TC_32_12"), 1, 0, 0, 0, 60000, 30000, 30000, 120000, 2, 2 );
err = cpClient->initialize( (P6KMIPFLG_TRACE_MSGS | P6KMIPFLG_TRACE_FORMATKMIPXML), m_cpKeystore, preferences );
if (P6FAILED( err = createSession( cpClient, P6FALSE ))) { return err; }
// [A] Locate managed object by name (which is a alias assigned by the client)
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &locateParams, 0, sizeof( P6KMIP_LOCATEPARAMS ));
attributeList[0].type = KMIP_ATTRIB_OBJECTTYPE;
attributeList[0].index = 0;
attributeList[0].value.objectType = KMIP_OBJECT_SYMMETRICKEY;
attributeList[1].type = KMIP_ATTRIB_NAME;
attributeList[1].index = 0;
attributeList[1].value.name.type = KMIP_NAME_TEXTSTR;
attributeList[1].value.name.value.pString = "TC-32-12-key20";
attributeList[1].value.name.value.length = 14;
locateParams.attribCount = 2;
locateParams.pAttributeList = attributeList;
// -> since we set the asynchronous mode this call is non-blocking, KMIP server sends back a partial result
if (P6FAILED( err = cpClient->locateObject( locateParams, cpUniqueId.addressof(), &resultCodes ))) {
printf("\ncall to locateObject by name has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error when calling locateObject by Name\n");
}
// -> do we have to poll for the final result or is a previous request now done
if ( KMIP_RESULT_PENDING == resultCodes.resultStatus && NULL != resultCodes.pAsynchCorrelation )
{
if (P6FAILED( err = extractAsynchHandle( resultCodes.pAsynchCorrelation, &asynchCorr ))) {
cpClient->close();
return err;
}
// -> go to sleep and wake up periodically to test to see of the operation has completed on the server
if ( P6SUCCEEDED( err = pollForResult( cpClient, asynchCorr, cpPollResult.addressof(), 60 )))
{
// -> we can query the enumerator for the number of items it contains
err = cpPollResult->count( &number );
m_cpStr->setMem( &batchResponse, 0, sizeof( P6KMIP_BATCHRESULT ));
if (P6SUCCEEDED( err = cpPollResult->next( &batchResponse )))
{
if (KMIP_RESULT_SUCCESS != batchResponse.result.resultStatus) {
printf("\ncall to pollResult has failed\n" );
}
if (KMIP_OP_LOCATE != batchResponse.type) {
printf("\ncall to pollResult type not as expected\n" );
}
if (1 != batchResponse.batchIndex) {
printf("\ncall to pollResult batch request index not as expected\n" );
}
}
}
else {
printf("\nERROR: call to pollResult was not long enough for the operation to finish.\n" );
}
}
if (NULL != cpUniqueId)
{
bMatch = isEqualId( cpUniqueId, keyId );
if (!bMatch) {
printf("\nexpected key located by group--asynch client was not returned as expected\n" );
}
// -> swith mode to synchronous (just on this client connection) to get the key material
cpClient->setAsynchronousIndicator( P6FALSE );
err = getSymmetricKey( cpClient, keyId, KMIP_AES, 128, cpKey.addressof() );
if (P6SUCCEEDED(err) && NULL != cpKey) cpKey = NULL; // Smart pointer releases the interface
cpClient->setAsynchronousIndicator( P6TRUE );
}
// [B] Locate managed object by group the key was placed in the server
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &locateParams, 0, sizeof( P6KMIP_LOCATEPARAMS ));
attributeList[0].type = KMIP_ATTRIB_OBJECTTYPE;
attributeList[0].index = 0;
attributeList[0].value.objectType = KMIP_OBJECT_SYMMETRICKEY;
attributeList[1].type = KMIP_ATTRIB_OBJECTGROUP;
attributeList[1].index = 0;
attributeList[1].value.objectGroup.pString = "Group1";
attributeList[1].value.objectGroup.length = 6;
locateParams.attribCount = 2;
locateParams.pAttributeList = attributeList;
// -> again a non-blocking call
if (P6FAILED( err = cpClient->locateObject( locateParams, cpUniqueId.addressofWithRelease(), &resultCodes ))) {
printf("\ncall to locateObject by Group has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
// -> do we have to poll for the final result or is a previous request now done
if ( KMIP_RESULT_PENDING == resultCodes.resultStatus && NULL != resultCodes.pAsynchCorrelation )
{
err = extractAsynchHandle( resultCodes.pAsynchCorrelation, &asynchCorr );
if (P6FAILED( err )) return err;
// -> go to sleep and wake up periodically to test to see of the operation has completed on the server
if ( P6SUCCEEDED( err = pollForResult( cpClient, asynchCorr, cpPollResult.addressofWithRelease(), 60 )))
{
// -> we can query the enumerator for the number of items it contains
err = cpPollResult->count( &number );
m_cpStr->setMem( &batchResponse, 0, sizeof( P6KMIP_BATCHRESULT ));
if (P6SUCCEEDED( err = cpPollResult->next( &batchResponse )))
{
if (KMIP_RESULT_SUCCESS != batchResponse.result.resultStatus) {
printf("\ncall to pollResult has failed\n" );
}
if (KMIP_OP_LOCATE != batchResponse.type) {
printf("\ncall to pollResult type not as expected\n" );
}
if (1 != batchResponse.batchIndex) {
printf("\ncall to pollResult batch request index not as expected\n" );
}
}
}
else {
printf("\nERROR: call to pollResult was not long enough for the operation to finish.\n" );
}
}
if (NULL != cpUniqueId)
{
bMatch = isIdContainedIn( cpUniqueId, keyId );
if (!bMatch) {
printf("\nexpected key located by group--asynch client was not returned as expected\n" );
}
// -> ask the server to return the key material but do it synchronously
cpClient->setAsynchronousIndicator( P6FALSE );
err = getSymmetricKey( cpClient, keyId, KMIP_AES, 128, cpKey.addressof() );
if (P6SUCCEEDED(err) && NULL != cpKey) cpKey = NULL;
cpClient->setAsynchronousIndicator( P6TRUE );
}
// [C] Locate managed object by name, cancel request before it is done
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &locateParams, 0, sizeof( P6KMIP_LOCATEPARAMS ));
attributeList[0].type = KMIP_ATTRIB_OBJECTTYPE;
attributeList[0].index = 0;
attributeList[0].value.objectType = KMIP_OBJECT_SYMMETRICKEY;
attributeList[1].type = KMIP_ATTRIB_NAME;
attributeList[1].index = 0;
attributeList[1].value.name.type = KMIP_NAME_TEXTSTR;
attributeList[1].value.name.value.pString = "TC-32-12-key20";
attributeList[1].value.name.value.length = 14;
locateParams.attribCount = 2;
locateParams.pAttributeList = attributeList;
if ( P6FAILED( err = cpClient->locateObject( locateParams, cpUniqueId.addressofWithRelease(), &resultCodes ))) {
printf("\ncall to locateObject by Name has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error when calling locateObject by Name\n");
}
// -> here we demonstrate cancelling a pending request
if ( KMIP_RESULT_PENDING == resultCodes.resultStatus && NULL != resultCodes.pAsynchCorrelation )
{
err = extractAsynchHandle( resultCodes.pAsynchCorrelation, &asynchCorr );
if (P6FAILED( err )) return err;
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
if ( P6FAILED( err = cpClient->cancel( asynchCorr, NULL, &resultCodes, &cancelResult ))) {
printf("\ncall to cancel has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error when calling cancel\n");
}
}
return cpClient->close();
}
// Create a key and then locate it using asynchronous locate operations.
// Main client function
// Identical to the KMIP TC_32_12 interop test case.
//
P6CLASSMETHODIMPL CKmipExample2::run( p6IDataStream *pStreamDebug )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_KEYPARAMS keyParams;
P6KMIP_ATTRIBUTE attributeList[6];
P6NCSTR keyId = {NULL, 0};
P6ERR err = eOk;
// [1] Initialize, "TC_32_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_32_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] Create symmetric key defining its set of attributes such as crypto usage mask
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &newKey, 0, sizeof( P6KMIP_NEWOBJECT ));
m_cpStr->setMem( &keyParams, 0, sizeof( P6KMIP_KEYPARAMS ));
m_cpStr->setMem( &attributes, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
attributeList[0].type = KMIP_ATTRIB_CRYPTOALGORITHM;
attributeList[0].index = 0;
attributeList[0].value.cryptoAlgorithm = KMIP_AES;
attributeList[1].type = KMIP_ATTRIB_CRYPTOLENGTH;
attributeList[1].index = 0;
attributeList[1].value.cryptoLength = 128;
attributeList[2].type = KMIP_ATTRIB_NAME;
attributeList[2].index = 0;
attributeList[2].value.name.type = KMIP_NAME_TEXTSTR;
attributeList[2].value.name.value.pString = "TC-32-12-key20";
attributeList[2].value.name.value.length = 14;
attributeList[3].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[3].index = 0;
attributeList[3].value.cryptoUsageMask = KMIP_USE_ENCRYPT;
attributeList[4].type = KMIP_ATTRIB_OBJECTGROUP;
attributeList[4].index = 0;
attributeList[4].value.objectGroup.pString = "Group1";
attributeList[4].value.objectGroup.length = 6;
attributes.attribCount = 5;
attributes.pAttributeList = attributeList;
keyParams.pAttributes = &attributes;
if ( P6FAILED( err = cpClient->createKeyObject( keyParams, &newKey, &resultCodes ))) {
printf("\ncall to createKeyObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != newKey.pUniqueId)
{
err = extractUniqueId( newKey.pUniqueId, &keyId );
newKey.pUniqueId->release();
newKey.pUniqueId = NULL;
}
// [4] Run a second client to do asynchronous calls to locate the newly created key
err = asynchClient( keyId );
// [5] If the key was created we need to clean up and remove it from the KMIP server
if (NULL != keyId.pString)
{
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
err = cpClient->destroyObject( keyId, NULL, &newKey.pUniqueId, &resultCodes );
if ( P6FAILED( err )) {
printf("\ndestroying key call has failed\n");
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
delete [] keyId.pString;
}
return cpClient->close();
}
P6VOID KMIP_TC_32_12( p6IDataStream* pDataStream )
{
CKmipExample2 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_32_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;
}