Azure Key Vault - retrieving RSA public key for on-premises encryption - c#

I'm trying to use an RSA key I have already generated on my Azure Key Vault in the following way:
Retrieve the public key
Encrypt some textual data with it (-locally-)
Decrypt it (in a different app) using Azure Key Vault
What I already managed to do is:
string clientId = "XYZ";
string tenantId = "ABC";
string clientSecret = "123";
string keyVaultName = "kvn";
string keyVaultKeyName = "kvkn";
string textToEncrypt = "StuffIDoNotWantYouToKnow";
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(
tenantId, // your tenant id
clientId, // your AD application appId
clientSecret // your AD application app secret
);
//get key
KeyClient keyClient = new KeyClient(new Uri($"https://{keyVaultName}.vault.azure.net/"), clientSecretCredential); ;
var key = keyClient.GetKey(keyVaultKeyName);
What I'm currently struggling to understand is how to use the retrieved key to encrypt the textual data.
Any help would be appreciated!
P.S I use .NET framework 4.6.1

Solved it
private static string clientId;
private static string tenantId;
private static string clientSecret;
private static string keyVaultName;
private static string keyVaultKeyName;
private static ClientSecretCredential clientSecretCredential;
public static void Main(string[] args)
{
PopulateParams();
KeyClient keyClient = new KeyClient(new Uri($"https://{keyVaultName}.vault.azure.net/"), clientSecretCredential); ;
var key = keyClient.GetKey(keyVaultKeyName);
byte[] N = key.Value.Key.N; //modulus
byte[] E = key.Value.Key.E; //exponent
string textToEncrypt = "StuffIDoNotWantYouToKnow";
byte[] encryptedData = EncryptLocally(textToEncrypt, N, E);
string res = DecryptRemotely(key.Value.Id, encryptedData);
Console.WriteLine(res);
}
public static void PopulateParams()
{
//TODO not hard coded
clientId = "XYZ";
tenantId = "ABC";
clientSecret = "123";
keyVaultName = "kvm";
keyVaultKeyName = "kvkm";
clientSecretCredential = new ClientSecretCredential(
tenantId,
clientId,
clientSecret
);
}
public static byte[] EncryptLocally(string data, byte[] N, byte[] E)
{
byte[] encryptedData = null;
try
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = N;
RSAKeyInfo.Exponent = E;
RSA.ImportParameters(RSAKeyInfo);
byte[] dataBytes = Encoding.ASCII.GetBytes(data);
encryptedData = RSA.Encrypt(dataBytes, true);
}
catch (CryptographicException e)
{
Console.WriteLine(e);
}
return encryptedData;
}
public static string DecryptRemotely(Uri keyId, byte[] encryptedData)
{
string decryptedText = null;
CryptographyClient cryptoClient = new CryptographyClient(keyId, clientSecretCredential);
var decryptedBytes = cryptoClient.Decrypt(EncryptionAlgorithm.RsaOaep, encryptedData);
decryptedText = System.Text.Encoding.UTF8.GetString(decryptedBytes.Plaintext);
return decryptedText;
}

Related

RSA encryption using modulus and exponent public key

I need to encrypt my steam login password. Before sending the auth data, steam sends this: "publickey_mod":"c511d72db5ebbba01977983eec2...","publickey_exp":"010001".
The browser encrypts password with this script:
var pubKey = RSA.getPublicKey(results.publickey_mod, results.publickey_exp);
password = password.replace(/[^\x00-\x7F]/g, ''); // remove non-standard-ASCII characters
var encryptedPassword = RSA.encrypt(password, pubKey);
I can't write a working algorithm in c# which will encrypt the password using modulus and exponent.
Here is what i tried:
static async Task Main()
{
var pubKey = SetPublicKey($"{response["publickey_mod"]}", $"{response["publickey_exp"]}");
string password = "123456";
byte[] password_byte = Encoding.ASCII.GetBytes(password);
byte[] encryptedPassword;
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.ImportParameters(pubKey);
encryptedPassword = RSA.Encrypt(password_byte, false);
}
string encodingPassword = Convert.ToHexString(encryptedPassword);
Console.WriteLine(encodingPassword);
}
public static RSAParameters SetPublicKey(string modulus, string exponent)
{
RSAParameters result = new RSAParameters();
result.Modulus = Convert.FromHexString(modulus);
result.Exponent = Convert.FromHexString(exponent);
return result;
}
working algorithm:
byte[] encryptedPasswordBytes;
using (var rsaEncryptor = new RSACryptoServiceProvider())
{
var passwordBytes = Encoding.ASCII.GetBytes(password);
var rsaParameters = rsaEncryptor.ExportParameters(false);
rsaParameters.Exponent = Convert.FromHexString($"{_response_getrsakey["publickey_exp"]}");
rsaParameters.Modulus = Convert.FromHexString($"{_response_getrsakey["publickey_mod"]}");
rsaEncryptor.ImportParameters(rsaParameters);
encryptedPasswordBytes = rsaEncryptor.Encrypt(passwordBytes, false);
}
string encryptedPassword = Convert.ToBase64String(encryptedPasswordBytes);

"Key does not exist" error in RSA asymmetric encryption decryption

I'm trying to implement an asymmetric cryptography approach using RSA algorithm
for that, I'm using the following class library from Microsoft System.Security.Cryptography
This Image Source
so based on public-key sharing, This is my architecture
Basically, I've three standalone applications
Encryption/Decryption Service, which is a class library.
WPF Application to encrypt plain text to ciphertext.
Console Application to decrypt cipher text to plain text.
so this is my Encryption/Decryption Service Backbone
public class EncryptDecryptService : IEncryptDecryptService
{
private static RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
private RSAParameters _privateKey;
private RSAParameters _publicKey;
public EncryptDecryptService()
{
_privateKey = csp.ExportParameters(true);
_publicKey = csp.ExportParameters(false);
}
public void WritePrivateKey(string _privatePath)
{
TextWriter _privateKeyTxt = new StreamWriter(_privatePath);
_privateKeyTxt.Write(GeneratePrivateKey());
_privateKeyTxt.Close();
}
public void WritePublicKey(string _publicPath)
{
TextWriter _publicKeyTxt = new StreamWriter(_publicPath);
_publicKeyTxt.Write(GeneratePublicKey());
_publicKeyTxt.Close();
}
public string GeneratePublicKey()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _publicKey);
return sw.ToString();
}
public string GeneratePrivateKey()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _privateKey);
return sw.ToString();
}
public string EncryptText(string plainText, string wpfPrivateKeyPath, string consolePublicKeyPath)
{
csp = new RSACryptoServiceProvider();
var consoleAppPublicKey = CovertRSAXMLtoRSAParam(consolePublicKeyPath, false);
var wpfAppPrivateKey = CovertRSAXMLtoRSAParam(wpfPrivateKeyPath, true);
csp.ImportParameters(consoleAppPublicKey);
csp.ImportParameters(wpfAppPrivateKey);
var data = Encoding.Unicode.GetBytes(plainText);// sample
var cypher = csp.Encrypt(data, false);
return Convert.ToBase64String(cypher);
}
public string DecryptText(string cypherText, string wpfPublicKeyPath, string consolePrivateKeyPath)
{
var dataBytes = Convert.FromBase64String(cypherText);
var consoleAppPrivateKey = CovertRSAXMLtoRSAParam(consolePrivateKeyPath, true);
var wpfAppPublicKey = CovertRSAXMLtoRSAParam(wpfPublicKeyPath, false);
csp.ImportParameters(consoleAppPrivateKey);
csp.ImportParameters(wpfAppPublicKey);
var plainText = csp.Decrypt(dataBytes, false);
return Encoding.Unicode.GetString(plainText);
}
public static RSAParameters CovertRSAXMLtoRSAParam(string _location, bool isExport)
{
string xmlContent = System.IO.File.ReadAllText(_location);
RSAParameters _publicKey = new RSAParameters();
CspParameters cspParam = new CspParameters();
cspParam.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider key = new RSACryptoServiceProvider(cspParam);
key.FromXmlString(xmlContent);
_publicKey = key.ExportParameters(isExport);
return _publicKey;
}
}
Like this way I'm generating the ciphertext for a plain text in WPF application
private void Button_Click(object sender, RoutedEventArgs e)
{
var wpfPrivateKeyPath = "C:\\SERVICE\\keys\\wpf_private_key.txt";
var consolePublicKeyPath = "C:\\SERVICE\\keys\\console_public_key.txt";
objWebservice.WritePrivateKey(wpfPrivateKeyPath);
objWebservice.WritePublicKey(consolePublicKeyPath);
string _plainText = PlainTextValue.Text;
string _encryptedText = objWebservice.EncryptText(_plainText, wpfPrivateKeyPath, consolePublicKeyPath);
EncryptedTextValue.Text = _encryptedText;
}
Then that ciphertext I'm trying to convert back to previous plain text in the following way.
public static void Main(string[] args)
{
EncryptDecryptService decryptService = new EncryptDecryptService();
string encryptedText = "SDSFDSFSDFDSFSsdfsdf";
var wpfPublicKeyPath = "C:\\SERVICE\\keys\\wpf_public_key.txt";
var consolePrivateKeyPath = "C:\\SERVICE\\keys\\console_private_key.txt";
decryptService.WritePrivateKey(consolePrivateKeyPath);
decryptService.WritePublicKey(wpfPublicKeyPath);
var plaintText = decryptService.DecryptText(encryptedText, wpfPublicKeyPath, consolePrivateKeyPath);
Console.WriteLine(plaintText);
Console.ReadLine();
}
this is where an error occurring when it's going to decrypt as in this image.
what did I missed here, please if can guide me to make this correct :)

C# AES-GCM and RSA Encryption using Sytem.Secuirty.Cryptography

I am developing the post data request in c# and using AES GCM encryption and RSA encryption. As compared to the C# code with Node.js / Python for encryption, we found that C# AES GCM encryption does not have tag value in the method.
e.g How do I return below data in C# after encryption.
return {
key: aesKey,
cipher_text: encrypted,
tag,
iv
}
In Python and Node.js using JsEncrypt, forge.js.... and implementations are working fine. But, now developing the same client in the C#, do not find such libraries which I can use directly.
However, I found two classes in C# AesGcm and AesManaged for AES Encryption. But I think AesManages is only for AES not GCM encryption. Therefore, I am using AesGcm class in below example, but not sure.
The code executes the following steps:
Request client certificate from DSG.
Extract the public key from the certificate.
Encrypt PII with AES key.
Encrypt AES key with RSA public key.
Send wrapped key and PII payload to DSG tokenization.
class Program
{
static void Main(string[] args)
{
string _ApiKey = "--- YOUR API KEY ----";
string _SecretKey = " ---- YOUR SECRET KEY --------";
string jsonPlainText = "<JSON TExT>";
#region Certificate
string certificate = GetCertificate();
Certificate rawSigningCert = JsonConvert.DescrializeObject<Certificate>(certificate);
var certBytes = Encoding.UTF8.GetBytes(rawSigningCert.crt);
// X509Certificate2
var x509Certificate2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certBytes);
byte[] publicKey = x509Certificate2.GetPublicKey();
#endregion
#region AES GCM Encryption
byte[] aesGcmKey = new byte[16];
RandomNumberGenerator.Fill(aesGcmKey);
byte[] nonce = new byte[12];
RandomNumberGenerator.Fill(nonce);
byte[] tag = new byte[16];
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(jsonPlainText);
byte[] cipherText = new byte[dataToEncrypt.Length];
using (AesGcm aesGcm = new AesGcm(aesGcmKey))
{
aesGcm.Encrypt(nonce, dataToEncrypt, cipherText, tag);
}
string encryptedCipher = Convert.ToBase64String(cipherText);
var keyString = Convert.ToBase64String(aesGcmKey);
var iVString = Convert.ToBase64String(nonce);
var tagString = Convert.ToBase64String(tag);
string aesGcmEncryptionString = string.Format("{0}:{1}:{2}", keyString, iVString, tagString);
#endregion
#region RSA Encryption
var rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = rsa.ExportParameters(false);
RSAKeyInfo.Modulus = publicKey;
rsa.ImportParameters(RSAKeyInfo);
byte[] encryptedSymmetricKey = RSAEncrypt(Encoding.UTF8.GetBytes(aesGcmEncryptionString), RSAKeyInfo, false);
var enc_key = Convert.ToBase64String(encryptedSymmetricKey);
var requestBodyObject = new { message = encryptedCipher, enckey = enc_key };
string jsonRequestBody = JsonConvert.SerializeObject(requestBodyObject);
#endregion
#region Calculating Message Signature
Random random = new Random();
int ClientRequestId = random.Next(0, 99999999);
var time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var rawSignature = _ApiKey + ClientRequestId + time + jsonRequestBody;
HMACSHA256 hashObject = new HMACSHA256(Encoding.UTF8.GetBytes(_SecretKey));
var signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(rawSignature));
var computedHmac = Convert.ToBase64String(signature);
#endregion
#region Make Request
try
{
var client = new RestClient("< API URL>");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("contentType", "application/json");
request.AddHeader("clientRequestId", ClientRequestId.ToString());
request.AddHeader("apiKey", _ApiKey);
request.AddHeader("timestamp", time.Tostring);
request.AddHeader("messageSignature", computedHmac);
request.AddJsonBody(jsonRequestBody);
request.RequestFormat = DataFormat.Json;
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
#endregion
}
static string GetCertificate()
{
string cer = string.Empty;
// Code to get certicate .crt from the service.
//"<GET CERTICATE HERE>";
return cer;
}
static public byte[] RSAEncrpt(byte[] data, RSAParameters keyInfo, bool doOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(keyInfo);
encryptedData = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
}
return encryptedData;
}
}
class Certificate
{
public string crt { get; set; }
}
How I can trace each steps as service is responding with incorrect message signature. Any way to verify and trace the above code steps?
Help!
Thanks in advance

Key not valid for use in specified state in RSA encryption

here is my code.get error in encryption method
encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
The method throws "Key not valid for use in specified state."
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
private void button1_Click(object sender, EventArgs e)
{
var rsa = new RSACryptoServiceProvider();
var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
var writer = new StringWriter();
var writer2 = new StringWriter();
var pemWriter = new PemWriter(writer);
var pemWriter2 = new PemWriter(writer2);
pemWriter.WriteObject(rsaKeyPair.Public);
pemWriter2.WriteObject(rsaKeyPair.Private);
string Pub = writer.ToString();
string Prv = writer2.ToString();
plaintext = ByteConverter.GetBytes(txtplain.Text);
encryptedtext = Encryption(plaintext, Pub, false);
}
get error here when encrypt data using by RSA encryption method.here pass DATA as a byte and RSA public key as a string.after that convert it to RSAParanetrs "RSA.ImportParameters(GetRSAParameters(RSAKey));"
then encrypt data get this error.
static public byte[] Encryption(byte[] Data, string RSAKey, bool DoOAEPPadding)
{
try
{
byte[] encryptedData;
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.ImportParameters(GetRSAParameters(RSAKey));
encryptedData = RSA.Encrypt(Data, DoOAEPPadding); // get error "Key not valid for use in specified state"
}
return encryptedData;
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
private static RSAParameters GetRSAParameters(string pPublicKey)
{
byte[] lDer;
int lBeginStart = "-----BEGIN PUBLIC KEY-----".Length;
int lEndLenght = "-----END PUBLIC KEY-------".Length;
string KeyString = pPublicKey.Substring(lBeginStart, (pPublicKey.Length - lBeginStart - lEndLenght));
lDer = Convert.FromBase64String(KeyString);
RSAParameters lRSAKeyInfo = new RSAParameters();
lRSAKeyInfo.Modulus = GetModulus(lDer);
lRSAKeyInfo.Exponent = GetExponent(lDer);
return lRSAKeyInfo;
}
private static byte[] GetModulus(byte[] pDer)
{
string lModulus = BitConverter.ToString(pDer).Replace("-", "").Substring(58, 256);
return StringHexToByteArray(lModulus);
}
private static byte[] GetExponent(byte[] pDer)
{
int lExponentLenght = pDer[pDer.Length - 3];
string lExponent = BitConverter.ToString(pDer).Replace("-", "").Substring((pDer.Length * 2) - lExponentLenght * 2, lExponentLenght * 2);
return StringHexToByteArray(lExponent);
}
public static byte[] StringHexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}

How can I create a SHA1WithRSA signature

I have a signature generator in Java:
private static String getPvtKeyFromConfig = "merchantPvtKey";
private static String getPubKeyFromConfig = "merchantPubKey";
private static String getSaltFromConfig = "merchant_salt";
public static void main(String[] args) throws Exception {
// Generate Signature
String uniqueId="ab123";
byte[] data = Base64.decodeBase64(uniqueId);
java.security.Signature sig =java.security.Signature.getInstance("SHA1WithRSA");
sig.initSign(getPrivateFromSpec(getPvtKeyFromConfig));
sig.update(data);
byte[] signatureBytes = sig.sign();
System.out.println("Signature for uniqueId - "+uniqueId+": "+ Base64.encodeBase64String(signatureBytes));
}
How can I do it in C#?
I think this is what you are looking for:
static byte[] Sign(string text, string certSubject)
{
// Access a store
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
// Find the certificate used to sign
RSACryptoServiceProvider provider = null;
foreach (X509Certificate2 cert in store.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// Get its associated CSP and private key
provider = (RSACryptoServiceProvider)cert.PrivateKey;
break;
}
}
if (provider == null)
throw new Exception("Certificate not found.");
// Hash the data
var hash = HashText(text);
// Sign the hash
var signature = provider.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
return signature;
}
}
static bool Verify(string text, byte[] signature, string certPath)
{
// Load the certificate used to verify the signature
X509Certificate2 certificate = new X509Certificate2(certPath);
// Get its associated provider and public key
RSACryptoServiceProvider provider = (RSACryptoServiceProvider)certificate.PublicKey.Key;
// Hash the data
var hash = HashText(text);
// Verify the signature with the hash
var result = provider.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
return result;
}
static byte[] HashText(string text)
{
SHA1Managed sha1Hasher = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1Hasher.ComputeHash(data);
return hash;
}
Sample usage:
var signature = Sign("To be or not to be, that is the question.", "CN=some_cert");
var result = Verify("To be or not to be, that is the question.", signature, "C:\\temp\\some_cert.cer");
Console.WriteLine("Verified: {0}", result);
Below C# code works for me for the exact java code mentioned in the question.
Few Notes :
.) Your project should have Target frameworks as 4.8
.) You should have existing private key
.) Using this Private key we can generate the .pfx certificate by using the OpenSSL commands.( we will have to generate the .crt first and then .pfx)
static string GenerateSignatureUsingCert(string dataToSign)
{
X509Certificate2 certificate = new X509Certificate2(#"C:\PFX\MyCertificate.pfx", "****", X509KeyStorageFlags.Exportable);
RSA privateKey1 = certificate.GetRSAPrivateKey();
Encoding unicode = Encoding.UTF8;
byte[] bytesInUni = unicode.GetBytes(dataToSign);
return Convert.ToBase64String(privateKey1.SignData(bytesInUni, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1));
}
Usage
string canonicalizeData = CanonicalizeHeaders(headers);
String data = null;
try
{
data = GenerateSignatureUsingCert(canonicalizeData);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Signature: " + data);

Categories