Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-16.c
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "p6com.h" // Basic COM definitions
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6keystore.h" // Keystore interface defintions
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "p6genkeys.h"
#include "cconsolestream.h"
P6DECLARE_IID( p6ICom );
P6DECLARE_IID( p6ISafeString );
P6DECLARE_CID( p6EntropySource );
P6DECLARE_IID( p6IEntropySource );
P6DECLARE_CID( p6Random );
P6DECLARE_IID( p6IRandomInit );
P6DECLARE_IID( p6IRandom );
P6DECLARE_IID( p6IRunningIface );
P6DECLARE_CID( p6Cert );
P6DECLARE_IID( p6ICert );
P6DECLARE_IID( p6ICertInit );
P6DECLARE_CID( p6GenKeys );
P6DECLARE_IID( p6IGenKeys );
P6DECLARE_CID( p6Sign );
P6DECLARE_IID( p6ISign );
P6DECLARE_CID( p6SymmetricCrypto );
P6DECLARE_IID( p6ISymmetricCrypto );
P6DECLARE_CID( p6CryptoKey );
P6DECLARE_IID( p6ICryptoKey );
P6DECLARE_IID( p6ICryptoKeyInit );
P6DECLARE_CID( p6Dir );
P6DECLARE_IID( p6IDir );
P6DECLARE_CID( p6UnbufferedFile );
P6DECLARE_IID( p6IUnbufferedFile );
P6DECLARE_IID( p6IKeystoreInit );
P6DECLARE_IID( p6IKeystore );
P6DECLARE_CID( p6Keystore );
P6DECLARE_IID( p6IKeystoreSSL );
P6DECLARE_IID( p6IDataStream );
// Global variables
static p6ISafeString *m_pStr = NULL; // -> P6R generic string library
static p6IRandom *m_pRandom = NULL; // -> crypto related to generate keys
static p6ICryptoKey *m_pKey = NULL; // -> used to sign the contents of the keystore
static p6ISymmetricCrypto *m_pCrypto = NULL; // -> used to encrypt the contents of the keystore
static p6IKeystore *m_pKeystore = NULL; // -> the KMIP client requires that a properly initialzed keystore is setup
static p6IKeystoreInit *m_pStoreInit = NULL; // -> keystore related
//
static P6ERR getSafeString( p6ISafeString **ppStr /*out*/ )
{
P6ERR err = eFail;
if ( P6SUCCEEDED( err = p6GetRuntimeIface( &IID_p6ISafeString, (P6VOID**)ppStr )))
printf( "getSafeString [ OK ]\n" );
else printf( "getSafeString [ FAILED ] %x\n", err );
return err;
}
// Key generation requires an entropy source
static P6ERR getRNG( p6IRandom **ppRandom /*out*/ )
{
P6ERR err = eFail;
p6IEntropySource *pSource = NULL;
p6IRandomInit *pInit = NULL;
if (P6SUCCEEDED( err = p6CreateCryptoInstance( &CID_p6EntropySource, &IID_p6IEntropySource, (P6VOID**)&pSource )))
{
if (P6SUCCEEDED( err = pSource->lpVtbl->initialize( pSource, P6ENTROPY_HIGH )))
{
if (P6SUCCEEDED( err = p6CreateCryptoInstance( &CID_p6Random, &IID_p6IRandomInit, (P6VOID**)&pInit )))
{
if (P6SUCCEEDED( err = pInit->lpVtbl->initialize( pInit, P6RAND_NOFLAGS, pSource ))) {
err = pInit->lpVtbl->queryInterface( pInit, &IID_p6IRandom, (P6VOID**)ppRandom );
}
}
}
}
if (NULL != pSource) pSource->lpVtbl->release( pSource );
if (NULL != pInit ) pInit->lpVtbl->release( pInit );
return err;
}
// The contents of the keystore is protected by a key
P6ERR getIGenKeys( p6IGenKeys **ppGenKeys /*out*/ )
{
P6ERR err = eFail;
p6IGenKeys *pTempKeys = NULL;
*ppGenKeys = NULL;
// Create an instance of the p6IGenKeys interface and then initialize it for use
if (P6SUCCEEDED( err = p6CreateCryptoInstance( &CID_p6GenKeys, &IID_p6IGenKeys, (P6VOID**)&pTempKeys )))
{
if (P6FAILED( err = pTempKeys->lpVtbl->initialize( pTempKeys, P6GENKEY_NOFLAGS, m_pRandom ))) {
pTempKeys->lpVtbl->release( pTempKeys );
}
else {
*ppGenKeys = pTempKeys;
}
}
return err;
}
// Load KMIP credentials into the keystore for the TLS server connection
P6ERR keystoreAddRootCertFromFile( p6IKeystore *pKeystore, const P6WCHAR *pszCertificateFile )
{
P6ERR err = eFail;
P6WCHAR certPath[P6MAXPATH+2] = {0};
p6IKeystoreSSL *pSSLHelp = NULL;
if (NULL == pKeystore || NULL == pszCertificateFile) return eInvalidArg;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, certPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, certPath, P6CNTOF(certPath), (const P6WCHAR*)P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, certPath, P6CNTOF(certPath), pszCertificateFile, NULL ))) return err;
if (P6FAILED( err = pKeystore->lpVtbl->queryInterface( pKeystore, &IID_p6IKeystoreSSL, (P6VOID**)&pSSLHelp ))) return err;
err = pSSLHelp->lpVtbl->importTrustedRootCertFromPEMFile( pSSLHelp, certPath, NULL );
pSSLHelp->lpVtbl->release( pSSLHelp );
return err;
}
// Load KMIP credentials into the keystore for the TLS server connection
P6ERR keystoreAddClientCertFromFile( p6IKeystore *pKeystore, const P6WCHAR *pszHostname, const P6WCHAR *pszPrivateKeyFile, const P6WCHAR *pszCertificateFile )
{
P6ERR err = eFail;
P6WCHAR certPath[P6MAXPATH+2] = {0};
P6WCHAR keyPath[P6MAXPATH+2] = {0};
p6IKeystoreSSL *pSSLHelp = NULL;
if (NULL == pKeystore || NULL == pszHostname || NULL == pszPrivateKeyFile || NULL == pszCertificateFile ) return eInvalidArg;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, keyPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, keyPath, P6CNTOF(keyPath), (const P6WCHAR*)P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, keyPath, P6CNTOF(keyPath), pszPrivateKeyFile, NULL ))) return err;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, certPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, certPath, P6CNTOF(certPath), (const P6WCHAR*)P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, certPath, P6CNTOF(certPath), pszCertificateFile, NULL ))) return err;
if (P6FAILED( err = pKeystore->lpVtbl->queryInterface( pKeystore, &IID_p6IKeystoreSSL, (P6VOID**)&pSSLHelp ))) return err;
err = pSSLHelp->lpVtbl->importCredentialsPEM( pSSLHelp, P6TRUE, pszHostname, keyPath, certPath, NULL, NULL );
pSSLHelp->lpVtbl->release( pSSLHelp );
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.
P6ERR createKeystore( const P6WCHAR* pKeystoreName,
const P6WCHAR* pszHostname,
const P6WCHAR* rootPEM,
const P6WCHAR* certPEM,
const P6WCHAR* privPEM,
p6IKeystoreInit** ppInit, /*out*/
p6IKeystore** ppKeystore /*out*/
)
{
P6ERR err = eFail;
*ppInit = NULL;
*ppKeystore = NULL;
// Create the keystore and fill it with vendor provided SSL certificates
if (P6FAILED( err = p6CreateInstance( NULL, &CID_p6Keystore, &IID_p6IKeystoreInit, (P6VOID**)ppInit ))) return err;
err = (*ppInit)->lpVtbl->queryInterface( (*ppInit), &IID_p6IKeystore, (P6VOID**)ppKeystore );
if (P6FAILED( err = (*ppInit)->lpVtbl->initialize( (*ppInit), P6KEYSTORE_NOFLAGS, m_pCrypto, SH_SHA256, m_pKey )))
{
if (NULL != (*ppKeystore)) (*ppKeystore)->lpVtbl->release( (*ppKeystore) );
(*ppKeystore) = NULL;
(*ppInit)->lpVtbl->release( (*ppInit) );
(*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)->lpVtbl->openSigned( (*ppInit), NULL, pKeystoreName )))
{
if (NULL != (*ppKeystore)) (*ppKeystore)->lpVtbl->release( (*ppKeystore) );
(*ppKeystore) = NULL;
(*ppInit)->lpVtbl->release( (*ppInit) );
(*ppInit) = NULL;
return err;
}
if (P6FAILED( err = keystoreAddRootCertFromFile( (*ppKeystore), rootPEM ))) return err;
if (P6FAILED( err = keystoreAddClientCertFromFile( (*ppKeystore), pszHostname, privPEM, certPEM ))) return err;
return eOk;
}
// Clean up after the example is over
P6VOID freeGlobals()
{
if (NULL != m_pStr ) m_pStr->lpVtbl->release( m_pStr );
if (NULL != m_pRandom ) m_pRandom->lpVtbl->release( m_pRandom );
if (NULL != m_pKey ) m_pKey->lpVtbl->release( m_pKey );
if (NULL != m_pCrypto ) m_pCrypto->lpVtbl->release( m_pCrypto );
if (NULL != m_pStoreInit) m_pStoreInit->lpVtbl->release( m_pStoreInit );
if (NULL != m_pKeystore ) m_pKeystore->lpVtbl->release( m_pKeystore );
}
// Initialize a keystore with the KMIP TLS credentials and write out the key used to encrypt and sign its contents.
// Replace the host name of "fqdn.com", used in this function, to the fully qualified domain name of the server you
// wish to connect to and that the server certificates match.
P6ERR generateKMIPKeystore()
{
P6ERR err = eOk;
P6WCHAR dbPath[P6MAXPATH+20] = {0};
p6IGenKeys *pGenKey = NULL;
p6IDir *pDir = NULL;
// [A] Create one key used to both to encrypt and sign the contents of the keystore
// -> or this example could be modified to create one key for encryption and a separate key for signning, if desired
if (P6FAILED( err = getSafeString( &m_pStr ))) return err;
if (P6FAILED( err = getRNG( &m_pRandom ))) return err;
if (P6FAILED( err = p6CreateInstance( NULL, &CID_p6Dir, &IID_p6IDir, (P6VOID**)&pDir ))) return err;
if (P6FAILED( err = pDir->lpVtbl->initialize( pDir ))) {
pDir->lpVtbl->release( pDir );
return err;
}
if (P6FAILED( err = getIGenKeys( &pGenKey ))) return err;
err = pGenKey->lpVtbl->genSymmetricKey( pGenKey, &m_pKey, 256, P6FALSE );
err = pGenKey->lpVtbl->release( pGenKey );
if (P6FAILED( err )) return err;
// [B] Initialize the keystore with certificates and keys provided to us by the KMIP server vendor
// -> first delete up any previously generated keystore from running this example
dbPath[0] = '\0';
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) {
pDir->lpVtbl->release( pDir );
return err;
}
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, dbPath, P6CNTOF(dbPath), (const P6WCHAR*)P6TEXT("/db/KMIP12_keystore"), NULL ))) {
pDir->lpVtbl->release( pDir );
return err;
}
pDir->lpVtbl->unlink( pDir, dbPath );
dbPath[0] = '\0';
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) {
pDir->lpVtbl->release( pDir );
return err;
}
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, dbPath, P6CNTOF(dbPath), (const P6WCHAR*)P6TEXT("/db/KMIP12_keystore.sig"), NULL ))) {
pDir->lpVtbl->release( pDir );
return err;
}
pDir->lpVtbl->unlink( pDir, dbPath );
// -> please use a domain name if possible and not an IP address to replace "fqdn.com" (required by TLS)
if (P6SUCCEEDED( err )) err = p6CreateCryptoInstance( &CID_p6SymmetricCrypto, &IID_p6ISymmetricCrypto, (P6VOID**)&m_pCrypto );
if (P6SUCCEEDED( err )) err = m_pCrypto->lpVtbl->initialize( m_pCrypto, P6SYM_NOPADDING, CIPHER_AES_CFB );
if (P6SUCCEEDED( err )) err = m_pCrypto->lpVtbl->setKey( m_pCrypto, m_pKey );
if (P6FAILED( err )) return err;
err = createKeystore( (const P6WCHAR*)P6TEXT("KMIP12_keystore"), (const P6WCHAR*)P6TEXT("fqdn.com"), (const P6WCHAR*)P6TEXT("RootCert.pem"), (const P6WCHAR*)P6TEXT("ClientCert.pem"), (const P6WCHAR*)P6TEXT("ClientPrivate.pem"), &m_pStoreInit, &m_pKeystore );
if (P6FAILED( err )) return err;
// [C] Write the key out to a file so when we need to open the keystore to pass it into a KMIP client we will have the key to do so
// -> first delete any previously created key file of the same name
dbPath[0] = '\0';
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) {
pDir->lpVtbl->release( pDir );
return err;
}
if (P6FAILED( err = m_pStr->lpVtbl->wstrlcat( m_pStr, dbPath, P6CNTOF(dbPath), (const P6WCHAR*)P6TEXT("/db/KMIP12_keystorekey.txt"), NULL ))) {
pDir->lpVtbl->release( pDir );
return err;
}
pDir->lpVtbl->unlink( pDir, dbPath );
if (P6FAILED( err = m_pKey->lpVtbl->serializeToFile( m_pKey, dbPath ))) return err;
pDir->lpVtbl->release( pDir );
return err;
}
//
int main(int argc,char *argv[])
{
P6ERR err = eFail;
p6IDataStream *pDataStream = NULL;
// -> datastream is used for logging
if (P6FAILED( err = createStream( &pDataStream ))) {
printf("ERROR: Failed to create a datastream [ %x ]\n", err );
return 1;
}
if ( P6SUCCEEDED( err = p6InitializeLoader( pDataStream, 9, P6SCLF_ALLLOG )))
{
if ( P6FAILED( err = generateKMIPKeystore()))
printf( "Result of keystore generation: failure [ %x ]\n", err );
else printf( "Result of keystore generation: success\n" );
freeGlobals();
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
return 0;
}