Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-14.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"
#include "cwalkmessage2.h"
using namespace P6R;
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 );
P6DECLARE_CID( p6IoBufferFactory );
P6DECLARE_CID( p6KMIPEncoder );
P6DECLARE_CID( p6KMIPDecoder );
P6DECLARE_CID( p6TcpSocket );
P6DECLARE_CID( p6Netdb );
P6DECLARE_CID( p6IntervalTime );
namespace {
class CKmipExample14
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream* pStreamDebug );
CKmipExample14(): m_pResponse( NULL ),
m_pWalk( NULL ),
m_port( 0 ),
m_pHostName( NULL ),
m_maxMsgSize( 8192 )
{ }
~CKmipExample14()
{
if (NULL != m_pHostName ) m_cpStr->wstrfree( m_pHostName );
if (NULL != m_cpStoreInit) m_cpStoreInit->close();
if (NULL != m_pResponse ) m_pResponse->release();
if (NULL != m_pWalk ) delete m_pWalk;
}
protected:
// -> create and use TLS connection to KMIP server
P6CLASSMETHOD createTLSSession();
P6CLASSMETHOD sendMessage( p6IIoBuffer* pRequest, p6ITcpSocket* pSocket, P6INTERVAL tTimeout, P6UINT32& cBytesSent );
// -> KMIP protocol related
P6CLASSMETHOD queryServer( KMIP_QUERY* pQuery );
P6CLASSMETHOD createKey( P6NCSTR* pUniqueId );
P6CLASSMETHOD getKeyMaterial( P6NCSTR* pUniqueId, P6BSTR* pKeyMaterial );
P6CLASSMETHOD addAttribute( P6NCSTR* pUniqueId, P6KMIP_ATTRIBUTE* pAttribute );
P6CLASSMETHOD locateByName( const P6CHAR* pObjName, P6UINT32 nameLength, P6UINT32 objType, P6NCSTR* pUniqueId );
P6CLASSMETHOD destroyObject( P6NCSTR* pUniqueId );
// -> supports crypto
P6CLASSMETHOD getRNG( 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 );
// -> set up keystore used in TLS connection
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
//
// -> network connection related // Note: you can use our TLS library or your own to make a connection to the KMIP server
p6ComPtr<p6IIoBufferFactory> m_cpPool; // -> buffer pool for TLS socket
p6ComPtr<p6ITcpSocket> m_cpSocket; // -> TLS socket
p6IIoBuffer* m_pResponse; // -> read KMIP server response into this buffer
//
// -> KMIP protocol specific //
p6ComPtr<p6IKMIPEncoder> m_cpEncoder; // -> low level API to create binary KMIP messages
p6ComPtr<p6IKMIPDecoder> m_cpDecoder; // -> low level API to parse binary KMIP messages
p6ComPtr<p6IKMIPRequest> m_cpRequest; // -> low level API to create a binary KMIP request message
CWalkMessage2* m_pWalk; // -> use pDecoder to walk the KMIP message to parse the full message
//
// -> misc items //
p6ComPtr<p6ISafeString> m_cpStr; // -> generic string library
P6UINT32 m_port; // -> KMIP server port to connect to
P6WCHAR* m_pHostName; // -> IP address or FQDN of KMIP server to connect to
P6UINT32 m_maxMsgSize; // -> size of our allocated message buffers
};
// Create the key file (supplied by the server vendor) into a p6ICryptoKey object for SSL
//
P6CLASSMETHODIMPL CKmipExample14::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 CKmipExample14::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 CKmipExample14::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 CKmipExample14::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 CKmipExample14::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 CKmipExample14::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 CKmipExample14::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;
}
// Send the bytes of the binary KMIP message to the server.
P6R::P6ERR CKmipExample14::sendMessage( p6IIoBuffer* pRequest, p6ITcpSocket* pSocket, P6INTERVAL tTimeout, P6UINT32& cBytesSent )
{
P6UINT8* pRawBuffer = NULL;
P6UINT32 dontCare = 0;
P6UINT32 bytesLeft = 0;
P6UINT32 bytesSent = 0;
P6UINT32 offset = 0;
P6ERR err = eOk;
if (P6FAILED( err = pRequest->getBufPtr( &pRawBuffer, &dontCare, &bytesLeft ))) return err;
while( P6SUCCEEDED(err) && 0 < bytesLeft )
{
err = pSocket->send( &pRawBuffer[offset], bytesLeft, &bytesSent, tTimeout );
bytesLeft -= bytesSent;
offset += bytesSent;
bytesSent = 0;
}
cBytesSent = offset;
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 CKmipExample14::initialize()
{
P6ERR err = eOk;
P6WCHAR dbPath[P6MAXPATH+20] = {0};
// [A] Right now configuration is hard coded, but could read out of a config file later
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6ISafeString, m_cpStr )))) return err;
m_port = 5696;
if (P6FAILED( err = m_cpStr->wstrdup( P6TEXT("fqdn.com"), &m_pHostName ))) return err;
// [B] Set up the keystore and load in the certificates and keys needed for the TLS connection
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;
// -> 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 );
err = createKeystore( P6TEXT("KMIP12_keystore"), P6TEXT("RootCert.pem"), P6TEXT("ClientCert.pem"), P6TEXT("ClientPrivate.pem"), m_cpStoreInit.addressof(), m_cpKeystore.addressof());
if (P6FAILED( err )) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IntervalTime, VALIDATECOMPTR( p6IIntervalTime, m_cpIT )))) return err;
if (P6FAILED( err = m_cpIT->initialize())) return err;
// [C] Buffers used by TLS networking and encoder
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IoBufferFactory, VALIDATECOMPTR( p6IIoBufferFactory, m_cpPool )))) return err;
if (P6FAILED( err = m_cpPool->initialize( P6CTEXT("Buffer pool"), m_maxMsgSize, 2, 3, P6IOBF_NOFLAGS ))) return err;
// -> allocate one buffer to read responses into
if (P6FAILED( err = m_cpPool->alloc( &m_pResponse ))) return err;
// [D] Create KMIP protocol specific objects
// -> low level KMIP encoder interface, define the protocol version in use on initialization
P6KMIP_ENCODER_PREF encodePrefs = {0, 0, 0};
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPEncoder, VALIDATECOMPTR( p6IKMIPEncoder, m_cpEncoder )))) return err;
if (P6FAILED( err = m_cpEncoder->initialize( P6KMIPENCODER_NOFLAGS, P6KMIP_VERSION_10, m_cpPool, &encodePrefs ))) return err; // or P6KMIP_VERSION_11 -- see file p6kmipencoder.h for all values
if (P6FAILED( err = m_cpEncoder->queryInterface( VALIDATECOMPTR( p6IKMIPRequest, m_cpRequest )))) return err; // -> the encoder supports both request and response KMIP message generation
// -> low level KMIP decoder interface
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPDecoder, VALIDATECOMPTR( p6IKMIPDecoder, m_cpDecoder )))) return err;
if (P6FAILED( err = m_cpDecoder->initialize( P6KMIPDECODER_NOFLAGS ))) return err;
if (NULL == (m_pWalk = new (std::nothrow) CWalkMessage2())) return eNoMemory;
if (P6FAILED( err = m_pWalk->initialize( m_cpDecoder ))) return err;
return err;
}
// Create a TLS socket and connect to the remote KMIP server.
// Note: you can use our TLS library or use your own networking in its place, the rest of the KMIP protocol handling does not change.
//
P6CLASSMETHODIMPL CKmipExample14::createTLSSession()
{
p6ComPtr<p6INetHelpers> cpNetHelper; // -> IP address formater
p6ComPtr<p6INetdb> cpNetdb; // -> domain name to IP address
p6ComPtr<p6ITcpSecureSocket> cpInitSSL; // -> set up TLS socket with ciphers etc
P6NETADDR netAddress;
P6NETADDR hostAddr;
P6ARG args[4];
P6WCHAR iphostName[100];
P6WCHAR ipPlusPort[100];
P6INTERVAL tTimeout = 0;
P6WCHAR* pAddr = NULL;
P6ERR err = eOk;
// [A] Convert the domain name into an IP address
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6INetHelpers, cpNetHelper )))) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Netdb, VALIDATECOMPTR( p6INetdb, cpNetdb )))) return err;
if (P6FAILED( err = cpNetdb->initialize())) return err;
if (P6FAILED( err = cpNetdb->getHostByNameW( m_pHostName, &hostAddr ))) return err;
if (P6FAILED( err = cpNetHelper->netAddrToWStr( &hostAddr, iphostName, P6CNTOF(iphostName), NULL, P6FALSE ))) return err;
pAddr = iphostName;
// [B] Create a socket and then convert it to TLS mode
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6TcpSocket, VALIDATECOMPTR( p6ITcpSocket, m_cpSocket )))) return err;
if (P6FAILED( err = m_cpSocket->initialize( P6AF_INET, P6SF_SECURESSL ))) return err;
if (P6FAILED( err = m_cpSocket->queryInterface( VALIDATECOMPTR( p6ITcpSecureSocket, cpInitSSL )))) return err;
err = cpInitSSL->initSecureSocket( m_cpKeystore, m_cpPool, m_pHostName, NULL, (P6SSF_METHOD_NEGOTIATE | P6SSF_SECURE_CLIENT | P6SSF_SECURE_CLIENT_AUTH | P6SSF_LOG_X509SUBJECTLOOKUPS | P6SSF_SECURE_CLIENT | P6SF_SECURESSL));
if (P6FAILED( err )) return err;
// [C] Connect to the remote KMIP server
ipPlusPort[0] = 0;
err = m_cpStr->wstrlcat( ipPlusPort, 100, P6CTEXT("0.0.0.0:0"), NULL );
if (P6FAILED( err = cpNetHelper->wStrToNetAddr( ipPlusPort, &netAddress ))) return err;
if (P6FAILED( err = m_cpSocket->bind( &netAddress ))) return err;
// -> format the KMIP server's IP address and port
P6AI_WCHARPTR( &args[0], pAddr );
P6AI_UINT32( &args[1], m_port );
if (P6FAILED( err = m_cpStr->formatStringW( ipPlusPort, P6CNTOF(ipPlusPort), NULL, P6CTEXT("%1$:%2$"), args, 2 ))) return err;
if (P6FAILED( err = cpNetHelper->wStrToNetAddr( ipPlusPort, &netAddress ))) return err;
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
return m_cpSocket->connect( &netAddress, tTimeout );
}
// Generic function to remove an object off of the KMIP server by its unique identifer
P6CLASSMETHODIMPL CKmipExample14::destroyObject( P6NCSTR* pUniqueId )
{
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
if (NULL == pUniqueId->pString) return eFail;
// -> create a Destroy KMIP message request
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
params.pMaxResponseSize = &m_maxMsgSize;
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addDestroyRequest( *pUniqueId, NULL ))) return err; // -> add the Destroy operation to that message
if (P6FAILED( err = m_cpRequest->endRequestMsg())) return err; // -> tell the encoder that the KMIP request message is done
if (P6FAILED( err = m_cpEncoder->getBufPtr( &pReqBuf ))) return err; // -> get out the encoded request message from the encoder
// -> send the message to the server
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
err = sendMessage( pReqBuf, m_cpSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err ))
{
// -> wait for a respnse and then parse the message using the m_pWalk object
P6UINT8* pBuffer = NULL;
P6UINT32 bufSize = 0;
P6UINT32 bufUsed = 0;
err = m_pResponse->setUsed( 0 );
if (P6SUCCEEDED( err = m_pResponse->getBufPtr( &pBuffer, &bufSize, &bufUsed ))) // -> get the byte buffer out of the m_pResponse object
{
if (P6SUCCEEDED( err = m_cpSocket->recv( pBuffer, bufSize, &cBytesRead, tTimeout ))) // -> receive the KMIP server response into pBuffer
{
if (P6SUCCEEDED( err = m_pResponse->setUsed( cBytesRead ))) // -> mark how many bytes of the pBuffer have data in them
{
// On success the "result.resultStatus" parameter will contain KMIP_RESULT_SUCCESS value indicating that the object was deleted
// -> look at the file cwalkmessage.h and cwalkmessage.cpp for how we parse the KMIP server response message
// -> Note: that "result" contains detailed error information if our KMIP request has failed
err = m_pWalk->getResponseResult( m_pResponse, KMIP_OP_DESTROY, &result );
}
}
}
// -> did we fail trying to destroy the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// Fetch the contents of the key already stored on the KMIP server by its unique identifer.
P6CLASSMETHODIMPL CKmipExample14::getKeyMaterial( P6NCSTR* pUniqueId, P6BSTR* pKeyMaterial )
{
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6NCSTR objId = { NULL, 0 };
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
if (NULL == pUniqueId->pString) return eFail;
// -> create a GET KMIP message request
// -> Note: the KMIP encoder API lets you build any complex message between the "startRequestMsg", and "endRequestMsg" calls
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
params.pMaxResponseSize = &m_maxMsgSize;
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addGetRequest( *pUniqueId, NULL, NULL, NULL, NULL ))) return err; // -> add the Get operation to that message
if (P6FAILED( err = m_cpRequest->endRequestMsg())) return err; // -> tell the encoder that the KMIP request message is done
if (P6FAILED( err = m_cpEncoder->getBufPtr( &pReqBuf ))) return err; // -> get out the encoded request message from the encoder
// -> send the message to the server
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
err = sendMessage( pReqBuf, m_cpSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err ))
{
P6UINT8* pBuffer = NULL;
P6UINT32 bufSize = 0;
P6UINT32 bufUsed = 0;
err = m_pResponse->setUsed( 0 );
if (P6SUCCEEDED( err = m_pResponse->getBufPtr( &pBuffer, &bufSize, &bufUsed ))) // -> get the byte buffer out of the m_pResponse object
{
if (P6SUCCEEDED( err = m_cpSocket->recv( pBuffer, bufSize, &cBytesRead, tTimeout ))) // -> receive the KMIP server response into pBuffer
{
if (P6SUCCEEDED( err = m_pResponse->setUsed( cBytesRead ))) // -> mark how many bytes of the pBuffer have data in them
{
// On success pKeyMaterial contains an allocated buffer with the bytes of the key material that was stored in the KMIP server,
// also another copy of uniqueId is returned by the server
// -> look at the file cwalkmessage.h and cwalkmessage.cpp for how we parse the KMIP server response message
// -> Note: that "result" contains detailed error information if our KMIP request has failed
err = m_pWalk->getResponseKeyMaterial( m_pResponse, &objId, pKeyMaterial, &result );
// -> we could test to see if objId matches the uniqueId parameter
if (NULL != objId.pString) delete [] objId.pString;
}
}
}
// -> did we fail trying to get the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// pAttribute holds information on a new attribute to add to the object defined by pUniqueId
P6CLASSMETHODIMPL CKmipExample14::addAttribute( P6NCSTR* pUniqueId, P6KMIP_ATTRIBUTE* pAttribute )
{
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6NCSTR objId = { NULL, 0 };
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
if (NULL == pUniqueId->pString) return eFail;
// -> Note: the KMIP encoder API lets you build any complex message between the "startRequestMsg", and "endRequestMsg" calls
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
params.pMaxResponseSize = &m_maxMsgSize;
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addAddAttributeRequest( *pUniqueId, *pAttribute, NULL ))) return err; // -> tell KMIP server to update this attribute associated with the unique id
if (P6FAILED( err = m_cpRequest->endRequestMsg())) return err; // -> tell the encoder that the KMIP request message is done
if (P6FAILED( err = m_cpEncoder->getBufPtr( &pReqBuf ))) return err; // -> get out the encoded request message from the encoder
// -> send the message to the server
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
err = sendMessage( pReqBuf, m_cpSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err ))
{
P6UINT8* pBuffer = NULL;
P6UINT32 bufSize = 0;
P6UINT32 bufUsed = 0;
err = m_pResponse->setUsed( 0 );
if (P6SUCCEEDED( err = m_pResponse->getBufPtr( &pBuffer, &bufSize, &bufUsed ))) // -> get the byte buffer out of the m_pResponse object
{
if (P6SUCCEEDED( err = m_cpSocket->recv( pBuffer, bufSize, &cBytesRead, tTimeout ))) // -> receive the KMIP server response into pBuffer
{
if (P6SUCCEEDED( err = m_pResponse->setUsed( cBytesRead ))) // -> mark how many bytes of the pBuffer have data in them
{
// On success the server returns the Unique Id to the object that was notified
// -> look at the file cwalkmessage.h and cwalkmessage.cpp for how we parse the KMIP server response message
// -> Note: that "result" contains detailed error information if our KMIP request has failed
err = m_pWalk->getResponseUniqueId( m_pResponse, KMIP_OP_ADDATTRIBUTE, pUniqueId, &result );
// -> we could test to see if objId matches the uniqueId parameter
if (NULL != objId.pString) delete [] objId.pString;
}
}
}
// -> did we fail trying to get the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// Ask the KMIP server to create a key for us.
//
P6CLASSMETHODIMPL CKmipExample14::createKey( P6NCSTR* pUniqueId ) // return the unique identifier of the newly created key
{
P6KMIP_ATTRIBUTE attributeList[7];
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
// -> create a Register KMIP message request
m_cpStr->setMem( &params,0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
params.pMaxResponseSize = &m_maxMsgSize;
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 = 256;
attributeList[2].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[2].index = 0;
attributeList[2].value.cryptoUsageMask = KMIP_USE_ENCRYPT | KMIP_USE_DECRYPT;
attributeList[3].type = KMIP_ATTRIB_EXTENSION;
attributeList[3].index = 0;
attributeList[3].value.extension.xName.pString = "x-ID";
attributeList[3].value.extension.xName.length = 4;
attributeList[3].value.extension.xValue.vText.pString = "SomeClientString";
attributeList[3].value.extension.xValue.vText.length = 16;
attributeList[3].value.extension.xType = KMIP_TYPE_TEXTSTRING;
// attributeList[4].type = KMIP_ATTRIB_OBJECTGROUP;
// attributeList[4].index = 0;
// attributeList[4].value.objectGroup.pString = "SomeGroupName";
// attributeList[4].value.objectGroup.length = 13;
attributes.pAttributeList = attributeList;
attributes.attribCount = 4;
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addCreateRequest( attributes, NULL ))) return err; // -> ask the KMIP server to create the key for us
if (P6FAILED( err = m_cpRequest->endRequestMsg())) return err; // -> tell the encoder that the KMIP request message is done
if (P6FAILED( err = m_cpEncoder->getBufPtr( &pReqBuf ))) return err; // -> get out the encoded request message from the encoder
// -> send the message to the server
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
err = sendMessage( pReqBuf, m_cpSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err ))
{
P6UINT8* pBuffer = NULL;
P6UINT32 bufSize = 0;
P6UINT32 bufUsed = 0;
err = m_pResponse->setUsed( 0 );
if (P6SUCCEEDED( err = m_pResponse->getBufPtr( &pBuffer, &bufSize, &bufUsed ))) // -> get the byte buffer out of the m_pResponse object
{
if (P6SUCCEEDED( err = m_cpSocket->recv( pBuffer, bufSize, &cBytesRead, tTimeout ))) // -> receive the KMIP server response into pBuffer
{
if (P6SUCCEEDED( err = m_pResponse->setUsed( cBytesRead ))) // -> mark how many bytes of the pBuffer have data in them
{
// On success pUniqueId contains an identifier for the newly created key that was assigned by the KMIP server
// -> look at the file cwalkmessage.h and cwalkmessage.cpp for how we parse the KMIP server response message
// -> Note: that "result" contains detailed error information if our KMIP request has failed
err = m_pWalk->getResponseUniqueId( m_pResponse, KMIP_OP_CREATE, pUniqueId, &result );
}
}
}
// -> did we fail trying to create the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
//
P6CLASSMETHODIMPL CKmipExample14::locateByName( const P6CHAR* pObjName, P6UINT32 nameLength, P6UINT32 objType, P6NCSTR* pUniqueId ) // return pUniqueId for key object found
{
P6KMIP_ATTRIBUTE attributeList[3];
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6NCSTR objId = { NULL, 0 };
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
if (NULL == pObjName || 0 == nameLength) return eFail;
// -> Note: the KMIP encoder API lets you build any complex message between the "startRequestMsg", and "endRequestMsg" calls
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
params.pMaxResponseSize = &m_maxMsgSize;
attributeList[0].type = KMIP_ATTRIB_OBJECTTYPE;
attributeList[0].index = 0;
attributeList[0].value.objectType = objType;
attributeList[1].type = KMIP_ATTRIB_NAME;
attributeList[1].index = 0;
attributeList[1].value.name.type = KMIP_NAME_TEXTSTR;
attributeList[1].value.name.value.pString = pObjName;
attributeList[1].value.name.value.length = nameLength;
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addLocateRequest( NULL, NULL, NULL, 2, attributeList, NULL ))) return err; // -> look for objects on the KMIP server that match these attributes
if (P6FAILED( err = m_cpRequest->endRequestMsg())) return err; // -> tell the encoder that the KMIP request message is done
if (P6FAILED( err = m_cpEncoder->getBufPtr( &pReqBuf ))) return err; // -> get out the encoded request message from the encoder
// -> send the message to the server
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
err = sendMessage( pReqBuf, m_cpSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err ))
{
P6UINT8* pBuffer = NULL;
P6UINT32 bufSize = 0;
P6UINT32 bufUsed = 0;
err = m_pResponse->setUsed( 0 );
if (P6SUCCEEDED( err = m_pResponse->getBufPtr( &pBuffer, &bufSize, &bufUsed ))) // -> get the byte buffer out of the m_pResponse object
{
if (P6SUCCEEDED( err = m_cpSocket->recv( pBuffer, bufSize, &cBytesRead, tTimeout ))) // -> receive the KMIP server response into pBuffer
{
if (P6SUCCEEDED( err = m_pResponse->setUsed( cBytesRead ))) // -> mark how many bytes of the pBuffer have data in them
{
// On success the server returns the Unique Id to the object that found
// Note that locate can return SUCCESS but did not find any match and thus no uniqueIds
// Also note that locate can return back more than one unique id if the parameters match more than one object on the server
err = m_pWalk->getResponseUniqueId( m_pResponse, KMIP_OP_LOCATE, pUniqueId, &result );
// -> we could test to see if objId matches the uniqueId parameter
if (NULL != objId.pString) delete [] objId.pString;
}
}
}
// -> did we fail trying to get the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// Determine the KMIP server's capabilities
P6CLASSMETHODIMPL CKmipExample14::queryServer( KMIP_QUERY* pQuery ) // return contents returned by the query response
{
P6UINT32 queryFunctions[5];
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
if (NULL == pQuery) return eFail;
// -> Note: the KMIP encoder API lets you build any complex message between the "startRequestMsg", and "endRequestMsg" calls
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
params.pMaxResponseSize = &m_maxMsgSize;
queryFunctions[0] = KMIP_QUERY_OPERATIIONS;
queryFunctions[1] = KMIP_QUERY_OBJECTS;
queryFunctions[2] = KMIP_QUERY_SERVERINFORMATION;
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addQueryRequest( 3, queryFunctions, NULL ))) return err; // -> ask the KMIP server about its capabilities
if (P6FAILED( err = m_cpRequest->endRequestMsg())) return err; // -> tell the encoder that the KMIP request message is done
if (P6FAILED( err = m_cpEncoder->getBufPtr( &pReqBuf ))) return err; // -> get out the encoded request message from the encoder
// -> send the message to the server
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
err = sendMessage( pReqBuf, m_cpSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err ))
{
P6UINT8* pBuffer = NULL;
P6UINT32 bufSize = 0;
P6UINT32 bufUsed = 0;
err = m_pResponse->setUsed( 0 );
if (P6SUCCEEDED( err = m_pResponse->getBufPtr( &pBuffer, &bufSize, &bufUsed ))) // -> get the byte buffer out of the m_pResponse object
{
if (P6SUCCEEDED( err = m_cpSocket->recv( pBuffer, bufSize, &cBytesRead, tTimeout ))) // -> receive the KMIP server response into pBuffer
{
if (P6SUCCEEDED( err = m_pResponse->setUsed( cBytesRead ))) // -> mark how many bytes of the pBuffer have data in them
{
// On success the server returns the Unique Id to the object that found
// Note that locate can return SUCCESS but did not find any match and thus no uniqueIds
// Also note that locate can return back more than one unique id if the parameters match more than one object on the server
err = m_pWalk->getResponseQuery( m_pResponse, pQuery, &result );
}
}
}
// -> did we fail trying to get the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// Main client function
// This function creates a new AES key, then modifies its name attribute, then locates that new key by its modified name,
// then it gets the key bytes via the unique identifier returned from the locate, and finally deletes the key.
//
P6CLASSMETHODIMPL CKmipExample14::run( p6IDataStream* pStreamDebug )
{
P6KMIP_ATTRIBUTE attribute;
KMIP_QUERY queryResults;
P6NCSTR uniqueId = { NULL, 0 };
P6NCSTR locateId = { NULL, 0 };
P6BSTR keyMaterial = { NULL, 0 };
P6ERR err = eOk;
if (P6FAILED( err = createTLSSession())) return err;
// [A] What can the server do?
m_cpStr->setMem( &queryResults, 0, sizeof( KMIP_QUERY ));
if (P6SUCCEEDED( err = queryServer( &queryResults )))
{
printf("\nStart Server Operations: %d\n", queryResults.countOps );
for( P6UINT32 i=0; i < queryResults.countOps; i++ ) {
printf( "%x, ", queryResults.operations[i] );
}
printf("\nEnd Operations\n");
printf("\nStart Server Objects: %d\n", queryResults.countObjects );
for( P6UINT32 j=0; j < queryResults.countObjects; j++ ) {
printf( "%x, ", queryResults.objects[j] );
}
printf("\nEnd Objects\n");
if (NULL != queryResults.serverInfo.pString) {
printf("\nVendorId: %s\n\n", queryResults.serverInfo.pString );
}
}
// [B] Create the key and add a unique Name attribute
if ( P6FAILED( err = createKey( &uniqueId ))) {
printf( "\nFailed to create key on KMIP server %x\n", err );
return err;
}
else
{ printf( "\nNew key's unique identifer [%s] %d\n\n", uniqueId.pString, (int)uniqueId.length );
// -> now update the NAME attribute with its real value
m_cpStr->setMem( &attribute, 0, sizeof( P6KMIP_ATTRIBUTE ));
attribute.type = KMIP_ATTRIB_NAME;
attribute.index = 0;
attribute.value.name.type = KMIP_NAME_TEXTSTR; // these can be a text string or a URL
attribute.value.name.value.pString = "A5B2CD83764F07764654"; // -> TBD for you generate the unique name per key
attribute.value.name.value.length = 20;
if (P6FAILED( err = addAttribute( &uniqueId, &attribute ))) {
printf( "\nFailed to add a Name attribute to the key %x\n", err );
}
}
// [C] Search for the new key via its new Name attribute
if ( P6FAILED( err = locateByName( "A5B2CD83764F07764654", 20, KMIP_OBJECT_SYMMETRICKEY, &locateId ))) {
printf( "\nFailed to locate the key's by name 1 %x\n", err );
}
else
{ // -> verify that the locate worked by comparing uniqueId to locateId
if (uniqueId.length != locateId.length) printf( "\nFailed to locate the key's by name 2\n" );
for( P6UINT32 i=0; i < uniqueId.length; i++ ) {
if (uniqueId.pString[i] != locateId.pString[i]) {
printf( "\nFailed to locate the key's by name 3\n" );
break;
}
}
}
// [D] Extract the key bytes from the server
if ( P6SUCCEEDED( err = getKeyMaterial( &locateId, &keyMaterial )))
{
// key data is binary print out here to verify we received it
printf( "\nStart key material: %d bytes\n", (int)keyMaterial.length );
for( P6UINT32 i=0; i < keyMaterial.length; i++ ) {
printf( "%x", keyMaterial.pString[i] );
}
printf("\nEnd key material\n\n");
}
else printf( "\nFailed to Get key material from the KMIP server %x\n", err );
// [E] Clean up
if (P6FAILED( err = destroyObject( &uniqueId ))) {
printf( "\nFailed to Destroy key off the KMIP server %x\n", err );
}
if (NULL != uniqueId.pString ) delete [] uniqueId.pString;
if (NULL != locateId.pString ) delete [] locateId.pString;
if (NULL != keyMaterial.pString) delete [] keyMaterial.pString;
return err;
}
P6VOID KMIP_ManageKeys( p6IDataStream* pDataStream )
{
CKmipExample14 example;
P6CHAR szTmp[32];
P6ERR err = eOk;
if (P6SUCCEEDED( err = example.initialize())) {
err = example.run( pDataStream );
}
printf( "KMIP 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_ManageKeys( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}