Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-13.cpp
#include <stdio.h>
#include <stdlib.h>
#include <memory> // nothrow
#include "p6skc.h"
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "p6gencerts.h"
#include "p6genkeys.h"
#include "cconsolestream.h"
#include "cwalkmessage.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 CKmipExample13
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream* pStreamDebug );
CKmipExample13(): m_pResponse( NULL ),
m_pWalk( NULL ),
m_port( 0 ),
m_pHostName( NULL )
{ }
~CKmipExample13()
{
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 registerSecretData( P6BCSTR secretData, P6NCSTR* pUniqueId );
P6CLASSMETHOD getSecretData( P6NCSTR* pUniqueId, P6BSTR* pKeyMaterial );
P6CLASSMETHOD destroyObject( P6NCSTR* pUniqueId );
// -> supports crypto
P6CLASSMETHOD getRNG( p6IRandom **ppRandom );
P6CLASSMETHOD getIGenKeys( p6IGenKeys** ppGenKeys );
P6CLASSMETHOD getGenCerts( p6IGenCerts** ppIface );
// -> keystore related
P6CLASSMETHOD savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize );
P6CLASSMETHOD saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath );
P6CLASSMETHOD saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath );
P6CLASSMETHOD createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** pInit, p6IKeystore** pKeystore );
P6CLASSMETHOD loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey );
P6CLASSMETHOD loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert );
P6CLASSMETHOD createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey );
// -> 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
CWalkMessage* 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
};
// Create the key file (supplied by the server vendor) into a p6ICryptoKey object for SSL
//
P6CLASSMETHODIMPL CKmipExample13::createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey )
{
P6ERR err = eOk;
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6CryptoKey, VALIDATECOMPTR( p6ICryptoKeyInit, cpKeyInit ))))
{
if (P6SUCCEEDED( err = cpKeyInit->initialize( P6CKF_NONE, m_cpRandom )))
{
if (P6SUCCEEDED( err = cpKeyInit->loadPKCS8Key( keyBuffer.pString, (P6UINT32) keyBuffer.length, (P6UINT32) keySize )))
{
err = cpKeyInit->queryInterface( VALIDATEIF( p6ICryptoKey, ppNewKey ));
}
}
}
return err;
}
// Load the KMIP server vendor supplied certificate file for SSL into a p6ICert object
//
P6CLASSMETHODIMPL CKmipExample13::loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert )
{
P6ERR err = eOk;
if (P6FAILED( err = p6CreateCryptoInstance( CID_p6Cert, VALIDATECOMPTR( p6ICertInit, cpInit )))) return err;
if (P6FAILED( err = cpInit->initialize( P6CERT_NOFLAGS ))) return err;
if (P6SUCCEEDED( err = cpInit->loadCert( pPath ))) {
err = cpInit->queryInterface( VALIDATEIF( p6ICert, ppNewCert ));
}
return err;
}
// Load the PEM private key file (supplied by the server vendor) for SSL into a p6ICryptoKey object
//
P6CLASSMETHODIMPL CKmipExample13::loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey )
{
P6BSTR keyBuffer = {NULL, 0};
P6SIZE cBytesRead = 0;
P6ERR err = eNotFound;
if (NULL == pwszFilename) return eInvalidArg;
if (P6SUCCEEDED( err = p6CreateInstance( NULL, CID_p6UnbufferedFile, VALIDATECOMPTR( p6IUnbufferedFile, cpFile ))))
{
if (P6SUCCEEDED( err = cpFile->initialize()))
{
if (P6SUCCEEDED( err = cpFile->open( pwszFilename, P6FILE_READ|P6FILE_OPENEXISTING,P6FP_DEFAULT )))
{
P6FILEINFO info;
if ( P6SUCCEEDED( err = cpFile->stat( &info )))
{
if (info.m_cSize)
{
P6CHAR* pBuffer = new (std::nothrow) P6CHAR[(P6UINT32)info.m_cSize];
if ( pBuffer )
{
if (P6SUCCEEDED( err = cpFile->read( pBuffer,(P6UINT32)info.m_cSize, &cBytesRead )))
{
if (cBytesRead == info.m_cSize)
{
keyBuffer.length = cBytesRead;
keyBuffer.pString = (P6UCHAR*)pBuffer;
err = createPKCS8Key( keyBuffer, keySize, ppNewKey );
}
}
}
else err = eIoError;
delete [] pBuffer;
}
else err = eNoMemory;
}
else {
err = eTooSmall;
}
cpFile->close();
}
}
}
return err;
}
// The contents of the keystore is protected by a key
P6CLASSMETHODIMPL CKmipExample13::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 CKmipExample13::getGenCerts( p6IGenCerts** ppIface )
{
P6ERR err = eOk;
*ppIface = NULL;
err = p6CreateCryptoInstance( CID_p6GenCerts, VALIDATEIF( p6IGenCerts, ppIface ));
if (P6SUCCEEDED( err ))
{
err = (*ppIface)->initialize( P6GENCERTS_NOFLAGS );
if (P6FAILED(err))
{
(*ppIface)->release();
*ppIface = NULL;
}
}
return err;
}
// Load in the root certificate we need to connect to the KMIP server into our new store
P6CLASSMETHODIMPL CKmipExample13::saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath )
{
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6WCHAR szHash[20] = {0};
P6SIZE nHash = 0;
P6ERR err = eOk;
p6ComPtr<p6ICert> cpNewCert;
P6ARG args[2];
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if ( P6SUCCEEDED( err = loadCertificateFromFile( confPath, cpNewCert.addressof() )))
{
// SSL library expects a specific namespace depending on how the cert is used
// We store the root cert by the has of its subject which during the SSL protocol is what OpenSSL looks for
if (P6SUCCEEDED( err = cpNewCert->getSubjectHash( &nHash )))
{
P6AI_SIZE_HEXL( args, nHash );
if (P6SUCCEEDED( err = m_cpStr->formatStringW( szHash, P6CNTOF(szHash), NULL, P6TEXT("%1$"), args, 1 )))
err = pKeystore->setCertificate( pNamespace, szHash, cpNewCert, NULL );
}
}
return err;
}
// KMIP requires both server and cliuent certificates, we store the certificate under the host name of the KMIP server
P6CLASSMETHODIMPL CKmipExample13::saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath )
{
p6ComPtr<p6ICert> cpNewCert;
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if (P6SUCCEEDED( err = loadCertificateFromFile( confPath, cpNewCert.addressof() )))
{
err = pKeystore->setCertificate( pNamespace, pName, cpNewCert, NULL );
}
return err;
}
// Load in the keys we need to connect to the KMIP server into our new store, we store the private key under the host name of the KMIP server
P6CLASSMETHODIMPL CKmipExample13::savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize )
{
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if (P6SUCCEEDED( err = loadKeyFromFile( confPath, keySize, cpNewKey.addressof() )))
{
err = pKeystore->setKey( pNamespace, pName, cpNewKey, NULL, 0, NULL );
}
return err;
}
// Create a keystore and then load it with the information we need to connect to the KMIP server.
// P6R's SSL looks into the keystore for certificates and the private key when it starts a connection to the KMIP server.
P6CLASSMETHODIMPL CKmipExample13::createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** ppInit, p6IKeystore** ppKeystore )
{
P6ERR err = eOk;
*ppInit = NULL;
*ppKeystore = NULL;
// Create the keystore and fill it with vendor provided SSL certificates
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Keystore, VALIDATEIF( p6IKeystoreInit, ppInit )))) return err;
err = (*ppInit)->queryInterface( VALIDATEIF( p6IKeystore, ppKeystore ));
if (P6FAILED( err = (*ppInit)->initialize( P6KEYSTORE_NOFLAGS, m_cpCrypto, SH_SHA256, m_cpSignKey )))
{
if (NULL != (*ppKeystore)) (*ppKeystore)->release();
(*ppKeystore) = NULL;
(*ppInit)->release();
(*ppInit) = NULL;
return err;
}
/*
* The first parameter of openSigned() is the file path where to create and access keystore databases.
* If NULL, then the keystore location will default to the P6R database directory (i.e., the "db" sub-directory).
* If the SKC is installed in a read-only directory then the first parameter will need to be set to
* an existing read/write directory.
*/
if (P6FAILED( err = (*ppInit)->openSigned( NULL, pKeystoreName )))
{
if (NULL != (*ppKeystore)) (*ppKeystore)->release();
(*ppKeystore) = NULL;
(*ppInit)->release();
(*ppInit) = NULL;
return err;
}
if (P6FAILED( err = saveRootCertInStore( (*ppKeystore), P6TEXT("p6r.trustedroot"), rootPEM ))) return err;
if (P6FAILED( err = saveClientCertInStore( (*ppKeystore), P6TEXT("p6r.clientauth"), m_pHostName, certPEM ))) return err;
if (P6FAILED( err = savePrivKeyInStore( (*ppKeystore), P6TEXT("p6r.clientauth"), m_pHostName, privPEM, privSize ))) return err;
return eOk;
}
// Key generation requires an entropy source
P6CLASSMETHODIMPL CKmipExample13::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 CKmipExample13::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 CKmipExample13::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"), 2048, 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"), 8192, 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
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_12, m_cpPool, &encodePrefs ))) return err;
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) CWalkMessage())) 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 CKmipExample13::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 CKmipExample13::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
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 secret data already stored on the KMIP server by its unique identifer.
// Secret Data is store in the key material part of the KMIP response message.
P6CLASSMETHODIMPL CKmipExample13::getSecretData( 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
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 secret data 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 destroy the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// Store the secret data on the KMIP server.
//
P6CLASSMETHODIMPL CKmipExample13::registerSecretData( P6BCSTR secretData, P6NCSTR* pUniqueId )
{
P6KMIP_TEMPLATEATTRIBUTE attributes; // -> not used in our test case, only for adding attributes directly to the object stored on the server
KMIPMSG_RESULT result = { 0, 0, {NULL, 0 }};
p6IIoBuffer* pReqBuf = NULL;
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6INTERVAL tTimeout = 0;
P6ERR err = eOk;
if (NULL == secretData.pString) return eFail;
// -> create a Register KMIP message request
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS )); // P6KMIP_REQUESTPARAMS lets you customize the KMIP request message
m_cpStr->setMem( &attributes, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
if (P6FAILED( err = m_cpRequest->startRequestMsg( params ))) return err; // -> tell the encoder to start a new KMIP request message
if (P6FAILED( err = m_cpRequest->addRegisterSecretDataRequest( KMIP_SECRET_SEED, secretData, NULL, attributes, NULL ))) return err; // -> add a Register operation to the request 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 pUniqueId contains an identifier for the registered secret data 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_REGISTER, pUniqueId, &result );
}
}
}
// -> did we fail trying to destroy the object?
if (P6SUCCEEDED( err )) {
if (KMIP_RESULT_SUCCESS != result.resultStatus) return eFail;
}
}
return err;
}
// Main client function
//
P6CLASSMETHODIMPL CKmipExample13::run( p6IDataStream* pStreamDebug )
{
P6NCSTR uniqueId = { NULL, 0 };
P6BSTR keyMaterial = { NULL, 0 };
P6BCSTR secretData = { NULL, 0 };
P6ERR err = eOk;
const P6UCHAR fakeSecretData[] = { 0x53,0x65,0x63,0x72,0x65,0x82,0x74,0x50,0x02,0x61,0x73,0x73,0x77,0x6F,0x72,0x64 };
if (P6FAILED( err = createTLSSession())) return err;
secretData.pString = fakeSecretData;
secretData.length = sizeof( fakeSecretData );
if ( P6FAILED( err = registerSecretData( secretData, &uniqueId ))) {
printf( "\nFailed to store secret data on KMIP server %x\n", err );
return err;
}
else printf( "\nSecret Data's unique identifer [%s] %ld\n", uniqueId.pString, (P6ULONG)uniqueId.length );
// -> compare what data we sent to the server and what we got back
if ( P6SUCCEEDED( err = getSecretData( &uniqueId, &keyMaterial )))
{
if ( keyMaterial.length != sizeof( fakeSecretData )) {
printf( "\nSecret data size mismatch %ld %ld\n", sizeof(fakeSecretData), (P6ULONG)keyMaterial.length );
}
else
{ for( P6UINT32 i=0; i < keyMaterial.length; i++ )
{
if (fakeSecretData[i] != keyMaterial.pString[i]) {
printf("\nSecret data value mismatch offset: %d\n", i );
break;
}
}
}
}
else printf( "\nFailed to Get Secret data from the server %x\n", err );
// -> clean up
if (P6FAILED( err = destroyObject( &uniqueId ))) {
printf( "\nFailed to Destroy Secret data off the server %x\n", err );
}
if (NULL != uniqueId.pString ) delete [] uniqueId.pString;
if (NULL != keyMaterial.pString) delete [] keyMaterial.pString;
return err;
}
P6VOID KMIP_SecretData( p6IDataStream* pDataStream )
{
CKmipExample13 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_SecretData( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}