Does Azure Key Vault support ECC(Elliptic Curve Cryptography)?
It looks like Elliptic Curve is now supported in Azure.
There is a tutorial online in F# with someone using it https://tomislav.tech/2018-01-31-ethereum-keyvault-generating-keys/
Or there is also some information in the documentation now.
It is not marked as complete here https://feedback.azure.com/forums/216840-security-and-compliance/suggestions/10877748-ecc-support-for-azure-key-vault but the latest comment says it is now supported.
Yes. Here's how I imported an EC key using Microsoft.Azure.KeyVault.WebKey in .NET.
Construct JSON web key and azure KeyBundle:
JsonWebKey jwk = new JsonWebKey();
jwk.CurveName = JsonWebKeyCurveName.P256;
jwk.Kty = "EC";
jwk.D = ec_d; // ec_d is a 32-byte byte array representing the private key
jwk.X = ec_x; // ec_x is a 32-byte byte array representing the x coordinate
jwk.Y = ec_y; // ec_x is a 32-byte byte array representing the y coordinate
KeyBundle keyBundle = new KeyBundle
{
Key = jwk
};
Construct request:
string keyName = "testECkey1";
await kvClient.ImportKeyAsync(vault.Properties.VaultUri, keyName, keyBundle);
You'll need to have authenticated to Azure first. I used a lot of the example code here: https://github.com/Azure-Samples/key-vault-dotnet-authentication/blob/master/KeyVaultAuthSample.cs
Once I figured out the right jwk parameters I got the error "EC key is not valid - bad crypto service output". Turns out I was pushing my bytes up little endian when they needed to be big endian, so I just used Array.Reverse.
Also, I had to extract the X and Y coords from the EC public key manually. This was helpful: https://davidederosa.com/basic-blockchain-programming/elliptic-curve-keys/
Good question. I dont believe EEC is supported yet, Christos Matskas (MS Azure Dev) blog from March 17th 2017 states:
"The service currently supports symmetric RSA keys but there is already scope for adding asymmetric and elliptic curve key support in future releases. The keys can be generated either by using the service or you can choose to import existing keys. For keys generated using the service, there are 2 types of supported algorithms:
Simple RSA
RSA-HSM
In addition USER VOICE has at least two user requests asking for this feature to be implemented, you can add your feedback request here too.
Ref:
https://blogs.technet.microsoft.com/uktechnet/2017/03/17/application-security-with-azure-key-vault/
https://feedback.azure.com/forums/216840-security-and-compliance/suggestions/10877748-ecc-support-for-azure-key-vault
https://feedback.azure.com/forums/586780-blockchain/suggestions/17940250-support-ecc-signing-within-key-vault
Related
I need to encrypt some message before sending it as request body in an API. I have to do this in Python and the library I used is Pycryptodome. From hereon, when I use the term API, I am referring to the API endpoint provided by the vendor and not the Pycryptodome API. The receiver(API vendor) shared the certificate (RSA 2048 bits) which I need to use to encrypt this message. This certificate is in binary format (DER) and has .cer as the extension. I need to import this using Pycryptodome and from the docs (https://pycryptodome.readthedocs.io/en/latest/src/public_key/rsa.html), I find that the import_key method accepts X.509 certificates in either binary or PEM encoding. The API documentation says I need to use RSA/NONE/OAEPWithSHA1AndMGF1Padding for encryption. This is my code.
def encrypt_message(message):
with open('/home/krishna/work/.certificates/random_certificate.cer', 'rb') as _file:
key_contents = _file.read()
recipient_key = RSA.import_key(key_contents)
cipher_rsa = PKCS1_OAEP.new(recipient_key)
return base64.b64encode(cipher_rsa.encrypt(message.encode())).decode()
I read the PKCS1_OAEP.py file, the default mask generation function is MGF1, default hash algorithm is SHA1. Hence, I did not specify these parameters in the new function in the above code. Also, I need to send this encrypted message as a base64 string (UTF-8). After reading the API documentation and the Pycryptodome documentation, I felt this code is sufficient for encryption. Also, this code is somewhat standard and is almost the same as the one in the Pycryptodome documentation. However, I get a decryption error and the vendor tells me this is due to "BAD PADDING EXCEPTION". This should not happen since I used OAEP for padding and the vendor confirmed they are using OAEP for padding as well. This is very hard for me to debug as I do not possess the vendor's private key.
So I tried generating my own pair of private and public keys (RSA-2048) and checked if I am able to decrypt my message. And duh, I am able to decrypt my message. Here is the code for encryption and decryption using my own keys.
def encrypt_message(message):
with open('my_pub.pem', 'rb') as _file:
pub = _file.read()
recipient_key = RSA.importKey(pub)
cipher_rsa = PKCS1_OAEP.new(recipient_key)
return base64.b64encode(cipher_rsa.encrypt(message.encode())).decode()
def decrypt_message(gibberish):
with open('my_priv.pem', 'rb') as _file:
priv = _file.read()
pvt_key = RSA.importKey(priv)
cipher_rsa = PKCS1_OAEP.new(pvt_key)
return cipher_rsa.decrypt(base64.b64decode(gibberish.encode())).decode()
So where I am going wrong? They key difference between this test and the actual API call is that my keys are generated in PEM format and have .pem as the extension. In the case of the API call, I have to use a .cer certificate in DER format. But I am assuming Pycryptodome is handling this for me (from the docs).
I have a few more questions regarding this.
What is NONE in RSA/NONE/OAEPWithSHA1AndMGF1Padding?
What is the difference between import_key() and importKey() in Pycryptodome? There are 2 methods and it does not look like importKey() has some documentation to it.
Can padding exceptions occur while using OAEP? From what I read from the internet, OAEP is the best method out there, not only in terms of adding randomness during encryption, but I also read that padding exceptions are very rare when this is used.
I really need to know what is going wrong here as I am dead clueless. Any suggestions or help will be appreciated.
I can successfully create an Hmac via NodeJS using the following code:
(slightly altered example from : https://nodejs.org/api/crypto.html#cryptocreatehmacalgorithm-key-options)
Crypto.createHmac('sha256', Crypto.randomBytes(16))
.update('I love cupcakes')
.digest('hex');
That results in a value like the following (hex-based string Hmac signature):
fb2937ca821264812d511d68ae06a643915931375633173ba64af9425f2ffd53
How do I use that signature to verify that the data was not altered? (using NodeJS, of course).
My Assumption
I'm assuming there is a method call where you supply the data and the signature and you get a boolean that tells you if the data was altered or not -- or something similar.
Another Solution?
Oh, wait, as I was writing that I started thinking...
Do I need to store the original random bytes I generated (Crypto.randomBytes(16)) and pass them to the receiver so they can just generate the HMac again and verify that the result is the same (fb2937ca821264812d511d68ae06a643915931375633173ba64af9425f2ffd53)?
If that is true that would be odd, because the parameter for Crypto.randomBytes(16) is named secret (in the official example)*. Seems like that needs to be kept secret??
Please let me know if there is a way to verify the signature on the receiving side & how I do that.
Official Documentation : A Bit Confusing
Here's the function as it is defined in the official docs:
crypto.createHmac(algorithm, key[, options])
In the function definition, you can see the second param is named key.
However, in the example they refer to it as secret
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
Just posting the answer so if anyone in future sees this they will be able to have the definitive answer.
As the commentor (Topaco) pointed out, the simple answer is that:
The receiver who want wants to validate the Hmac simply needs to use the same key value & data and apply it to the method and retrieve the hash value.
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
The original Hmac-creating party must provide three things for the verifying party:
data : (could be encrypted data from AES256, for example)
key : original key passed into the createHmac() method -- note: this item is called secret in the sample code by NodeJS (above).
hash :the (clearText) hash which the original creator generated when calling the createHmac() method.
With those three things the verifying party can now call the createHmac() method and determine if the hash they get matches the hash that the original hmac-creating party generated.
Doing this validates that the Data which was sent has not been corrupted or altered.
Additional Note On Key (secret)
I've come back after thinking about the Hmac a bit more.
It is required that both parties know the key (aka secret) but it does not mean that it should be exposed to others.
This must be kept secret (as the code implies) because if a nefarious type knew the value and could alter it, then they could also alter the data and generate a new key (secret) and pass it along as if the original creator sent it along (MITM - man in the middle attack).
So, the point here is that yes, both parties have to know the key (secret) value, but it should not be shared where it might be discovered by nefarious types.
Instead, it will have to be agreed upon or based upon a secret password, etc.
So with lots of different services around now, Google APIs, Twitter API, Facebook API, etc etc.
Each service has an API key, like:
AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q
All the keys vary in length and the characters they contain, I'm wondering what the best approach is for generating an API key?
I'm not asking for a specific language, just the general approach to creating keys, should they be an encryption of details of the users app, or a hash, or a hash of a random string, etc. Should we worry about hash algorithm (MSD, SHA1, bcrypt) etc?
Edit:
I've spoke to a few friends (email/twitter) and they recommended just using a GUID with the dashes stripped.
This seems a little hacky to me though, hoping to get some more ideas.
Use a random number generator designed for cryptography. Then base-64 encode the number.
This is a C# example:
var key = new byte[32];
using (var generator = RandomNumberGenerator.Create())
generator.GetBytes(key);
string apiKey = Convert.ToBase64String(key);
API keys need to have the properties that they:
uniquely identify an authorized API user -- the "key" part of "API key"
authenticate that user -- cannot be guessed/forged
can be revoked if a user misbehaves -- typically they key into a database that can have a record deleted.
Typically you will have thousands or millions of API keys not billions, so they do not need to:
Reliably store information about the API user because that can be stored in your database.
As such, one way to generate an API key is to take two pieces of information:
a serial number to guarantee uniqueness
enough random bits to pad out the key
and sign them using a private secret.
The counter guarantees that they uniquely identify the user, and the signing prevents forgery. Revocability requires checking that the key is still valid in the database before doing anything that requires API-key authorization.
A good GUID generator is a pretty good approximation of an incremented counter if you need to generate keys from multiple data centers or don't have otherwise a good distributed way to assign serial numbers.
or a hash of a random string
Hashing doesn't prevent forgery. Signing is what guarantees that the key came from you.
Update, in Chrome's console and Node.js, you can issue:
crypto.randomUUID()
Example output:
'4f9d5fe0-a964-4f11-af99-6c40de98af77'
Original answer (stronger):
You could try your web browser console by opening a new tab, hitting CTRL + SHIFT + i on Chrome, and then entering the following immediately invoked function expression (IIFE):
(async function (){
let k = await window.crypto.subtle.generateKey(
{name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]);
const jwk = await crypto.subtle.exportKey("jwk", k)
console.log(jwk.k)
})()
Example output:
gv4Gp1OeZhF5eBNU7vDjDL-yqZ6vrCfdCzF7HGVMiCs
References:
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey
I'll confess that I mainly wrote this for myself for future reference...
I use UUIDs, formatted in lower case without dashes.
Generation is easy since most languages have it built in.
API keys can be compromised, in which case a user may want to cancel their API key and generate a new one, so your key generation method must be able to satisfy this requirement.
If you want an API key with only alphanumeric characters, you can use a variant of the base64-random approach, only using a base-62 encoding instead. The base-62 encoder is based on this.
public static string CreateApiKey()
{
var bytes = new byte[256 / 8];
using (var random = RandomNumberGenerator.Create())
random.GetBytes(bytes);
return ToBase62String(bytes);
}
static string ToBase62String(byte[] toConvert)
{
const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
BigInteger dividend = new BigInteger(toConvert);
var builder = new StringBuilder();
while (dividend != 0) {
dividend = BigInteger.DivRem(dividend, alphabet.Length, out BigInteger remainder);
builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
}
return builder.ToString();
}
An API key should be some random value. Random enough that it can't be predicted. It should not contain any details of the user or account that it's for. Using UUIDs is a good idea, if you're certain that the IDs created are random.
Earlier versions of Windows produced predictable GUIDs, for example, but this is an old story.
I am having great difficulty implementing some encryption for a node.js project. I am using node-rsa which in turn uses the built in node crypto module.
My code appears to encrypt the data just fine and I can decrypt it. Unfortunately, the system I am sending the data to cannot. Having hit a bit of a wall I've tried decryption some data generated the other end. That also fails.
What is unusual in this case is that the data has to be encrypted using the PRIVATE key and decrypted using the PUBLIC key. As stupid as this is, I had no say in the decision to use this mechanism and it is already in wide use in a UK pubic transport ticketing system. So there is no possibility of doing anything different.
The specification [only] says:
The payload data (Appendix A) is encrypted using the TIS private key,
using 1024Bit RSA PKCS#1v1.5. (This is a freely available public
standard). This protects a payload of up to 116 bytes, or 928Bits,
creating a 128 byte or 1024Bit encrypted output. The RSA encryption
algorithm adds a header of length 12 bytes to the payload before
encryption to improve security. For decryption purposes, this header
should be removed to get the correct payload.
The format of this header is: 0x00 [10 non-zero bytes which are
padding] 0x00 [content]. This is often done automatically by
encryption/decryption software.
Here is an example public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDriuvWAKiTKFgmjHjHYjWK4IZ6
W1UOdrntxFAGT1szZlbKJfWGeBOPSk6MnW6AeX54zEta74Dnoj4MBT+MlekVQ2zp
G3qIZpp4DR+kF6eVRlguUg9l/Fgr9ZrFgkumCKIx/ccc6rvZcNvPnOVdPCBVoMRr
3X59WlLmH4d+RAivAQIDAQAB
-----END PUBLIC KEY-----
I've derived this from the Modules / Exponent listed below... I think it is correct...?
Which is [supposedly] valid to decode this encrypted data:
1f876b22ea8af4341c4198347aabf1b1ba40649ad6918bbe26ebfcf4633786a2669ee499d796cf438a76ff0d4f6efc43f605994330e6fb88c8117eea6aad1a12c2525940f7b9946b3f6cbd674e2740070400ae6f2cf1ed6d36f610822a23d5a8a4717ecf671bf0530200f75a269951020be9f69a992d423f55eb6fefe6caa449
I get the following error:
decryptData error Error: Error during decryption (probably incorrect key). Original error: Error: error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01
I hate to say it, but I am now completely stumped. If anyone is able to decrypt that data using the supplied key and can explain how I would be very grateful.
Just to save the usual response to this sort of question that I see whenever anyone else has asked a similar question in the past. Please do not comment if all you want to say is the process is wrong. That is not helpful in any way. Just to confirm, whilst encryption with the private key is unusual, it is not impossible....It is supported by the RSA specification.
There are several ticket retailers who are encrypting their data this way and several scanning equipment manufacturers who are producing equipment that decrypts in this way. It's out there, it's working.
The only issue seems to be that there is some small mismatch between the encrypted data I am seeing from node-rsa (node.js crypto library) and the example data I have been given.
This is the original format the keys were given to me.....
Appendix D - Example Security Keys for Tickets
All example tickets are encrypted using the following fixed 1024Bit RSA key
(Never use these keys in production, you must generate your own key pair for this purpose as this private key cannot be considered secure and is for testing only)
Modulus = 16540368155253773454125654483794538435594869143015457768150677916704644349481001621163 93533906833356026635527060528398017212524170789714748325265707476885395169247950736258 76338137535298120673942399072260833758038152595105311260002268650951968659366409471762 707986435753824060971148933772149012757770173722369
= HEX [00eb8aeb d600a893 2858268c 78c76235 8ae0867a 5b550e76 b9edc450 064f5b33 6656ca25 f5867813 8f4a4e8c 9d6e8079 7e78cc4b 5aef80e7 a23e0c05 3f8c95e9 15436ce9 1b7a8866 9a780d1f a417a795 46582e52 0f65fc58 2bf59ac5 824ba608 a231fdc7 1ceabbd9 70dbcf9c e55d3c20 55a0c46b dd7e7d5a 52e61f87 7e4408af 01]
Public Exponent = 65537
= HEX [010001]
Private Exponent = 23262061627321059848127951748956201956280865601289878122136319690902346303487555417896 45421978315004119428666694674801184773156427997741861134011936129888508204465790901818 10860126020371138225614906842567831455763314175530505369901364910691824152015094858350 72189773434923487177372245370361799421231555208773
= HEX [21205394 b0590501 3a8c895a ff2797c2 255ba45f adf1afce ec5a9caa 96848c11 0b89b896 f44774f0 c5119103 1f246071 e209515b c3ad4c66 6bf582d3 72312f2b 7250fe61 f6abed7f e219c08d c3985ae1 3f6b6db2 0e3c040b df7a817d 14a5a6f1 20d94047 08512132 aca00baa 29805440 4ad5dec2 1bd544bb 8938c74b 2904e645]
Just to confirm... I was given incorrect data.
So with lots of different services around now, Google APIs, Twitter API, Facebook API, etc etc.
Each service has an API key, like:
AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q
All the keys vary in length and the characters they contain, I'm wondering what the best approach is for generating an API key?
I'm not asking for a specific language, just the general approach to creating keys, should they be an encryption of details of the users app, or a hash, or a hash of a random string, etc. Should we worry about hash algorithm (MSD, SHA1, bcrypt) etc?
Edit:
I've spoke to a few friends (email/twitter) and they recommended just using a GUID with the dashes stripped.
This seems a little hacky to me though, hoping to get some more ideas.
Use a random number generator designed for cryptography. Then base-64 encode the number.
This is a C# example:
var key = new byte[32];
using (var generator = RandomNumberGenerator.Create())
generator.GetBytes(key);
string apiKey = Convert.ToBase64String(key);
API keys need to have the properties that they:
uniquely identify an authorized API user -- the "key" part of "API key"
authenticate that user -- cannot be guessed/forged
can be revoked if a user misbehaves -- typically they key into a database that can have a record deleted.
Typically you will have thousands or millions of API keys not billions, so they do not need to:
Reliably store information about the API user because that can be stored in your database.
As such, one way to generate an API key is to take two pieces of information:
a serial number to guarantee uniqueness
enough random bits to pad out the key
and sign them using a private secret.
The counter guarantees that they uniquely identify the user, and the signing prevents forgery. Revocability requires checking that the key is still valid in the database before doing anything that requires API-key authorization.
A good GUID generator is a pretty good approximation of an incremented counter if you need to generate keys from multiple data centers or don't have otherwise a good distributed way to assign serial numbers.
or a hash of a random string
Hashing doesn't prevent forgery. Signing is what guarantees that the key came from you.
Update, in Chrome's console and Node.js, you can issue:
crypto.randomUUID()
Example output:
'4f9d5fe0-a964-4f11-af99-6c40de98af77'
Original answer (stronger):
You could try your web browser console by opening a new tab, hitting CTRL + SHIFT + i on Chrome, and then entering the following immediately invoked function expression (IIFE):
(async function (){
let k = await window.crypto.subtle.generateKey(
{name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]);
const jwk = await crypto.subtle.exportKey("jwk", k)
console.log(jwk.k)
})()
Example output:
gv4Gp1OeZhF5eBNU7vDjDL-yqZ6vrCfdCzF7HGVMiCs
References:
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey
I'll confess that I mainly wrote this for myself for future reference...
I use UUIDs, formatted in lower case without dashes.
Generation is easy since most languages have it built in.
API keys can be compromised, in which case a user may want to cancel their API key and generate a new one, so your key generation method must be able to satisfy this requirement.
If you want an API key with only alphanumeric characters, you can use a variant of the base64-random approach, only using a base-62 encoding instead. The base-62 encoder is based on this.
public static string CreateApiKey()
{
var bytes = new byte[256 / 8];
using (var random = RandomNumberGenerator.Create())
random.GetBytes(bytes);
return ToBase62String(bytes);
}
static string ToBase62String(byte[] toConvert)
{
const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
BigInteger dividend = new BigInteger(toConvert);
var builder = new StringBuilder();
while (dividend != 0) {
dividend = BigInteger.DivRem(dividend, alphabet.Length, out BigInteger remainder);
builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
}
return builder.ToString();
}
An API key should be some random value. Random enough that it can't be predicted. It should not contain any details of the user or account that it's for. Using UUIDs is a good idea, if you're certain that the IDs created are random.
Earlier versions of Windows produced predictable GUIDs, for example, but this is an old story.