Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-9.cpp
#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( 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 CKmipExample9
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream *pStreamDebug );
CKmipExample9()
: m_port(0), m_compatMask(0), m_pHostName(NULL)
{ }
~CKmipExample9()
{
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 );
// -> managed object manipulation functions
P6CLASSMETHOD getAsymmetricKey( p6IKMIPClient* pClient, P6NCSTR keyId, P6UINT32& pubOrPriv, p6ICryptoKey** pKey );
P6CLASSMETHOD getCertificate( p6IKMIPClient* pClient, P6NCSTR certId, p6ICert** pCert );
P6CLASSMETHOD getServerCertAttributes( p6IKMIPClient* pClient, P6NCSTR certId );
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 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 );
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 CKmipExample9::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 CKmipExample9::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 CKmipExample9::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;
}
P6CLASSMETHODIMPL CKmipExample9::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 CKmipExample9::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 CKmipExample9::createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, 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 = keystoreAddRootCertFromFile( (*ppKeystore), rootPEM ))) return err;
if (P6FAILED( err = keystoreAddClientCertFromFile( (*ppKeystore), m_pHostName, privPEM, certPEM ))) return err;
return eOk;
}
// Key generation requires an entropy source
P6CLASSMETHODIMPL CKmipExample9::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 CKmipExample9::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"), 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 CKmipExample9::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 CKmipExample9::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 CKmipExample9::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 CKmipExample9::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 CKmipExample9::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 CKmipExample9::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 );
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 ) {
case KMIP_OBJECT_PUBLICKEY:
//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;
case KMIP_OBJECT_PRIVATEKEY:
//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;
}
// To get the raw contents of a certificate the p6ICert object has methods to extract the certificate bytes.
// If the application wants to put the p6ICert into the P6R keystore then it can be passed as is to the
// keystore since it takes p6ICert objects directly.
//
P6CLASSMETHODIMPL CKmipExample9::getCertificate( p6IKMIPClient* pClient, P6NCSTR certId, p6ICert** pCert )
{
P6KMIP_RESULT resultCodes;
P6KMIP_GETPARAMS getParams;
P6KMIP_MANAGED managedObj;
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 ));
*pCert = NULL;
getParams.uniqueId.pString = certId.pString;
getParams.uniqueId.length = certId.length;
if ( P6FAILED( err = pClient->getObject( getParams, &managedObj, &resultCodes ))) {
printf("\ncall to createKeyPairObjects has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if ( NULL != managedObj.pUniqueId )
{
if ( KMIP_OBJECT_CERTIFICATE == managedObj.type )
(*pCert) = managedObj.value.pCertificate;
else err = eInvalidHandle;
managedObj.pUniqueId->release();
}
else err = eNotFound;
return err;
}
// Examine the attributes of a certificate generated by the KMIP server
P6CLASSMETHODIMPL CKmipExample9::getServerCertAttributes( p6IKMIPClient* pClient, P6NCSTR certId )
{
P6KMIP_RESULT resultCodes;
P6KMIP_GETATTRIBPARAMS attribParams;
P6NCSTR attribNames[11];
P6CHAR tempName[200];
P6CHAR serialNumber[200];
P6UINT32 attribType = 0;
P6CHAR* pName = NULL;
P6ERR err = eOk;
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &getResult, 0, sizeof( P6KMIP_ATTRIBRESULT ));
m_cpStr->setMem( &attribParams, 0, sizeof( P6KMIP_GETATTRIBPARAMS ));
// -> tell the KMIP server the attributes of the certificate that we are interested in
attribNames[0].pString = "Certificate Identifier";
attribNames[0].length = 22;
attribNames[1].pString = "Certificate Issuer";
attribNames[1].length = 18;
attribNames[2].pString = "Certificate Subject";
attribNames[2].length = 19;
attribNames[3].pString = "Certificate Type";
attribNames[3].length = 16;
attribNames[4].pString = "Digital Signature Algorithm";
attribNames[4].length = 27;
attribNames[5].pString = "Cryptographic Length";
attribNames[5].length = 20;
attribNames[6].pString = "Certificate Length";
attribNames[6].length = 18;
attribNames[7].pString = "X.509 Certificate Identifier";
attribNames[7].length = 28;
attribNames[8].pString = "X.509 Certificate Issuer";
attribNames[8].length = 24;
attribNames[9].pString = "X.509 Certificate Subject";
attribNames[9].length = 25;
attribParams.uniqueId = certId;
attribParams.attribCount = 10;
attribParams.pAttributeNames = attribNames;
if ( P6FAILED( err = pClient->getAttributes( attribParams, &getResult, &resultCodes ))) {
printf("\ncall to getAttributes has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != getResult.pAttribute)
{
// -> the enumerator will return eEndOfRecord when there are no more entries
while (P6SUCCEEDED( getResult.pAttribute->next( &attribType )))
{
m_cpStr->setMem( &objAttribute, 0, sizeof( P6KMIP_OBJECT_ATTRIBUTE ));
switch( attribType ) {
case KMIP_ATTRIB_CERTIFICATEIDENTIFIER:
// -> *** NOTE ***, here we just use large fixed buffers to get out the identifier value from the enumerator
objAttribute.value.certificateIdentifier.issuer.pString = tempName;
objAttribute.value.certificateIdentifier.serialNumber.pString = serialNumber;
err = getResult.pAttribute->getValue( &objAttribute );
// should be something like: "CN=KMIP,OU=OASIS,O=TEST,C=US"
// value in: objAttribute.value.certificateIdentifier.issuer.pString
// value in: objAttribute.value.certificateIdentifier.serialNumber.pString
break;
case KMIP_ATTRIB_CERTIFICATEISSUER:
// -> *** NOTE ***, here we do dynamic memory allocation to get out the issuer value from the enumerator
if (eTooSmall == (err = getResult.pAttribute->getValue( &objAttribute )))
{
if (NULL != (pName = new (std::nothrow) P6CHAR[ objAttribute.value.certificateIssuer.distinguishedName.length + 1 ])) {
err = getResult.pAttribute->getValue( &objAttribute );
//printf( "\nissuer name [%s] %d\n", pName, objAttribute.value.certificateIssuer.distinguishedName.length );
}
}
// should be something like: "CN=KMIP,OU=OASIS,O=TEST,C=US"
// value in: objAttribute.value.certificateIssuer.distinguishedName.pString
if (NULL != pName) { delete [] pName; pName = NULL; }
break;
case KMIP_ATTRIB_CERTIFICATESUBJECT:
err = getResult.pAttribute->getValue( &objAttribute );
// should be something like: "CN=Client,OU=KMIP,O=ACME,C=US"
// value in: objAttribute.value.certificateSubject.distinguishedName.pString
break;
case KMIP_ATTRIB_CERTIFICATETYPE:
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.certificateType, should be KMIP_CERT_X509
break;
case KMIP_ATTRIB_CRYPTOLENGTH:
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.cryptoLength
break;
case KMIP_ATTRIB_CERTIFICATELENGTH:
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.certificateLength
break;
case KMIP_ATTRIB_DIGITALSIGALG:
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.digitalSigAlg, should be KMIP_SIG_SHA256RSA
break;
case KMIP_ATTRIB_X509CERTIFICATEIDENTIFIER:
// -> these are all ASN.1 binary strings
objAttribute.value.x509certificateIdentifier.issuer.pString = (const P6UCHAR*)tempName;
objAttribute.value.x509certificateIdentifier.serialNumber.pString = (const P6UCHAR*)serialNumber;
err = getResult.pAttribute->getValue( &objAttribute );
break;
case KMIP_ATTRIB_X509CERTIFICATESUBJECT:
// -> these are all ASN.1 binary strings
err = getResult.pAttribute->getValue( &objAttribute );
break;
case KMIP_ATTRIB_X509CERTIFICATEISSUER:
// -> these are all ASN.1 binary strings
objAttribute.value.x509certificateIssuer.distinguishedName.pString = (const P6UCHAR*)tempName;
err = getResult.pAttribute->getValue( &objAttribute );
break;
default:
break;
}
}
}
return err;
}
// KMIP server generates certificate for key pair.
// Main client function
// Based on the KMIP TC_134_12 interop test case.
//
P6CLASSMETHODIMPL CKmipExample9::run( p6IDataStream *pStreamDebug )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_PAIRPARAMS keyParams;
P6KMIP_CERTIFYPARAMS certifyParams;
P6KMIP_TEMPLATEATTRIBUTE privateAttrib;
P6KMIP_ATTRIBUTE commonList[3];
P6KMIP_ATTRIBUTE privateList[3];
P6KMIP_ATTRIBUTE publicList[3];
P6KMIP_ATTRIBUTE attributeList[6];
P6KMIP_NEWOBJECT privKey;
P6NCSTR privId = {NULL, 0}; // -> private key unique id generated by KMIP server
P6NCSTR pubId = {NULL, 0}; // -> public key unique id generated by KMIP server
P6NCSTR certId = {NULL, 0}; // -> certificate unique id generated by KMIP server
p6ICryptoKey* pKey1 = NULL;
p6ICryptoKey* pKey2 = NULL;
p6ICert* pCertServer = NULL; // -> downloaded from the KMIP server
P6UINT32 pubOrPriv = 0;
P6ERR err = eOk;
// Hard coded CRL for the test. Required by KMIP to create a certificate
//
const P6UCHAR CRL[] =
{ 0x30,0x82,0x02,0x81,0x30,0x82,0x01,0x69,0x02,0x01,0x00,0x30,0x3C,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0D,0x30,0x0B,0x06,0x03,0x55,0x04,0x0A,0x13,0x04,0x41,0x43,0x4D,0x45,0x31,0x0D,0x30,0x0B,0x06,0x03,0x55,
0x04,0x0B,0x13,0x04,0x4B,0x4D,0x49,0x50,0x31,0x0F,0x30,0x0D,0x06,0x03,0x55,0x04,0x03,0x13,0x06,0x43,0x6C,0x69,0x65,0x6E,0x74,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,
0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xAB,0x7F,0x16,0x1C,0x00,0x42,0x49,0x6C,0xCD,0x6C,0x6D,0x4D,0xAD,0xB9,0x19,0x97,0x34,0x35,0x35,0x77,0x76,0x00,0x3A,0xCF,0x54,0xB7,0xAF,0x1E,0x44,0x0A,0xFB,0x80,0xB6,0x4A,0x87,0x55,0xF8,0x00,0x2C,
0xFE,0xBA,0x6B,0x18,0x45,0x40,0xA2,0xD6,0x60,0x86,0xD7,0x46,0x48,0x34,0x6D,0x75,0xB8,0xD7,0x18,0x12,0xB2,0x05,0x38,0x7C,0x0F,0x65,0x83,0xBC,0x4D,0x7D,0xC7,0xEC,0x11,0x4F,0x3B,0x17,0x6B,0x79,0x57,0xC4,0x22,0xE7,0xD0,0x3F,0xC6,0x26,0x7F,0xA2,0xA6,
0xF8,0x9B,0x9B,0xEE,0x9E,0x60,0xA1,0xD7,0xC2,0xD8,0x33,0xE5,0xA5,0xF4,0xBB,0x0B,0x14,0x34,0xF4,0xE7,0x95,0xA4,0x11,0x00,0xF8,0xAA,0x21,0x49,0x00,0xDF,0x8B,0x65,0x08,0x9F,0x98,0x13,0x5B,0x1C,0x67,0xB7,0x01,0x67,0x5A,0xBD,0xBC,0x7D,0x57,0x21,
0xAA,0xC9,0xD1,0x4A,0x7F,0x08,0x1F,0xCE,0xC8,0x0B,0x64,0xE8,0xA0,0xEC,0xC8,0x29,0x53,0x53,0xC7,0x95,0x32,0x8A,0xBF,0x70,0xE1,0xB4,0x2E,0x7B,0xB8,0xB7,0xF4,0xE8,0xAC,0x8C,0x81,0x0C,0xDB,0x66,0xE3,0xD2,0x11,0x26,0xEB,0xA8,0xDA,0x7D,0x0C,0xA3,0x41,
0x42,0xCB,0x76,0xF9,0x1F,0x01,0x3D,0xA8,0x09,0xE9,0xC1,0xB7,0xAE,0x64,0xC5,0x41,0x30,0xFB,0xC2,0x1D,0x80,0xE9,0xC2,0xCB,0x06,0xC5,0xC8,0xD7,0xCC,0xE8,0x94,0x6A,0x9A,0xC9,0x9B,0x1C,0x28,0x15,0xC3,0x61,0x2A,0x29,0xA8,0x2D,0x73,0xA1,0xF9,0x93,
0x74,0xFE,0x30,0xE5,0x49,0x51,0x66,0x2A,0x6E,0xDA,0x29,0xC6,0xFC,0x41,0x13,0x35,0xD5,0xDC,0x74,0x26,0xB0,0xF6,0x05,0x02,0x03,0x01,0x00,0x01,0xA0,0x00,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,
0x00,0x2D,0x90,0xF5,0x49,0x2C,0x3D,0xF1,0x77,0x1D,0xF4,0xE8,0x7E,0x10,0x87,0xCB,0x95,0x21,0x97,0x31,0x9A,0x96,0x96,0xE2,0xD5,0x88,0xEF,0xDA,0x58,0x0D,0x8D,0x33,0x04,0x42,0x7B,0x99,0x7C,0xD9,0x21,0xAD,0x7C,0x67,0x4A,0xEA,0x41,0x3F,0xBA,0x85,
0xFD,0x61,0xE6,0xA4,0x81,0xDE,0x9A,0xB2,0xE8,0xA4,0xFF,0x43,0xC0,0x26,0x55,0x01,0x5D,0x34,0x37,0xF7,0x83,0xFE,0x0C,0x78,0x15,0x19,0xCD,0x08,0xFF,0xD3,0xC0,0x07,0xC7,0xFA,0xDE,0x96,0x32,0xFE,0x56,0x59,0xE2,0xCA,0xC3,0x5B,0xD6,0xAA,0xF3,0xE1,0x3D,
0xC1,0x80,0x97,0xD9,0x96,0xDF,0x01,0xB6,0x6F,0xC5,0xE2,0x6C,0xA1,0x09,0x38,0x08,0x63,0xA2,0x09,0x12,0x5C,0xC0,0xFD,0x79,0x53,0x3F,0x32,0x7F,0xA1,0xCA,0xD4,0x44,0xD8,0x9D,0x3F,0xF8,0x1B,0x92,0xA9,0x14,0x28,0xC4,0x69,0xC8,0x46,0x09,0x0F,0xD1,
0x32,0x48,0x46,0xE1,0x2D,0x01,0x67,0x19,0x62,0xC3,0x32,0xA7,0x82,0x61,0x52,0xDA,0xAF,0x48,0x6C,0xC8,0x67,0x18,0x5C,0x2E,0x27,0xCA,0xF2,0xF0,0x09,0x89,0x8D,0xB0,0x7F,0xE4,0xB4,0x5C,0x51,0x81,0x92,0xAA,0x49,0x3D,0x8F,0x8C,0x01,0x98,0xDB,0x67,0xF9,
0x06,0x72,0xAB,0x6D,0xE0,0x5A,0x08,0x03,0x29,0x41,0x37,0x7F,0x47,0x3D,0x80,0x71,0x6D,0x85,0xAD,0xC6,0x18,0x20,0x03,0xAB,0x34,0x94,0x23,0x02,0x21,0x4E,0xB3,0x89,0x5F,0x15,0x40,0x3F,0x26,0x16,0xAD,0xFD,0x6B,0xB5,0xE6,0xAA,0x47,0xFA,0x38,0xC9,
0xDF,0xC7,0x3F,0x4D,0xE8,0x0D,0xDB,0x91,0xBD,0xB0,0x4D,0x21,0xC8,0x2B,0xA6 };
// [1] Initialize, "TC_134_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_134_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].type = KMIP_ATTRIB_CRYPTOALGORITHM;
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-privatekeyC";
privateList[0].value.name.value.length = 20;
privateList[1].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
privateList[1].index = 0;
privateList[1].value.cryptoUsageMask = KMIP_USE_SIGN;
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-publickeyC";
publicList[0].value.name.value.length = 19;
publicList[1].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
publicList[1].index = 0;
publicList[1].value.cryptoUsageMask = KMIP_USE_VERIFY;
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();
}
// [6] 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
// [7] Have the KMIP server generate a certificate for the key pair
// -> this can also be done on a key pair that the client creates and registers
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &certAttrib, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
m_cpStr->setMem( &certifyParams, 0, sizeof( P6KMIP_CERTIFYPARAMS ));
m_cpStr->setMem( &certObj, 0, sizeof( P6KMIP_NEWOBJECT ));
attributeList[0].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[0].index = 0;
attributeList[0].value.cryptoUsageMask = KMIP_USE_SIGN | KMIP_USE_VERIFY;
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-134-12-certificateC";
attributeList[1].value.name.value.length = 22;
certAttrib.attribCount = 2;
certAttrib.pAttributeList = attributeList;
certifyParams.pAttributes = &certAttrib;
certifyParams.uniqueId = pubId;
certifyParams.requestType = KMIP_CERTREQUEST_PKCS10;
certifyParams.certificateRequest.pString = (const P6UCHAR*)CRL;
certifyParams.certificateRequest.length = sizeof( CRL );
if ( P6FAILED( err = cpClient->certifyObject( certifyParams, &certObj, &resultCodes ))) {
printf("\ncall to certifyObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != certObj.pUniqueId)
{
err = extractUniqueId( certObj.pUniqueId, &certId );
certObj.pUniqueId->release();
}
err = getCertificate( cpClient, certId, &pCertServer ); // -> get a copy of the generated certificate off of the server
// -> to extract the certificate raw bytes, see p6cert.h
// err = pCertServer->serializeToFile( const P6WCHAR* pFile ); -- get in PEM
// err = pCertServer->serializeToBuffer( P6VOID* pBuffer, P6UINT32 cBuffer, P6UINT32* pcBytesWritten ); -- get in PEM
// err = pCertServer->serializeDERToBuffer( P6VOID* pBuffer, P6UINT32 cBuffer, P6UINT32* pcBytesWritten ); -- get the ASN.1
// err = pCertServer->printCertificate( const P6WCHAR* pCertFile ); -- in human readable form
// [8] Get the values of attributes associated with the KMIP server generated certificate
err = getServerCertAttributes( cpClient, certId );
// [9] Finished clean up remove the keys off of the KMIP server
destroyObject( cpClient, privId, P6TRUE );
destroyObject( cpClient, pubId, P6TRUE );
destroyObject( cpClient, certId, P6TRUE );
err = cpClient->close();
if (pKey1) pKey1->release();
if (pKey2) pKey2->release();
if (pCertServer) pCertServer->release();
return err;
}
P6VOID KMIP_TC_134_12( p6IDataStream* pDataStream )
{
CKmipExample9 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_134_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;
}