Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-7.cpp
#include <stdio.h>
#include <stdlib.h>
#include <memory> // nothrow
#include "p6skc.h"
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "p6gencerts.h"
#include "p6genkeys.h"
#include "cconsolestream.h"
using namespace P6R;
P6DECLARE_CID( p6KMIPClient );
P6DECLARE_CID( p6EntropySource );
P6DECLARE_CID( p6Random );
P6DECLARE_IID( p6IRunningIface );
P6DECLARE_CID( p6Cert );
P6DECLARE_CID( p6GenCerts );
P6DECLARE_CID( p6GenKeys );
P6DECLARE_CID( p6Sign );
P6DECLARE_CID( p6SymmetricCrypto );
P6DECLARE_CID( p6CryptoKey );
P6DECLARE_CID( p6Dir );
P6DECLARE_CID( p6UnbufferedFile );
P6DECLARE_CID( p6Keystore );
namespace {
// Fake Certificate data declarations for test
#define ONEYEAR (60*60*24*365) // seconds in one year
P6CERTRDN gKMIP_7_Subject[] = { {RDN_COUNTRY, (P6WCHAR*)P6TEXT("US")}, {RDN_STATE, (P6WCHAR*)P6TEXT("CA")},
{RDN_LOCALITY, (P6WCHAR*)P6TEXT("95030")}, {RDN_ORG, (P6WCHAR*)P6TEXT("P6R, Inc.")},
{RDN_ORGUNIT, (P6WCHAR*)P6TEXT("Engineering")}, {RDN_ORGUNIT, (P6WCHAR*)P6TEXT("Optics2")},
{RDN_COMMONNAME, (P6WCHAR*)P6TEXT("KMIP Engineer")}, {RDN_EMAIL, (P6WCHAR*)P6TEXT("mark@p6r.com")},
{RDN_GIVENNAME, (P6WCHAR*)P6TEXT("Mark")}, {RDN_SURNAME, (P6WCHAR*)P6TEXT("Joseph")},
{RDN_NAME, (P6WCHAR*)P6TEXT("Mark K. Joseph")} };
P6CERTRDN gKMIP_7_Issuer[] = { {RDN_COUNTRY, (P6WCHAR*)P6TEXT("US")}, {RDN_STATE, (P6WCHAR*)P6TEXT("CA")},
{RDN_LOCALITY, (P6WCHAR*)P6TEXT("95030")}, {RDN_ORG, (P6WCHAR*)P6TEXT("P6R, Inc.")},
{RDN_ORGUNIT, (P6WCHAR*)P6TEXT("Services")}, {RDN_COMMONNAME, (P6WCHAR*)P6TEXT("P6R CA")} };
class CKmipExample7
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream *pStreamDebug );
CKmipExample7()
: m_port(0), m_compatMask(0), m_pHostName(NULL)
{ }
~CKmipExample7()
{
if (NULL != m_pHostName) m_cpStr->wstrfree( m_pHostName );
if (NULL != m_cpStoreInit) m_cpStoreInit->close();
}
protected:
// -> connection related
P6CLASSMETHOD createSession( p6IKMIPClient* pClient, P6BOOL bWithCredentials );
P6CLASSMETHOD setPreferences( P6KMIP_PREF* pPrefs, P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy );
P6CLASSMETHOD destroyObject( p6IKMIPClient* pClient, P6NCSTR objectId, P6BOOL bFree );
// -> function used often to extract a unique id returned by the server
P6CLASSMETHOD extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId );
P6R::P6BOOL isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id );
// -> supports crypto
P6CLASSMETHOD getRNG(P6R::p6IRandom **ppRandom);
P6CLASSMETHOD getIGenKeys( p6IGenKeys** ppGenKeys );
P6CLASSMETHOD getGenCerts( p6IGenCerts** ppIface );
// -> keystore related
P6CLASSMETHOD savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize );
P6CLASSMETHOD saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath );
P6CLASSMETHOD saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath );
P6CLASSMETHOD createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** pInit, p6IKeystore** pKeystore );
P6CLASSMETHOD loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey );
P6CLASSMETHOD loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert );
P6CLASSMETHOD createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey );
p6ComPtr<p6ISafeString> m_cpStr; // -> generic string library
p6ComPtr<p6ISymmetricCrypto> m_cpCrypto; // -> used to encrypt the contents of the keystore
p6ComPtr<p6ICryptoKey> m_cpSignKey; // -> keystore related
p6ComPtr<p6IRandom> m_cpRandom; // -> crypto related
p6ComPtr<p6IKeystore> m_cpKeystore; // -> the KMIP client requires that a properly initialzed keystore is setup
p6ComPtr<p6IKeystoreInit> m_cpStoreInit; // -> keystore related
P6UINT32 m_port; // -> KMIP server port to connect to
P6UINT32 m_compatMask; // -> compatibility mask: 0 use TTLV message encoding, 2 use XML message encoding, 4 use JSON message encoding
P6WCHAR* m_pHostName; // -> IP address or FQDN of KMIP server to connect to
};
// Create the key file (supplied by the server vendor) into a p6ICryptoKey object for SSL
//
P6CLASSMETHODIMPL CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::initialize()
{
P6ERR err = eOk;
P6WCHAR dbPath[P6MAXPATH+20] = {0};
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6ISafeString, m_cpStr )))) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Dir, VALIDATECOMPTR( p6IDir, cpDir )))) return err;
if (P6FAILED( err = cpDir->initialize())) return err;
if (P6FAILED( err = getRNG( m_cpRandom.addressof()))) return err;
// -> right now configuration is hard coded, but could read out of a config file later
// m_compatMask is set to zero for TTLV message encoding, set to 2 for XML message encoding, and set to 4 for JSON message encoding
//
m_port = 5696;
m_compatMask = 0;
if (P6FAILED( err = m_cpStr->wstrdup( P6TEXT("fqdn.com"), &m_pHostName ))) return err;
// -> create the keys required to encrypt the contents of the keystore
if (P6SUCCEEDED( err = getIGenKeys( cpGenKey.addressof() )))
{
err = cpGenKey->genSymmetricKey( m_cpSignKey.addressof(), 256, P6FALSE );
if (P6SUCCEEDED( err )) {
err = cpGenKey->genSymmetricKey( cpKey.addressof(), 256, P6FALSE );
}
}
if (P6SUCCEEDED( err )) err = p6CreateCryptoInstance( CID_p6SymmetricCrypto, VALIDATECOMPTR( p6ISymmetricCrypto, m_cpCrypto ));
if (P6SUCCEEDED( err )) err = m_cpCrypto->initialize( P6SYM_NOPADDING, CIPHER_AES_CFB );
if (P6SUCCEEDED( err )) err = m_cpCrypto->setKey( cpKey );
// -> initialize the keystore with keys provided to us by the server vendor
dbPath[0] = P6CHAR('\0');
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( dbPath, P6CNTOF(dbPath), P6TEXT("/db/KMIP12_keystore"), NULL ))) return err;
cpDir->unlink( dbPath );
dbPath[0] = P6CHAR('\0');
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( dbPath, P6CNTOF(dbPath), P6TEXT("/db/KMIP12_keystore.sig"), NULL ))) return err;
cpDir->unlink( dbPath );
return createKeystore( P6TEXT("KMIP12_keystore"), P6TEXT("RootCert.pem"), P6TEXT("ClientCert.pem"), P6TEXT("ClientPrivate.pem"), 2048, m_cpStoreInit.addressof(), m_cpKeystore.addressof());
}
// The client creates an SSL connection to the KMIP server and then makes its requests
// Configure the client on how it is supposed to behave (e.g., protocol version, use TTLV or XML or JSON, how to encode keys PKCSXXX).
//
P6CLASSMETHODIMPL CKmipExample7::setPreferences( P6KMIP_PREF* pPrefs, P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy )
{
m_cpStr->setMem( pPrefs, 0, sizeof( P6KMIP_PREF ));
pPrefs->pVersion = "1.2"; pPrefs->pSubdirectory = pLogDir;
pPrefs->asynchronous = asynchronous; pPrefs->maxBufferSize = maxBufferSize;
pPrefs->connectTimeout = connectTimeout; pPrefs->sendTimeout = sendTimeout;
pPrefs->receiveTimeout = receiveTimeout; pPrefs->initialBufCount = initialBufCount;
pPrefs->growBufsBy = growBufsBy; pPrefs->privateKeyEncoding = privateKeyEncoding;
pPrefs->publicKeyEncoding = publicKeyEncoding; pPrefs->symmetricKeyEncoding = symmetricKeyEncoding;
pPrefs->compatibility1 = m_compatMask;
return eOk;
}
// We just hard code the credentials (i.e., name and password) for a test if needed
P6CLASSMETHODIMPL CKmipExample7::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 CKmipExample7::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 CKmipExample7::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 CKmipExample7::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");
}
bMatch = isEqualId( pUniqueId, objectId );
if (!bMatch) {
printf("\nKMIP server to destroy object with wrong object uniqueId\n");
}
if (bFree) delete [] objectId.pString;
// -> did we fail trying to destroy the object?
if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) return eFail;
}
return err;
}
// Asymmtric keys have a different set of attributes to set than symmetric keys expected by a KMIP server.
// Main client function
// Identical to the KMIP TC_82_12 interop test case.
//
P6CLASSMETHODIMPL CKmipExample7::run( p6IDataStream *pStreamDebug )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_LOCATEPARAMS locateParams;
P6KMIP_ATTRIBPARAMS attribParams;
P6KMIP_ATTRIBUTE attributeList[6];
P6UCHAR* pX509 = NULL;
P6SIZE x509Length = 0;
P6NCSTR privId = {NULL, 0};
P6NCSTR pubId = {NULL, 0};
p6IKMIPStr* pUniqueId = NULL;
p6IGenCerts* pGenCerts = NULL;
p6IGenKeys* pGenKey = NULL;
p6ICryptoKey* pPubKey = NULL;
p6ICryptoKey* pPrvKey = NULL;
p6ICert* pSelfCert = NULL;
p6ICert* pAppCert = NULL;
P6BOOL bMatch = P6FALSE;
P6ERR err = eOk;
// [1] Initialize, "TC_82_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
// -> note here we tell the KMIP client to use the key formats: KMIP_KEYFORMAT_PKCS8, KMIP_KEYFORMAT_X509 for the private and public keys
//
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPClient, VALIDATECOMPTR( p6IKMIPClient, cpClient )))) return err;
setPreferences( &preferences, P6TEXT("TC_82_12"), 0, KMIP_KEYFORMAT_PKCS8, KMIP_KEYFORMAT_X509, 0, 60000, 30000, 30000, 120000, 2, 2 );
err = cpClient->initialize( (P6KMIPFLG_TRACE_MSGS | P6KMIPFLG_TRACE_FORMATKMIPXML), m_cpKeystore, preferences );
// [2] Open a connection to the KMIP server via SSL
if (P6FAILED( err = createSession( cpClient, P6FALSE ))) return err;
// [3] Create an asymmetric key pair
// -> here we create the certificate so we can extract the public Key as an X.509 format
if (P6FAILED( err = getGenCerts( &pGenCerts ))) return err;
if (P6SUCCEEDED( err = getIGenKeys( &pGenKey )))
{
err = pGenKey->genRSAKeyPair( &pPubKey, &pPrvKey, 1024 );
pGenKey->release();
if (P6FAILED( err )) { pGenCerts->release(); return err; }
// Note: no need to set meta data on this key, its type (i.e., RSA) is already part of the object
}
// [4] We are passing the public key in X509 ASN.1 key format (i.e. KMIP_KEYFORMAT_X509), to do that extract if out of the certificate
// -> generate a self-signed certificate
if (P6FAILED( err = pGenCerts->genCertificate( gKMIP_7_Issuer, 6, pPubKey, pPrvKey, NULL, ONEYEAR, NULL, 0, &pSelfCert ))) {
pPubKey->release();
pPrvKey->release();
pGenCerts->release();
return err;
}
// -> now generate an application certifcate signed with the self signed certifcate
if (P6FAILED( err = pGenCerts->genCertificate( gKMIP_7_Subject, 11, pPubKey, pPrvKey, pSelfCert, ONEYEAR, NULL, 0, &pAppCert ))) {
pPubKey->release();
pPrvKey->release();
pGenCerts->release();
pSelfCert->release();
return err;
}
// -> get the required buffer length then call get to get the public key bytes
err = pSelfCert->getPublicKey( NULL, 0, &x509Length );
if ( eTooSmall == err && 0 < x509Length )
{
if (NULL != (pX509 = new P6UCHAR[ x509Length ])) {
err = pSelfCert->getPublicKey( pX509, x509Length, &x509Length );
}
}
pSelfCert->release();
pAppCert->release();
pGenCerts->release();
if (NULL == pX509) return eNoMemory;
// [4] Register both private and public keys with the KMIP server
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &keyParams, 0, sizeof( P6KMIP_REGKEYPARAMS ));
m_cpStr->setMem( &privKey, 0, sizeof( P6KMIP_NEWOBJECT ));
attributeList[0].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[0].index = 0;
attributeList[0].value.cryptoUsageMask = KMIP_USE_SIGN;
attributeList[1].type = KMIP_ATTRIB_EXTENSION;
attributeList[1].index = 0;
attributeList[1].value.extension.xName.pString = "x-ID";
attributeList[1].value.extension.xName.length = 4;
attributeList[1].value.extension.xValue.vText.pString = "TC-82-12-prikey";
attributeList[1].value.extension.xValue.vText.length = 15;
keyParams.attributes.attribCount = 2;
keyParams.attributes.pAttributeList = attributeList;
keyParams.type = 1;
keyParams.value.pKey = pPrvKey;
if ( P6FAILED( err = cpClient->registerKeyObject( keyParams, &privKey, &resultCodes ))) {
printf("\ncall to registerKeyObject for private key has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != privKey.pUniqueId) {
err = extractUniqueId( privKey.pUniqueId, &privId );
}
// -> now register the public key in the X509 format
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &keyParams, 0, sizeof( P6KMIP_REGKEYPARAMS ));
m_cpStr->setMem( &pubKey, 0, sizeof( P6KMIP_NEWOBJECT ));
attributeList[0].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[0].index = 0;
attributeList[1].type = KMIP_ATTRIB_LINK;
attributeList[1].index = 0;
attributeList[1].value.link.linkType = KMIP_LINK_PRIVATEKEY; // -> point to the private key to associate the two
attributeList[1].value.link.linkObjectId = privId; // -> unique identifer returned by KMIP server (see extractUniqueId call above)
attributeList[2].type = KMIP_ATTRIB_EXTENSION;
attributeList[2].index = 0;
attributeList[2].value.extension.xName.pString = "x-ID";
attributeList[2].value.extension.xName.length = 4;
attributeList[2].value.extension.xValue.vText.pString = "TC-82-12-pubkey";
attributeList[2].value.extension.xValue.vText.length = 15;
keyParams.attributes.attribCount = 3;
keyParams.attributes.pAttributeList = attributeList;
// -> look up the definition of P6KMIP_REGKEYPARAMS to see all the key types that are supported
keyParams.type = 8;
keyParams.value.X509Key.cryptoLength = 1024;
keyParams.value.X509Key.keyMaterial.pString = pX509;
keyParams.value.X509Key.keyMaterial.length = x509Length;
if ( P6FAILED( err = cpClient->registerKeyObject( keyParams, &pubKey, &resultCodes ))) {
printf("\ncall to registerKeyObject for public key has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != pubKey.pUniqueId) {
err = extractUniqueId( pubKey.pUniqueId, &pubId ); // -> unique id of the public key assigned by the KMIP server
}
if (NULL != pPubKey) pPubKey->release();
if (NULL != pPrvKey) pPrvKey->release();
// [5] Add a link to the public key pointing to its private key
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &attribParams, 0, sizeof( P6KMIP_ATTRIBPARAMS ));
m_cpStr->setMem( &getResult, 0, sizeof( P6KMIP_ATTRIBRESULT ));
attribParams.uniqueId = privId; // -> identifies the private key managed object to modify
attribParams.attribute.index = 0;
attribParams.attribute.value.link.linkObjectId = pubId; // -> identifies the public key managed object for the link
if ( P6FAILED( err = cpClient->addAttributeObject( attribParams, &getResult, &resultCodes ))) {
printf("\ncall to addAttributeObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
// -> even if we don't use it we must free it, the KMIP server echoes all the attributes we defined
if (NULL != getResult.pAttribute) getResult.pAttribute->release();
// [6] Search for a key by unique id
// -> when looking for a public key we use the private key's link to show they are linked together
if (NULL != pubId.pString)
{
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &locateParams, 0, sizeof( P6KMIP_LOCATEPARAMS ));
attributeList[0].type = KMIP_ATTRIB_OBJECTTYPE;
attributeList[0].index = 0;
attributeList[0].value.objectType = KMIP_OBJECT_PUBLICKEY; // -> search for a public key
attributeList[1].type = KMIP_ATTRIB_LINK;
attributeList[1].index = 0;
attributeList[1].value.link.linkType = KMIP_LINK_PRIVATEKEY; // -> a public key linked to this private key
attributeList[1].value.link.linkObjectId = privId;
locateParams.attribCount = 2;
locateParams.pAttributeList = attributeList;
if ( P6FAILED( err = cpClient->locateObject( locateParams, &pUniqueId, &resultCodes ))) {
printf("\ncall to locateObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != pUniqueId)
{
// -> did we find the public key using the private key's link?
bMatch = isEqualId( pUniqueId, pubId );
}
}
// [7] Finished clean up removed the keys off of the KMIP server
destroyObject( cpClient, privId, P6TRUE );
destroyObject( cpClient, pubId, P6TRUE );
err = cpClient->close();
return err;
}
P6VOID KMIP_TC_82_12( p6IDataStream* pDataStream )
{
CKmipExample7 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_82_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;
}