My question is about use case with CNG API and Microsoft providers. I don't write code sample because I ask for your help about the best way to use CNG API in my application compared to CSP API.
I built an application which use symetric keys stored using these steps:
enumerate certificates in "My" store using CertFindCertificateInStore
for each certificate found, asking for private key informations using CertGetCertificateContextProperty
for each private key informations found, storing provider name pwszProvName and container name pwszContainerName
Then, when a key is found, my application performs signature function using private key found using CSP API:
Initialize provider operation using CryptAcquireContext with pwszProvName and pwszContainerName
Compute signature using CSP functions: CryptCreateHash, CryptHashData and CryptSignHash
All is OK with CSP function.
Now I try signature operation using CNG API:
Initialize provider operation using NCryptOpenStorageProvider with pwszProvName
Open algorithm provider using CNG function BCryptOpenAlgorithmProvider fails with STATUS_NOT_FOUND
This error happens when the private key is stored in Microsoft Software Key Storage Provider.
Reading Microsoft documentation I understand that type of provider is KSP provider, and only functions about key management. That's why it fails when I try a primitive function, I need to use a "Primitive Provider".
I found the way to use CNG provider following these setps:
Windows Server 2008: create a certificate template with provider requirement (on "encryption" tab). And the only one provider availabe is "Microsoft Software Key Storage Provider
Windows 7: user ask for key generation, the key is stored in Microsoft KSP.
So here are my questions:
Is it normal I can't perform primitive function with "Microsoft Software Key Storage Provider" ?
If I can't perform primitive functions (signature, encryption, decryption, hash) with Microsoft KSP (which is KSP provider), how can I make my private key stored and managed in a Microsoft Primitive Provider?
My trouble here, is that with CSP API, default Microsoft CSP provider performs signature (and decyrption, encryption, etc) function. But with CNG API, default provider only performs key storage management.
For asymmetric keys, the functionality supported by a CNG Key Storage Provider is comparable to that of a Primitive Provider, apart of course from the the fact that the KSP (Key Storage Provider) allows you to persist and load keys.
In fact, the KSP API calls for doing the crypto operations look much the same as the primitive ones, except the KSP ones start with N and the primitive ones start with B.
For example:
NCryptSignHash for KSP signing
NCryptSecretAgreement for KSP secret agreement
NCryptEncrypt for KSP asymmetric encryption
What is missing from the KSP is symmetric functionality (including hashing), and this may be where the confusion has arisen. Compared to CAPI (CSP/Crypto API), the CNG signing functions are a bit more low-level - you hash the data separately first, and then pass that hash byteblock to NCryptSignHash (no hash object handle like in CAPI).
To re-iterate, as this is a source of confusion for people coming from CAPI, you can hash with any primitive provider, MS_PRIMITIVE_PROVIDER or a third-party provider, and then pass the result to any Key Storage Provider's NCryptSignHash, because it's just bytes of data, it doesn't matter who did the hashing. The NCRYPT_KEY_HANDLE passed to NCryptSignHash determines what KSP is used to do the signing; there is no CNG equivalent of HCRYPTHASH passed to NCryptSignHash.
So, if you want to sign with a KSP, you should hash the message to be signed first with a primitive provider (using BCryptCreateHash/BCryptHashData/BCryptFinishHash), and pass the result to NCryptSignHash.
Related
I am looking to generate a HMAC key and secret value as I want to use it as part of API request signatures. I want to be able to share the secret value and key with a 3rd party so I need access the value in plain text for one time. There would be a HMAC per 3rd party so the number could be large.
Option 1, I could generate this application side but I don't want to store in the dB and I was hoping to use a aws for storage but unsure what the process would be?
Option 2, Preferably I wanted to use AWS to generate the key and secret for HMAC as it can ensure uniqueness etc. I wanted it to provide the key and the secret one time. Looking at the documentation it seems to suggest that the secret value never leaves the HSM. Is my understanding correct or what is the best way to implement this using AWS?
In the AWS SDK https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kms/package-summary.html the KMS has two different types of clients. A regular client and a client builder. What is the purpose of them both? When do you choose one over the other?
I'm trying to implement envelope encryption using KMS. I want to be able to hit the KMS endpoint and encrypt the payload. Which client library should I be using?
There is only one client type: KmsClient.
It can be created in 2 ways:
Using the KmsClientBuilder returned by KmsClient.builder() to modify properties and ultimately do .Build for your customised version of the client - this KmsClientBuilder is an instance of DefaultKmsClientBuilder, which is currently the only class that implements the client builder interface.
Using the KmsClient returned by KmsClient.create(), which is exactly the equivalent (and a shortcut) to new DefaultKmsClientBuilder().build() - this method returns a client set up with the region & credentials already loaded from the respective default provider chain, for applications that don't require further customisation.
This is how the above looks like in code:
final KmsClient defaultKmsClient = KmsClient.create();
final KmsClient alsoDefaultKmsClientButLonger = KmsClient.builder().build();
final KmsClient customisedKmsClient = KmsClient.builder()
.region(...)
.credentialsProvider(...)
.httpClient(...)
.endpointOverride(...)
...
In conclusion, use KmsClient.create() if you do not require a particular configuration, as the default region and creds should be sufficient in most cases.
If not, then customise it via an instance of the builder (which can only be accessed via the KmsClient.builder() method since KmsClientBuilder is an interface).
They are not 'different'.
The builder ultimately is what creates the client.
I am trying to get the details of keys in Microsoft Key Storage Provider.
For this I open the storage provider using the below API call:
NCryptOpenStorageProvider(&prov, MS_KEY_STORAGE_PROVIDER, 0);
Then I call NCryptEnumKeys in a while loop to get the key details.
However I am only able to get one key from the KSP.
During the second iteration of the loop NCryptEnumKeys returns NTE_NO_MORE_ITEMS.
But I have at-least 3 certificates in my local machine store that have Microsoft Key Storage Provider as Provider.
I have confirmed the same through certutil -store my command.
What could possibly be wrong?
After days of analysis and discussions, finally I was able to identify the root cause. It is related to privileges. If I run with Admin privilege, I can extract keys for ECDSA certificate as well from the Local Machine certificate store.
If you do not intend to use Admin privilege, just take the certificate manager or mmc and select the certificate, take All tasks > Manage Private Keys give privileges as required.
We have RSA key pairs generated on on-prem and plan to sync them to GCP-KMS. There is an yearly key rotation policy which would be done on on-prem and new key_versions would be synced to KMS. My concern is with the KMS API.
Problem: The API always asks for the 'key_version' as an argument to encrypt/decrypt a file.
Desired behaviour: During decryption, is it not possible that the KMS sees the certificate thumbprint and returns the appropriate key version to decrypt a given encrypted file? e.g. a DEK wrapped with the RSA_public when supplied to KMS gets decrypted by the RSA_Private(or KEK) of the correct version.
If yes, is there any documentation that elaborates on this use case?
According to the documentation, you can achieve that with symmetric signature (no key version specified), but you can't with the asymetricDecrypt (key version is required in the URL path of the API)
I am wondering how signing key and encryption key of a gcp shielded VM instance can be used? I am thinking of using the encryption key (ekPub) to encrypt an arbitrary blob of data and be sure only the the associated gcp instance can decrypt it. But I am not sure how to ask vTPM to decrypt the encrypted data?
Shielded VM and Confidential computing are 2 different features on Google Cloud.
Shielded VM check at startup is any component has been tampered and can lead to a dataleak (through malware/backdoor)
Confidential Computing automatically create a cryptographic key at startup. This key is used to cipher all the data in memory. The data are only decipher inside the CPU, while processing.
When the data are written on disk, the data are get from encrypted memory, decipher in the CPU and written in plain text on the disk, which is automatically encrypted (but by another process, not by the CPU)
You have nothing to do, it's automatic!
Background and Definitions
The endorsement key (EK) is a key on TPM2.0 that is used for attestation. The EK typically comes with a certificate signed by the manufacturer (note, not available on GCE instances) stating that the TPM is a genuine TPM[1]. However, the TCG had privacy concerns around attestation with one signing key. So, they decided to make the endorsement key an encryption key. The ActivateCredential flow[2] is typically used to trust a new signing key. This sidesteps the privacy concerns by allowing the use of a privacy CA to create an AK cert endorsing that the EK and AK are on the same TPM. GCE creates an AK by default that allows users to avoid this process by using the get-shielded-identity API.
Decryption
There are a few ways to encrypt data using the endorsement key.
Since the EK is restricted [3], you have to jump through some hoops to easily use it. Restricted here means the key cannot be used for general decryption. Rather, they are used for storage/wrapping TPM objects. A storage key is typically a restricted decryption key.
Here are some ways you can get around this problem:
1. Use TPM2_Import and TPM2_Unseal (Part 3 of the TPM spec [3])
TPM2_Import has the TPM decrypt an external blob (public and private) with a storage key. Then, the user can load that object under the storage key and use. TPM2_Unseal returns the secret within the sealed blob.
The flow is roughly the following:
A remote entity creates a blob containing a private part and a corresponding public part. The private part contains the original secret to decrypt.
Remote entity uses an EK to wrap a seed for a known KDF that derives a symmetric and HMAC key.
Use seed and KDF derived key to encrypt the private part. This is the "duplicate" blob.
Send duplicate, public, and encrypted seed to the VM.
TPM2_Import on duplicate, public, and encrypted seed with handle for the EK.
TPM2_Load on public and outPrivate (decrypted private) from TPM2_Import.
TPM2_Unseal on the object handle, secret will be in outData.
This is all done for you in https://github.com/google/go-tpm-tools. All you need is to pass in the PEM, decode it, and parse it into a public key.
Then you can use server.CreateImportBlob.
Send the output blob to the VM.
On the client side, use EndorsementKeyRSA (or EndorsementKeyECC) to create a go-tpm-tools key.
Use key.Import with the blob.
Specifically, see https://pkg.go.dev/github.com/google/go-tpm-tools/server#CreateImportBlob and https://pkg.go.dev/github.com/google/go-tpm-tools/tpm2tools#Key.Import
Note package tpm2tools was recently renamed client, but this is not yet a public release.
2. Use TPM2_ActivateCredential (TPM spec, Part 3)
ActivateCredential allows you to verify a key is co-resident with another. Again, while this is typically used for attestation, you can use this to create an asymmetric key pair for general decryption.
In this scenario, the VM would generate an unrestricted decryption key on the TPM.
The server then generates the ActivateCredential challenge with the known templates of the EK and the decryption key.
If the decryption key's properties match, the TPM can fetch the challenge secret and return it to the server.
The server, upon receiving the successful response, can rely on the corresponding public key generated in the challenge and encrypt data to the VM.
One thing you may notice is, if you only want to decrypt a few times, you can just use the challenge secret as the plaintext.
You would need to stitch this together using https://pkg.go.dev/github.com/google/go-tpm/tpm2/credactivation and
https://pkg.go.dev/github.com/google/go-tpm/tpm2#ActivateCredential, as I don't currently know of tooling that supports this out of the box.
References
[1] EK specification: https://trustedcomputinggroup.org/resource/tcg-ek-credential-profile-for-tpm-family-2-0/
[2] Credential activation: https://github.com/google/go-attestation/blob/master/docs/credential-activation.md
[3] TPM spec: https://trustedcomputinggroup.org/resource/tpm-library-specification