Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-11.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_11_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 CKmipExample11
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream *pStreamDebug );
CKmipExample11()
: m_port(0), m_compatMask(0), m_pHostName(NULL)
{ }
~CKmipExample11()
{
if (NULL != m_pHostName) m_cpStr->wstrfree( m_pHostName );
if (NULL != m_cpStoreInit) m_cpStoreInit->close();
}
protected:
// -> connection related
P6CLASSMETHOD createSession( p6IKMIPClient* pClient );
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 );
// -> managed object related
P6CLASSMETHOD getAttributeList( p6IKMIPClient* pClient, P6NCSTR objId );
P6CLASSMETHOD getAllAttributes( p6IKMIPClient* pClient, P6NCSTR objId );
P6CLASSMETHOD getServerCertAttributes( p6IKMIPClient* pClient, P6NCSTR certId );
P6CLASSMETHOD destroyObject( p6IKMIPClient* pClient, P6NCSTR objectId, P6BOOL bFree );
// -> function used often to extract a unique id returned by the server
P6CLASSMETHOD extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId );
P6R::P6BOOL isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id );
// -> supports crypto
P6CLASSMETHOD getRNG(P6R::p6IRandom **ppRandom);
P6CLASSMETHOD getIGenKeys( p6IGenKeys** ppGenKeys );
P6CLASSMETHOD getGenCerts( p6IGenCerts** ppIface );
// -> keystore related
P6CLASSMETHOD 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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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;
}
//
P6CLASSMETHODIMPL CKmipExample11::createSession( p6IKMIPClient* pClient )
{
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;
return pClient->open( m_pHostName, m_port, 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 CKmipExample11::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 CKmipExample11::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 CKmipExample11::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;
}
// Get the string names of every type of attribute that the certificate has associated with it.
// The objId parameter is a unique identifier to the object on the KMIP server which could be a certificate, key, secret data, or any managed object.
// Attribute lists on an object vary based on what has been done to it.
//
P6CLASSMETHODIMPL CKmipExample11::getAttributeList( p6IKMIPClient* pClient, P6NCSTR objId )
{
P6KMIP_RESULT resultCodes;
P6KMIP_ATTRIBUTENAMES attribNames;
P6CHAR tempBuffer[500];
P6NCSTR enumStr = {NULL, 0};
P6UINT32 count = 0;
P6UINT32 number = 0;
P6BOOL bMatch = P6FALSE;
P6ERR err = eOk;
P6ERR err2 = eOk;
m_cpStr->setMem(&resultCodes, 0, sizeof(P6KMIP_RESULT));
m_cpStr->setMem(&attribNames, 0, sizeof(P6KMIP_ATTRIBUTENAMES));
if ( P6FAILED( err = pClient->getAttributeList( objId, NULL, &attribNames, &resultCodes ))) {
printf("\ncall to getAttributeList has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != attribNames.pUniqueId && NULL != attribNames.pAttributeName)
{
bMatch = isEqualId( attribNames.pUniqueId, objId ); // -> could verify that the object returned was the requested
err = attribNames.pAttributeName->count( &number ); // -> the number of entries in the enumerator is available via one call
// -> the next() call will return eEndOfRecord when it hits the end of all attributes, after which we could call reset() and start over again
attribNames.pAttributeName->reset();
count = 0;
enumStr.pString = (const P6R::P6CHAR*)tempBuffer;
enumStr.length = 500;
while( P6SUCCEEDED( err2 = attribNames.pAttributeName->next( &enumStr )))
{
// several possible attribute names are: "Last Change Date", "Initial Date", "x-ID", "Object Type"
printf( "\nattributeList [%s] %d\n", enumStr.pString, enumStr.length );
enumStr.length = 500;
}
attribNames.pAttributeName->release();
}
return err;
}
// Query the KMIP server for all attributes of a certificate
//
P6CLASSMETHODIMPL CKmipExample11::getAllAttributes( p6IKMIPClient* pClient, P6NCSTR objId )
{
P6KMIP_RESULT resultCodes;
P6KMIP_GETATTRIBPARAMS attribParams;
P6UINT32 attribType = 0;
P6UINT32 number = 0;
P6ERR err = eOk;
// NOTE, since we have not specified any attribute names the KMIP server will return ALL attributes associate with the object
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &getResult, 0, sizeof( P6KMIP_ATTRIBRESULT ));
m_cpStr->setMem( &attribParams, 0, sizeof( P6KMIP_GETATTRIBPARAMS ));
attribParams.uniqueId = objId;
if ( P6FAILED( err = pClient->getAttributes( attribParams, &getResult, &resultCodes ))) {
printf("\ncall to getAttributes has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != getResult.pAttribute)
{
number = 0;
err = getResult.pAttribute->count( &number );
// -> the enumerator will return eEndOfRecord when there are no more entries
while( P6SUCCEEDED( getResult.pAttribute->next( &attribType )))
{
// -> LOOK at the getServerCertAttributes to see an example of how to get the value out of each attribute based on its type
//m_cpStr->setMem( &objAttribute, 0, sizeof( P6KMIP_OBJECT_ATTRIBUTE ));
printf( "\nlist the types of attributes returned: %d\n", attribType );
}
getResult.pAttribute->release();
}
return err;
}
// Query the KMIP server for specific attributes of a certificate.
// This function shows in detail how to extract the value of each attribute requested.
//
P6CLASSMETHODIMPL CKmipExample11::getServerCertAttributes( p6IKMIPClient* pClient, P6NCSTR certId )
{
P6KMIP_RESULT resultCodes;
P6KMIP_GETATTRIBPARAMS attribParams;
P6NCSTR attribNames[11];
P6CHAR tempName[200];
P6CHAR serialNumber[200];
P6UINT32 attribType = 0;
P6CHAR* pName = NULL;
P6ERR err = eOk;
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &getResult, 0, sizeof( P6KMIP_ATTRIBRESULT ));
m_cpStr->setMem( &attribParams, 0, sizeof( P6KMIP_GETATTRIBPARAMS ));
// -> tell the KMIP server the attributes of the certificate that we are interested in
attribNames[0].pString = "Certificate Identifier";
attribNames[0].length = 22;
attribNames[1].pString = "Certificate Issuer";
attribNames[1].length = 18;
attribNames[2].pString = "Certificate Subject";
attribNames[2].length = 19;
attribNames[3].pString = "Certificate Type";
attribNames[3].length = 16;
attribNames[4].pString = "Digital Signature Algorithm";
attribNames[4].length = 27;
attribNames[5].pString = "Cryptographic Length";
attribNames[5].length = 20;
attribNames[6].pString = "Certificate Length";
attribNames[6].length = 18;
attribNames[7].pString = "X.509 Certificate Identifier";
attribNames[7].length = 28;
attribNames[8].pString = "X.509 Certificate Issuer";
attribNames[8].length = 24;
attribNames[9].pString = "X.509 Certificate Subject";
attribNames[9].length = 25;
// -> NOTE, you can use this same call to get attributes for a KEY, since it is the unique identifier that points to the object on the KMIP server
attribParams.uniqueId = certId;
attribParams.attribCount = 10;
attribParams.pAttributeNames = attribNames;
if ( P6FAILED( err = pClient->getAttributes( attribParams, &getResult, &resultCodes ))) {
printf("\ncall to getAttributes has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
// -> NOTE, that getResult.pAttribute is an enumerator object returned from the call to getAttributes() above.
// It contains all the attributes for the certificate that the KMIP server returned.
if (NULL != getResult.pAttribute)
{
// -> the enumerator will return eEndOfRecord when there are no more entries
while (P6SUCCEEDED( getResult.pAttribute->next( &attribType )))
{
m_cpStr->setMem( &objAttribute, 0, sizeof( P6KMIP_OBJECT_ATTRIBUTE ));
switch( attribType ) {
// -> *** NOTE ***, here we just use large fixed buffers to get out the identifier value from the enumerator
objAttribute.value.certificateIdentifier.issuer.pString = tempName;
objAttribute.value.certificateIdentifier.serialNumber.pString = serialNumber;
err = getResult.pAttribute->getValue( &objAttribute );
// should be something like: "CN=KMIP,OU=OASIS,O=TEST,C=US"
// value in: objAttribute.value.certificateIdentifier.issuer.pString
// value in: objAttribute.value.certificateIdentifier.serialNumber.pString
break;
// -> *** NOTE ***, here we do dynamic memory allocation to get out the issuer value from the enumerator
if (eTooSmall == (err = getResult.pAttribute->getValue( &objAttribute )))
{
if (NULL != (pName = new (std::nothrow) P6CHAR[ objAttribute.value.certificateIssuer.distinguishedName.length + 1 ])) {
err = getResult.pAttribute->getValue( &objAttribute );
//printf( "\nissuer name [%s] %d\n", pName, objAttribute.value.certificateIssuer.distinguishedName.length );
}
}
// should be something like: "CN=KMIP,OU=OASIS,O=TEST,C=US"
// value in: objAttribute.value.certificateIssuer.distinguishedName.pString
if (NULL != pName) { delete [] pName; pName = NULL; }
break;
err = getResult.pAttribute->getValue( &objAttribute );
// should be something like: "CN=Client,OU=KMIP,O=ACME,C=US"
// value in: objAttribute.value.certificateSubject.distinguishedName.pString
break;
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.certificateType, should be KMIP_CERT_X509
break;
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.cryptoLength
break;
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.certificateLength
break;
err = getResult.pAttribute->getValue( &objAttribute );
// value in: objAttribute.value.digitalSigAlg, should be KMIP_SIG_SHA256RSA
break;
// -> these are all ASN.1 binary strings
objAttribute.value.x509certificateIdentifier.issuer.pString = (const P6UCHAR*)tempName;
objAttribute.value.x509certificateIdentifier.serialNumber.pString = (const P6UCHAR*)serialNumber;
err = getResult.pAttribute->getValue( &objAttribute );
break;
// -> these are all ASN.1 binary strings
err = getResult.pAttribute->getValue( &objAttribute );
break;
// -> these are all ASN.1 binary strings
objAttribute.value.x509certificateIssuer.distinguishedName.pString = (const P6UCHAR*)tempName;
err = getResult.pAttribute->getValue( &objAttribute );
break;
default:
break;
}
}
getResult.pAttribute->release();
}
return err;
}
//
P6CLASSMETHODIMPL CKmipExample11::run( p6IDataStream *pStreamDebug )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_CERTPARAMS certParams;
P6KMIP_ATTRIBUTE attributeList[6];
P6NCSTR certId = {NULL, 0};
p6IGenCerts* pGenCerts = NULL;
p6IGenKeys* pGenKey = NULL;
p6ICryptoKey* pPubKey = NULL;
p6ICryptoKey* pPrvKey = NULL;
p6ICert* pSelfCert = NULL;
P6ERR err = eOk;
// [1] Initialize, ENUMERATOR_1" directory created in the configured log directory to hold all logs for this example
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPClient, VALIDATECOMPTR( p6IKMIPClient, cpClient )))) return err;
setPreferences( &preferences, P6TEXT("ENUMERATOR_1"), 0, 0, 0, 0, 60000, 30000, 30000, 120000, 2, 2 );
err = cpClient->initialize( (P6KMIPFLG_TRACE_MSGS | P6KMIPFLG_TRACE_FORMATKMIPXML), m_cpKeystore, preferences );
if (P6FAILED( err = createSession( cpClient ))) return err;
// [2] We are just going to create and register the certificate, but we need a key pair to do that
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
}
// -> generate a self-signed certificate
if (P6FAILED( err = pGenCerts->genCertificate( gKMIP_11_Issuer, 6, pPubKey, pPrvKey, NULL, ONEYEAR, NULL, 0, &pSelfCert ))) {
pPubKey->release();
pPrvKey->release();
pGenCerts->release();
return err;
}
// [3] Register a certificate on the KMIP server, the server will add a bunch of its own attributes to this cert
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &certParams, 0, sizeof( P6KMIP_CERTPARAMS ));
m_cpStr->setMem( &certObj, 0, sizeof( P6KMIP_NEWOBJECT ));
attributeList[0].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[0].index = 0;
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-cert1";
attributeList[1].value.extension.xValue.vText.length = 14;
certParams.attributes.attribCount = 2;
certParams.attributes.pAttributeList = attributeList;
certParams.type = 1;
certParams.value.pCertificate = pSelfCert;
if ( P6FAILED( err = cpClient->registerCertificateObject( certParams, &certObj, &resultCodes ))) {
printf("\ncall to registerCertificateObject for cert has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error\n");
}
if (NULL != certObj.pUniqueId) {
err = extractUniqueId( certObj.pUniqueId, &certId ); // -> unique id of the certificate assigned by the KMIP server
}
pSelfCert->release();
// [5] Look at all the attributes associated with the certificate, several ways, using enumerators
if (P6FAILED( err = getAttributeList( cpClient, certId ))) {
printf("\ncall to getAttributeList for cert has failed %x\n", err );
}
if (P6FAILED( err = getServerCertAttributes( cpClient, certId ))) {
printf("\ncall to getServerCert Attributes for cert has failed %x\n", err );
}
if (P6FAILED( err = getAllAttributes( cpClient, certId ))) {
printf("\ncall to get All Attributes for cert has failed %x\n", err );
}
// [6] Finished clean up removed the cert off of the KMIP server
destroyObject( cpClient, certId, P6TRUE );
pGenCerts->release();
pPubKey->release();
pPrvKey->release();
err = cpClient->close();
return err;
}
P6VOID KMIP_Attributes( p6IDataStream* pDataStream )
{
CKmipExample11 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_Attributes( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}