Import key programmatically to Azure Key Vault - azure

Im able to import keys to Key Vault via PowerShell. Now I want to make a web interface to import the keys.
I tried using KeyVaultClient.ImportKeyAsync() function but Im stuck with the keyBundle parameter. I understand that keyBundle is returned from the KeyVault. I have no idea how to convert the PFX file to keyBundle.
Is there any extension method similar to the Add-AzureKeyVaultKey cmdlet, where I pass the file path and password? Or a method to convert PFX to keyBundle?

It's not quite as easy as a single method, but this should do the trick in .Net 4.6.1. It will only work for PFX that contain RSA keys, but that's essentially the only thing supported by both PFX and KeyVault. Here's the code:
X509Certificate2 cert = new X509Certificate2(
pfxBytes,
password,
X509KeyStorageFlags.Exportable);
using (RSA rsa = cert.GetRSAPrivateKey())
{
var parameters = rsa.ExportParameters(true);
KeyBundle bundle = new KeyBundle
{
Key = new JsonWebKey
{
Kty = JsonWebKeyType.Rsa,
// Private stuff
D = parameters.D,
DP = parameters.DP,
DQ = parameters.DQ,
P = parameters.P,
Q = parameters.Q,
QI = parameters.InverseQ,
// Public stuff
N = parameters.Modulus,
E = parameters.Exponent,
},
};
}
If you are using an older version of .Net, you'd have to use RSA rsa = (RSA) cert.PrivateKey instead of cert.GetRSAPrivateKey(), but the code above is recommended because it handles IDisposable and non-RSA keys more clearly.

Related

XAdES creation with manual signature entry

I am trying to create a digitally signed XML document using the signature from my ID card.
I have two parts of the program. The first one is getting the certificates and signature of the file from the ID.
For that I am using python PKCS11 library with something like this:
with open("input.xml", "rb") as f:
data = f.read()
lib = lib('path/to/pkcs11/lib.dylib')
token = lib.get_token('name of token')
with token.open(PIN) as session:
certificate = None
for obj in session.get_objects({Attribute.CLASS: ObjectClass.CERTIFICATE}):
certificate = obj
der_bytes = certificate[Attribute.VALUE]
with open('certificate.der', "wb") as f:
f.write(der_bytes)
# calculate SHA256 of data
digest = session.digest(data, mechanism=Mechanism.SHA256)
for obj in session.get_objects({Attribute.CLASS: ObjectClass.PRIVATE_KEY}):
private_key = obj
signature = private_key.sign(digest, mechanism=Mechanism.RSA_PKCS)
with open('signature', "wb") as f:
f.write(signature)
That generates the certificate.der and signature files and is working properly (at least I think)
For the XML generation part I am using Europe's DSS library in Java like this:
DSSDocument toSignDocument = new FileDocument("input.xml");
// Preparing parameters for the XAdES signature
XAdESSignatureParameters parameters = new XAdESSignatureParameters();
// We choose the level of the signature (-B, -T, -LT, -LTA).
parameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B);
// We choose the type of the signature packaging (ENVELOPED, ENVELOPING, DETACHED).
parameters.setSignaturePackaging(SignaturePackaging.ENVELOPED);
// We set the digest algorithm to use with the signature algorithm. You must use the
// same parameter when you invoke the method sign on the token. The default value is SHA256 parameters.setDigestAlgorithm(DigestAlgorithm.SHA256);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new FileInputStream("certificate.der");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
// We set the signing certificate
parameters.setSigningCertificate(new CertificateToken(cert));
// Create common certificate verifier
CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();
// Create XAdES service for signature
XAdESService service = new XAdESService(commonCertificateVerifier);
// Get the SignedInfo XML segment that need to be signed.
ToBeSigned dataToSign = service.getDataToSign(toSignDocument, parameters);
File file = new File("signature");
SignatureValue signatureValue = new SignatureValue(SignatureAlgorithm.RSA_SHA256, Files.readAllBytes(file.toPath()));
// We invoke the service to sign the document with the signature value obtained in
// the previous step.
DSSDocument signedDocument = service.signDocument(toSignDocument, parameters, signatureValue);
File signedFile = new File("output.xml");
signedFile.createNewFile();
signedDocument.writeTo(new FileOutputStream(signedFile, false));
That creates XAdES file, but when I try to validate the signature (e.g. using this) it fails saying the signature is not intact.
What am I doing wrong?
You do not use dataToSign variable at all for signature value creation.
What you should do is by using the private key corresponding to the created certificate to actually sign the digested dataToSign. I.e., instead of:
File file = new File("signature");
SignatureValue signatureValue = new SignatureValue(SignatureAlgorithm.RSA_SHA256, Files.readAllBytes(file.toPath()));
you should do something like this (using your example above):
# calculate SHA256 of data
digest = session.digest(dataToSign, mechanism=Mechanism.SHA256)
for obj in session.get_objects({Attribute.CLASS: ObjectClass.PRIVATE_KEY}):
private_key = obj
signatureValue = private_key.sign(digest, mechanism=Mechanism.RSA_PKCS)
Please pay attention, that you shall sign not the original document, but the dataToSign, as it contains the reference to the original document (its digest), but also signed parameters, required to ensure compliance to AdES format.
I hope this will help you.
Best regards,
Aleksandr.

How can I sign a JWT with RSA SHA256 in an Azure API Management Policy Expression?

In an Azure API Management Policy Expression I need to create a JWT signed with a private key.
When I try to use RSACryptoServiceProvider - just to check whether this feedback already got resolved - I get this error when trying to save the policy:
Usage of type 'System.Security.Cryptography.RSACryptoServiceProvider' is not supported within expressions
Following a hint from maxim-kim, I tried RSA.Create() and to convert from this tutorial
var privateKey = "whatever";
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(privateKey, out _);
var signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256)
{
CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
};
var now = DateTime.Now;
var unixTimeSeconds = new DateTimeOffset(now).ToUnixTimeSeconds();
var jwt = new JwtSecurityToken(
audience: _settings.Audience,
issuer: _settings.Issuer,
claims: new Claim[] {
new Claim(JwtRegisteredClaimNames.Iat, unixTimeSeconds.ToString(), ClaimValueTypes.Integer64),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(nameof(claims.FirstName), claims.FirstName),
new Claim(nameof(claims.LastName), claims.LastName),
new Claim(nameof(claims.Email), claims.Email)
},
notBefore: now,
expires: now.AddMinutes(30),
signingCredentials: signingCredentials
);
string token = new JwtSecurityTokenHandler().WriteToken(jwt);
return new JwtResponse
{
Token = token,
ExpiresAt = unixTimeSeconds,
};
but got the next error:
'RSA' does not contain a definition for 'ImportRSAPrivateKey' and no extension method 'ImportRSAPrivateKey' accepting a first argument of type 'RSA' could be found (are you missing a using directive or an assembly reference?)
So my question: Is there a way to create a signed JWT in an Azure API Management Policy Expression?
Thanks to this and other articles, I managed to sign in an APIM policy. Therefore I would like to share this.
<set-variable name="signedPayload" value="#{
using (RSA rsa = context.Deployment.Certificates["thumbprint"].GetRSAPrivateKey())
{
long unixTimeStampInSeconds = DateTimeOffset.Now.ToUnixTimeSeconds();
string header = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
string claimset = String.Format("{{ \"scope\": \"https://www.googleapis.com/auth/devstorage.read_write\", \"aud\": \"https://oauth2.googleapis.com/token\", \"iss\": \"blahblah.gserviceaccount.com\", \"iat\": {0}, \"exp\": {1} }}", unixTimeStampInSeconds, unixTimeStampInSeconds + 3599);
string payload = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(header)) + "." + System.Convert.ToBase64String(Encoding.UTF8.GetBytes(claimset));
byte[] signature = rsa.SignData(Encoding.UTF8.GetBytes(payload), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return System.Net.WebUtility.UrlEncode(payload + "." + System.Convert.ToBase64String(signature));
}
}" />
RSA initialization based on dynamically resolved private and public keys is not supported today.
If RSA parameters are not request specific you can upload x509 certificate to APIM containing required RSA parameters and use it within expressions:
using (var rsa = context.Deployment.Certificates["thumbprint"].GetRSAPrivateKey())
{
....
}

Playing around with certificates in .net5

I am trying to learn about certificates, the need for that is from an idea I had a few days ago to simplify the ordering and delivery of SSL certificates to my collegues. So I startet to investigate "how hard can it be"?
And to large entusiasm, the entire process was "not so hard", but then I came to "generating pfx certificates", with private key...
The code is from a console test app:
Creating keys and storing it.
int keysize = 2048;
CngKeyCreationParameters ckcParams = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
KeyCreationOptions = CngKeyCreationOptions.None,
KeyUsage = CngKeyUsages.AllUsages,
};
ckcParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(KeySize), CngPropertyOptions.None));
CngKey myCngKey = CngKey.Create(CngAlgorithm.Rsa, KeyName, ckcParams);
byte[] privatePlainTextBlob = myCngKey.Export(CngKeyBlobFormat.Pkcs8PrivateBlob);
string privateblob_string = Convert.ToBase64String(privatePlainTextBlob);
// Now I can save the Pkcs8PrivateBlob "somewhere"
Later I can pick up this up and create a Certificate Request and send it to a certificate service by API.
byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);
request = new CertificateRequest(
new X500DistinguishedName(order.CertName),
rsa,
HashAlgorithmName.SHA512,
RSASignaturePadding.Pkcs1);
etc....
Now I can download the issued certificate in various formats from the API.
So far so good. Now I want to create PFX file and KEY file.
Creating PFX:
// My issued certificate from provider
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
// pfx
var certificate = new X509Certificate2(PublicKeyStr, string.Empty, X509KeyStorageFlags.Exportable);
byte[] certificateData = certificate.Export(X509ContentType.Pfx, "password");
// Now I have the pfx, but no private key
I am obviously is not capable of solving this. I have been on a journey into Bouncy Castle, with no luck (btw: where is their documentation?).
I have noticed that .net5 has a metode that I thought (again) might solve this.
X509Certificate2.CreateFromPem(ReadOnlySpan certpem, ReadOnlySpan keypem)
But then I need to get the keypem "private key pem".
My question is simple: Have I totally misunderstod or is the any way to add the needed private key to the pfx file with the information I have stored from the CngKey?
ANY suggestions, ideas, help, tips will be very welcomed. It is simply so frustrating to be so close and just fail miserably.
You need to associate the public certificate with the private key before exporting it as a PFX.
// Key
byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);
// Cert
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
var certificate = new X509Certificate2(PublicKeyStr);
// Together:
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa);
// PFX:
byte[] pfx = certWithKey.Export(X509ContentType.Pfx, pwd);

How to properly implement AES encryption with .net core 2?

For some reason, I've been asked to implement some basic encryption to secure a specific value transmitted to the client (depending on the client).
Context is: we are the one who generate the encrypted key, we pass that encrypted key to the client, and the client will never have to decrypt it (but we will have to in backend)
Example => we give the encrypted key "123ABCDE==" to the client. The client calls our API passing data + that encrypted key, like:
{
"encKey": "123ABCDE==",
"payload": "somedatahere"
}
Then we decrypt the key, if it matches a specific value in DB (again, depending on client), we continue with some other operations.
So, I decided to go with AES encryption. Following is what I have for now.
definition of the key information:
public class KeyInfo
{
public byte[] Key { get; }
public byte[] Iv { get; }
public KeyInfo()
{
using (var myAes = Aes.Create())
{
Key = myAes.Key;
Iv = myAes.IV;
}
}
public KeyInfo(string key, string iv)
{
Key = Convert.FromBase64String(key);
Iv = Convert.FromBase64String(iv);
}
}
the encryption method
private static byte[] Encrypt_AES(string plainText, byte[] key, byte[] iv)
{
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException(nameof(plainText));
if (key == null || key.Length <= 0)
throw new ArgumentNullException(nameof(key));
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException(nameof(iv));
byte[] encrypted;
using (var aesAlgo = Aes.Create())
{
aesAlgo.Key = key;
aesAlgo.IV = iv;
var encryptor = aesAlgo.CreateEncryptor(aesAlgo.Key, aesAlgo.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
the decryption method:
private static string Decrypt_AES(byte[] cipherText, byte[] key, byte[] iv)
{
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException(nameof(cipherText));
if (key == null || key.Length <= 0)
throw new ArgumentNullException(nameof(key));
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException(nameof(iv));
string plaintext;
using (var aesAlgo = Aes.Create())
{
aesAlgo.Key = key;
aesAlgo.IV = iv;
var decryptor = aesAlgo.CreateDecryptor(aesAlgo.Key, aesAlgo.IV);
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
the key and IV stored in appsettings.json:
"EncryptionKey": "myEncryptionKeyHere",
"EncryptionInitialVector": "myInitialVectorHere",
the registration in Satrtup.cs
services.AddTransient(ec => new EncryptionService(new KeyInfo(appSettings.EncryptionKey, appSettings.EncryptionInitialVector)));
I have a few question about all of this.
is AES rh right choice for my needs?
is the way it is implemented here correct?
where should I store the Key and IV ? (not sure appsettings.json is okay)
how do I generate the Key and IV ? Is there tools for that?
Thanks for reading!
EDIT:
You say "we are the one who generate the encrypted key, we pass that
encrypted key to the client" - how does that happen securely?
-> The client will have to connect to his account where he could access that encKey.
So, reading #vcsjones answer, AES may not be the right thing to implement here. Since I don't want to store the IV on database, if the client loses it, it means he would have to generate another key, with another IV, and change the encKey in all applications.
Would an asymetric encryption be better? If I understood correctly, it would mean to encrypt the value with a private key, give that encrypted value to the client + the public key (which would be the same for every client?)
is AES the right choice for my needs?
By itself, no it is not. AES is a cryptographic primitive - a building block - and such building blocks are not usually useful by themselves. For example with AES-CBC (the mode you are using), this is currently vulnerable to a padding oracle attack and lacks authentication. AES might be the right choice when combined with other primitives that provide authentication, like an HMAC.
The best way to solve this problem is to treat primitives for what they are - primitives that are insufficient for use on their own. There are other libraries, like libsodium, that are more abstracted concepts of cryptography, and provides simple APIs that "do the right thing".
You say "we are the one who generate the encrypted key, we pass that encrypted key to the client" - how does that happen securely?
Barring using something like libsodium, there are some issues to address.
There is no authentication of the ciphertext ("authentication" in cryptography has its own meaning, not like sign-on authentication).
The initialization vector should never be used more than once with the same key. You appear to be using a fixed IV. Every thing you encrypt should use its own random IV. The IV is not a secret. It should be authenticated however along with the cipher text as per point 1.
where should I store the Key and IV ?
The IV, since there should be a 1:1 of them with each cipher text (encrypted output) should be stored with the cipher text. When it's time to decrypt, the caller will need to provide the IV again.
The Key is the real secret. Storing them securely is important. If you store it in appsettings.json, then the security of your key is the same as the security of that JSON file. A more common approach in a cloud environment is to use a managed service. Azure has Azure Key Vault, AWS has KMS and Secret Manager.
how do I generate the Key and IV ? Is there tools for that?
They should be generated with a CSPRNG. For example, to do so programmatically:
byte[] iv = new byte[128 / 8];
RandomNumberGenerator.Fill(iv);
//iv now contains random bytes
You can do the same for generating a key.

How to generate a PEM-formatted Key from a 64Byte raw hex-formatted Key

I have the following problem:
After recreating the public key from a signed transaction, I try to encrypt some payload with it.
However the node.js-module named "crypto" is expecting a pem-formatted key in the publicEncrypt(key, payload) function.
My Question:
How can I create the pem-formatted key from a raw hex-encoded key?
Here is the recreated 64 Byte public key:
9f9f445051e788461952124dc08647035c0b31d51f6b4653485723f04c9837adb275d41731309f6125c14ea1546d86a27158eec4164c00bab4724eed925e9c60
Information:
I know, that a pem-format-key consists of base64 encoded data, a header and a footer.
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----
I have also found out that within the base64 encoded data the following DER-structure is present:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
So the only question is how to get from the raw hex-encoded key to this DER-structure.
I would appreciate any help!
Problem solved
Thanks to Maarten Bodewes and his comment regarding the key being secp256k1 and not RSA.
After some further research, I finally managed to encrypt/decrypt a message asymmetrically with secp256k1 keys.
With the help of Cryptos ECDH class I managed to create a key-object and then assign the private key to it. When assigned, you can easily derive the public key with getPublicKey(). All participants would create a key object for themselves and assign their private keys to it. Then they share their retrieved public keys (in my case over a shared medium). In addition I used a npm-package named standard-ecies which provides the ECIES encryption-scheme.
Code:
const crypto = require('crypto');
const ecies = require('standard-ecies');
var buffer = new Buffer("Hello World");
var ecdh = crypto.createECDH('secp256k1');
ecdh.setPrivateKey(privateKey);
var encryptedText = ecies.encrypt(ecdh.getPublicKey(), buffer);
var decryptedText = new Buffer(ecies.decrypt(ecdh, encryptedText));
I should have noticed this, because crypto's encryption function (link to the api-doc) clearly works only with RSA keys and not with secp256k1 keys.
Anyway if someone has a similar issue, I hope this answer helps!

Resources