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

Hadrien Flammang 1 Reputation point
2021-04-01T12:47:04.863+00:00

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 API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,429 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,544 questions
Windows 10 Security
Windows 10 Security
Windows 10: A Microsoft operating system that runs on personal computers and tablets.Security: The precautions taken to guard against crime, attack, sabotage, espionage, or another threat.
2,767 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Fei Xue - MSFT 1,111 Reputation points
    2021-04-22T01:47:44.59+00:00

    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/

    0 comments No comments