question

HadrienFlammang-8574 avatar image
0 Votes"
HadrienFlammang-8574 asked FeiXue-MSFT answered

CNG/BCrypt/NCrypt Import a ephemeral AES key wrapped with a persistent RSA key

I will write some kind of security client/server pair:
- The server part provides wrapped AES keys.
- The client part, based on CNG win32 API ( ncrypt.h + bcrypt.h ), will request the server to send some keys (when needed).

Here is my plan:

  1. On the client side:
    • I create a persistent RSA key ( NCryptCreatePersistedKey ) once for all

    • I export the public part of this key ( NCryptExportKey + BCRYPT_RSAPUBLIC_BLOB )

    • I send this public key to the server (format TBD)

  2. On the server side:
    • I generate a random AES key

    • I wrap it using the received public key

    • I send back the wrapped AES key to the client (format TBD)

  3. Back to the client side:
    • extract the wrapped key and serialize it into a BCrypt compatible blob.
      ->how to build a BCrypt blob?

    • import the wrapped AES key as ephemeral ( BCryptImportKey )
      -> how to import an ephemeral symmetric key wrapped with a persistant asymmetric key?

    • now I can use this AES key to cipher/decipher data...


I wrote a code that should work, but it still remain some grey areas...
Here is the code:

void get_wrapped_aes_key ( PUCHAR rsa_pub_key_blob , ULONG rsa_pub_key_blob_size ,   
                     PUCHAR wrapped_aes_key_blob , PULONG wrapped_aes_key_blob_size )
    {
    // send the rsa public key (from buffer rsa_pub_key_blob) to the secu server
    // secu server generates a random AES key and wraps it with the rsa public key
    // receive the wrapped AES key and serialize it into a BCrypt blob
    // mem copy this blob into the buffer wrapped_aes_key_blob)
    }

void create_persistent_key ( const wchar_t * key_name )
    {
    const wchar_t      * storage          = MS_PLATFORM_CRYPTO_PROVIDER ;
    NCRYPT_PROV_HANDLE   storage_provider = NULL ;
    NCRYPT_KEY_HANDLE    rsa_key          = NULL ;

    NCryptOpenStorageProvider( &storage_provider,storage,0 ) ;
    NCryptCreatePersistedKey( storage_provider,&rsa_key,BCRYPT_RSA_ALGORITHM,key_name,0,NCRYPT_OVERWRITE_KEY_FLAG ) ;
    NCryptFinalizeKey( rsa_key,0 ) ;
    NCryptFreeObject( rsa_key ) ;
    NCryptFreeObject( storage_provider ) ;
    }

BCRYPT_KEY_HANDLE import_key_using_persistent_key ( const wchar_t * key_name )
    {
    const wchar_t      * storage          = MS_PLATFORM_CRYPTO_PROVIDER ;
    const wchar_t      * blob_type        = BCRYPT_RSAPUBLIC_BLOB ;
    BCRYPT_ALG_HANDLE    algo_provider    = NULL;
    NCRYPT_PROV_HANDLE   storage_provider = NULL;
    NCRYPT_KEY_HANDLE    rsa_key          = NULL;
    BCRYPT_KEY_HANDLE    bcrypt_rsa_key   = NULL;
    BCRYPT_KEY_HANDLE    imported_aes_key = NULL;
    BYTE                 rsa_pub_key_blob [500] ;
    DWORD                rsa_pub_key_blob_size = sizeof(rsa_pub_key_blob) ;
    BYTE                 wrapped_aes_key_blob [500] ;
    DWORD                wrapped_aes_key_blob_size = sizeof(wrapped_aes_key_blob) ;

    //------------------- retrieve the persistent key
    NCryptOpenStorageProvider( &storage_provider,storage,0 ) ;
    NCryptOpenKey( storage_provider,&rsa_key,key_name,0,0 ) ;
    NCryptExportKey( rsa_key,NULL,blob_type,NULL,rsa_pub_key_blob,rsa_pub_key_blob_size,&rsa_pub_key_blob_size,0 ) ;

    //------------------- get a symmetric key from the security server
    get_wrapped_aes_key( rsa_pub_key_blob,rsa_pub_key_blob_size,wrapped_aes_key_blob,&wrapped_aes_key_blob_size ) ;

    // mysterious conversion from  rsa_key  to   bcrypt_rsa_key 

    //------------------- import the security server
    BCryptOpenAlgorithmProvider( &algo_provider,BCRYPT_RSA_ALGORITHM,NULL,0 ) ;
    BCryptImportKey( algo_provider,bcrypt_rsa_key,BCRYPT_KEY_DATA_BLOB,&imported_aes_key,NULL,0,wrapped_aes_key_blob,wrapped_aes_key_blob_size,0 ) ;
    BCryptCloseAlgorithmProvider( algo_provider,0 ) ;
    NCryptFreeObject( storage_provider ) ;
    NCryptFreeObject( rsa_key ) ;
    return imported_aes_key ;
    }


Since the server side is not written, the function get_wrapped_aes_key is still empty, but, by the way:
how to build a blob containing a wrapped AES key ?

The function create_persistent_key works fine, but I have a question: how to chose the RSA size ?
(It produces 2048 bits keys, which what I want, but can I change it ?)

In the function import_key_using_persistent_key : BCryptImportKey expects a BCRYPT_KEY_HANDLE as wrapping key, but my persistant key is a NCRYPT_KEY_HANDLE.
I can't figure out what to do...

Thanks in advance

windows-apic++windows-10-security
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@HadrienFlammang-8574, I am looking into this issue, any update will post here.

0 Votes 0 ·

1 Answer

FeiXue-MSFT avatar image
0 Votes"
FeiXue-MSFT answered

You can refer to the below to accomplish the goal.

 CLIENT
 // Create RSA Key Pair
 NCryptOpenStorageProvider
 NCryptCreatePersistedKey("MyRSAKeyName") -> n_hRSAKey
 NCryptFinalizeKey
     
 // Export RSA Public Key blob
 NCryptExportKey (n_hRSAKey, BCRYPT_RSAPUBLIC_BLOB ) -> n_pbRSAPublicKeyBlob
    
 SERVER
 // Import RSA Public Key Blob
 NCryptOpenStorageProvider
 NCryptImportKey(n_pbRSAPublicKeyBlob) -> n_hRSAPublicKey
     
 // Generate Random AES Key
 BCryptOpenAlgorithmProvider(BCRYPT_AES_ALGORITHM)
 BCryptGenerateSymmetricKey(random AES key bytes) -> b_hAESKey
 BCryptExportKey(b_hAESKey, BCRYPT_KEY_DATA_BLOB) -> b_pbAESKeyBlob, b_dwAESKeyBlobSize
     
 // Create NCRYPT_CIPHER_KEY_BLOB
 NCRYPT_KEY_BLOB_HEADER *pbCipherBlob;
 DWORD dwCipherBlobSize;
 LPCWSTR szwAESAlg[] = { NCRYPT_AES_ALGORITHM };
 PBYTE pbPointer;
     
 dwCipherBlobSize = sizeof(NCRYPT_KEY_BLOB_HEADER) + b_dwAESKeyBlobSize + ((wcslen(szwAESAlg[0]) + 1) * sizeof(WCHAR));
 pbCipherBlob = // Allocate dwCipherBlobSize bytes
 pbCipherBlob->cbSize = sizeof(NCRYPT_KEY_BLOB_HEADER);
 pbCipherBlob->dwMagic = NCRYPT_CIPHER_KEY_BLOB_MAGIC;
 pbCipherBlob->cbAlgName = (wcslen(szwAESAlg[0]) + 1 ) * sizeof(WCHAR);
 pbCipherBlob->cbKeyData = b_dwAESKeyBlobSize;
     
 // Immediately follow with Algorithm Name and Key Blob
 pbPointer = (PBYTE)pbCipherBlob + pbCipherBlob->cbSize;
 CopyMemory(pbPointer, szwAESAlg[0], pbCipherBlob->cbAlgName);
 pbPointer += pbCipherBlob->cbAlgName;
 CopyMemory(pbPointer, b_pbAESKeyBlob, b_dwAESKeyBlobSize);
     
 // Import  NCRYPT_CIPHER_KEY_BLOB
 NCryptImportKey(NCRYPT_CIPHER_KEY_BLOB, pbCipherBlob, dwCipherBlobSize) -> n_hAESKey
     
 // Export (Encrypt) the AES key with RSA key
 NCryptExportKey(n_hAESKey, n_hRSAPublicKey) -> n_pbEncryptedAESKeyBlob, n_dwEncryptedAESKeyBlobSize
     
 CLIENT
 NCryptOpenStorageProvider
 NCryptOpenKey("MyRSAKeyName") -> n_hRSAKey
     
 // Import and decrypt key blob using RSA private key)
 NCryptImportKey(n_hRSAKey, NCRYPT_OPAQUETRANSPORT_BLOB, n_pbEncryptedAESKeyBlob, n_dwEncryptedAESKeyBlobSize) -> n_hAESKey
     
 // Export AES key to NCRYPT_CIPHER_KEY_BLOB)
 NCryptExportKey(n_hAESKey, NCRYPT_CIPHER_KEY_BLOB) -> n_pbCipherBlob
     
 // Import BCRYPT_KEY_DATA_BLOB contained in NCRYPT_CIPHER_KEY_BLOB to get BCryptHandle
 b_pbAESKeyBlob = n_pbCipherBlob + n_pbCipherBlob->cbSize + n_ pbCipherBlob->cbAlgName;
 BCryptOpenAlgorithmProvider(BCRYPT_AES_ALGORITHM)
 BCryptImportKey(BCRYPT_KEY_DATA_BLOB, b_pbAESKeyBlob, n_pbCipherBlob->cbKeyData) -> b_hAESKey

If you still have problem or concern, a support ticket is recommend so that our engineer could work closely with you to resolve this issue as soon as possible. You can open links below and navigate to 'Contact US' to open the ticket:

https://developer.microsoft.com/en-us/windows/support/

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.