I have an Ethereum private key which consists of 64 bytes. I need to use this key for asymmetric encryption using p256k1 elliptic curve (the signing algorithm used on Ethereum).
This is supported on KMS now in the process of importing my key, I came across the following section in the GCP KMS documentation.
It stipulates the key must be in PKCS#8 format. I have tried a couple of commands using openssl. like the following
openssl pkcs8 -topk8 -in ./private.pem -outform DER -out ./private.key
Here is my private.pem format
-----BEGIN PRIVATE KEY-----
64_CHAR_PRIVATE_KEY_PASTED_HERE
-----END PRIVATE KEY-----
I understand this might not be the correct way to convert as errors happen when I run this command.
unable to load key
4456490668:error:0DFFF07B:asn1 encoding routines:CRYPTO_internal:header too long:/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Library/Caches/com.apple.xbs/Sources/libressl/libressl-75/libressl-2.8/crypto/asn1/asn1_lib.c:152:
4456490668:error:0DFFF066:asn1 encoding routines:CRYPTO_internal:bad object header:/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Library/Caches/com.apple.xbs/Sources/libressl/libressl-75/libressl-2.8/crypto/asn1/tasn_dec.c:1132:
4456490668:error:0DFFF03A:asn1 encoding routines:CRYPTO_internal:nested asn1 error:/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Library/Caches/com.apple.xbs/Sources/libressl/libressl-75/libressl-2.8/crypto/asn1/tasn_dec.c:317:Type=PKCS8_PRIV_KEY_INFO
4456490668:error:09FFF00D:PEM routines:CRYPTO_internal:ASN1 lib:/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Library/Caches/com.apple.xbs/Sources/libressl/libressl-75/libressl-2.8/crypto/pem/pem_pkey.c:143:
My goal remains to be able to import the key into KMS as securely as possible. Thank you in advance for your responses :D
So for anyone, who might deal with something similar. John Hanley pointed me to this link which describes the process of basically converting a Ethereum private key into a EC PEM or DER encoded key file.
A couple of strings need to be added (The linked answer does a good job at explaining it). I was able to then convert EC PEM to PKCS#8 DER format which is what I needed for importing the key to GCP KMS.
I have a complete solution for this, but it uses python instead of openssl.
It has 4 main functions.
1 - Create an Import job in GCP KMS
2 - Convert the Ethereum key to PKCS#8 DER and wrap it with the Import job's wrapping key
3 - Upload it to GCP KMS
4 - Send a GCP-KMS-HSM-signed test transaction on your choice of chain.
I suggest converting the key and wrapping it offline or on a secure boot image like Tails.
Read the code first, and use a throwaway pk to test. I hope this helps.
https://github.com/TMCTG/GCP-KMS-ETH-Private-Key-Import-Sign-Tx
Related
I have created a service account key for a GCP service account using the Terraform google provider. I've set the private key type to "TYPE_PKCS12_FILE", which we require for compatibility with an existing application.
When I was testing this as a PoC, I created the P12 key though the console, and it worked with no issues. Now, I want to handle key generation in our Terraform script, and I cannot get a working P12 key. The actual key resource is created, and it contains a public_key field, which can be base64 decoded to a valid RSA certificate, and a private_key, which is supposedly a P12 file which has been base64 encoded, if I am reading the documentation properly.
I have tried saving the private_key value from Terraform into a file, and base64 decoding it manually. It superficially resembles a known valid P12 bundle, but it is reported as an invalid certificate when I try to import it anywhere.
The object in the state looks like:
"private_key": "MIIJ[...]GoA==",
"private_key_type": "TYPE_PKCS12_FILE",
"public_key": "LS0t[...]LQo=",
"public_key_data": null,
"public_key_type": "TYPE_X509_PEM_FILE",
So, how do I turn the private_key from the Terraform resource into a usable P12 file that can be uploaded to our application?
Answering this question for myself because the specific error received from Terraform needs some explanation. If you try to use TF's built-in base64decode() function on the private key, it gives the error "the result of decoding the provided string is not valid UTF-8".
I originally assumed that this was an error with the cert, because I had thought that I was expecting the private key to be a PEM certificate, but the private_key value actually contains the full P12 bundle.
The basic operation of decoding that string as base64 is correct, but as it turns out, Terraform only supports a limited range of encodings. Decoding to a P12 bundle is not supported in Terraform, because TF parses the output of the base64decode() call to confirm it is valid and it cannot validate the encoding of a P12, since that encoding is not supported.
The solution is to save the output string of the private_key property into a txt file, then use a certificate management tool like openssl or certutil to handle the decoding.
Example:
certutil -decode please-decode-me.txt my-mydecoded-cert.p12
EDIT: Also, note that at minimum, the encode/decode extensions for VS Code do NOT handle that operation correctly for P12 files. I tried that approach thinking that those operations would leave the underlying data of the file unchanged and simply parse it again with a different encoding. This is incorrect, it will change the file data to match the "symbol not found" glyph wherever that glyph is used.
I assume this is well known and/or obvious for security specialists, but I thought I'd explicitly mention it in case it's of use to any other SREs or application devs who don't interact with certificate automation on a regular basis.
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
Is it possible to obtain the private key used to sign the EC2 Identity Document (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html) from within the instance, or in any other way? Assuming that one signs the Identity Document with this key I'm looking for, the OpenSSL command would be:
openssl smime -sign -in document.txt -out document.p7 -outform pem -nodetach -inkey <private_key> -signer AWSpubkey
Where <private_key> is the key I'm looking for.
I'm trying to determine if someone could potentially forge the PKCS7 signature in http://169.254.169.254/latest/dynamic/instance-identity/pkcs7 by creating a document.txt and signing it with the private key.
Thanks.
I'm pretty sure the point of Amazon doing this is that they don't publish the private key so there is no way for someone to forge the signature. Unless there are some breakthroughs in quantum computing soon, you should be confident that the signature hasn't been forged.
There's a different Stack Exchange site where you are more likely to have your question answered by a cryptographic specialist.
I'm trying to import an existing keypair from my computer to use in EC2. But once I click "Yes, Import", the fingerprint Amazon shows doesn't match the fingerprint shown by ssh -lf for the same key. I've verified that they're the same key, tried reimporting the key, etc. The common practice seems to be to use the "Create Key Pair" part instead, but I'd prefer to use my usual SSH keypair. I'm also unable to login using SSH into an instance that's set to use this keypair (I get Permission denied (publickey).).
Has anyone encountered such issues with AWS? Any insights into what the issue might be?
There seems to be an answer in the AWS forums for the fingerprint difference. I'm pasting the content here for posterity:
Hello,
I discussed with my colleagues and looks like it is a limitation from
our end to provide keypair in different format. You'll notice the
different lengths of the Amazon-generated Key Pair and the Import Key
Pair. In the case of an Amazon-generated Key Pair, the Fingerprint is
for the Private Key, while if you use Import Key Pair the fingerprint
is for your public key. Amazon does not retain a copy of the generated
Private Key, but the EC2 command line tools do provide a way to
reproduce the SSH2 MD5 fingerprint:
ec2-fingerprint-key ./testpair1-private.pem
61:26:cc:7d:2a:2c:a4:e9:fb:86:ca:ef:57:d6:68:f8:24:bc:59:cd
This should match what you see in the console for the region in which
you created the key, such as US-West-1 (North California).
Unfortunately the ec2-fingerprint-key command-line tool does not
fingerprint public keys. If you import the public key in another
region such as US-East-1, the web AWS Console will only display the
fingerprint of the public key.
Secondly, the AWS Console should be more clear on exactly what type of
fingerprint it displays, which is the "MD5 public key fingerprint as
specified in section 4 of RFC4716" (also known as SSH2 format) as
mentioned here:
http://docs.amazonwebservices.com/AWSEC2/latest/CommandLineReference/ApiReference-cmd-ImportKeyPair.html
We have already put in a feature request for the web-based AWS Console
to support the more common OpenSSH format. Unfortunately I was not
able to find any user-friendly tools to generate the SSH2/RFC4716
format fingerprint, though I did find that you can import the same
public key in your original region (with a name such as "Test2") and
match the shown fingerprint between regions.
(emphases mine)
As he mentions, I too wasn't able to locate any tool to generate the SSH2/RFC4716 format fingerprint. This at least solves the mystery of mismatching fingerprints (at least if we assume ssh-keygen -lf gives output in the "more common OpenSSH format", please correct me if this assumption is wrong); I'm still getting a Permission denied (publickey) when i try to ssh, but I'll assume it's not an actual key mismatch now and explore other avenues.
Here's an alternative way to verify finger print:
openssl pkcs8 -in my-aws-key.pem -nocrypt -topk8 -outform DER | openssl sha1 -c