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 :)
Related
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;
}
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();
}
This question might be duplicate but other solutions seem to be not working for me. I am working on RSA for the first time, so little confusing. I have public and private keys and want to sign the text using these. Below is my code:
public void RSA()
{
var publickey = "***";
var privatekey = "***";
using (var RSA = new RSACryptoServiceProvider())
{
UnicodeEncoding ByteConverter = new UnicodeEncoding();
RSAParameters myRSAParameters = RSA.ExportParameters(true);
myRSAParameters.Modulus = ByteConverter.GetBytes(publickey);
myRSAParameters.D = ByteConverter.GetBytes(privatekey);
myRSAParameters.Exponent = ByteConverter.GetBytes("5");
string signedmessage = SignData("ABC", myRSAParameters);
}
}
public static String SignData(string message, RSAParameters privatekey)
{
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] originalData = encoder.GetBytes(message);
try
{
rsa.ImportParameters(privatekey);
signedBytes = rsa.SignData(originalData, CryptoConfig.MapNameToOID("SHA512"));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
return Convert.ToBase64String(signedBytes);
}
Actually I am not sure about RSAParameters they are given correctly or not. After running this code this throws an exception as
"Bad data .\r\n"
Any help would be appreciated.
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);
I am building a custom shopping cart where CC numbers and Exp date will be stored in a database until processing (then deleted). I need to encrypt this data (obviously).
I want to use the RSACryptoServiceProvider class.
Here is my code to create my keys.
public static void AssignNewKey(){
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "KeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
string publicPrivateKeyXML = rsa.ToXmlString(true);
string publicOnlyKeyXML = rsa.ToXmlString(false);
// do stuff with keys...
}
Now the plan is to store the private key xml on a USB drive attached to the managers key chain.
Whenever a manager leaves the company I want to be able to generate new public and private keys (and re-encrypt all currently stored CC numbers with the new public key).
My problem is that the keys generated by this code are always the same. How would I generate a unique set of keys every time?
UPDATE. My test code is below.:
note: the "privatekey" parameter here is the original private key. In order for the keys to be changed I need to verify that the private key is valid.
In Default.aspx.cs
public void DownloadNewPrivateKey_Click(object sender, EventArgs e)
{
StreamReader reader = new StreamReader(fileUpload.FileContent);
string privateKey = reader.ReadToEnd();
Response.Clear();
Response.ContentType = "text/xml";
Response.End();
Response.Write(ChangeKeysAndReturnNewPrivateKey(privateKey));
}
In Crytpography.cs:
public static privateKey;
public static publicKey;
public static RSACryptoServiceProvider rsa;
public static string ChangeKeysAndReturnNewPrivateKey(string _privatekey)
{
string testData = "TestData";
string testSalt = "salt";
// encrypt the test data using the exisiting public key...
string encryptedTestData = EncryptData(testData, testSalt);
try
{
// try to decrypt the test data using the _privatekey provided by user...
string decryptTestData = DecryptData(encryptedTestData, _privatekey, testSalt);
// if the data is successfully decrypted assign new keys...
if (decryptTestData == testData)
{
AssignNewKey();
// "AssignNewKey()" should set "privateKey" to the newly created private key...
return privateKey;
}
else
{
return string.Empty;
}
}
catch (Exception ex)
{
return string.Empty;
}
}
public static void AssignParameter(){
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "KeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
}
public static void AssignNewKey()
{
AssignParameter();
using (SqlConnection myConn = new SqlConnection(Utilities.ConnectionString))
{
SqlCommand myCmd = myConn.CreateCommand();
string publicPrivateKeyXML = rsa.ToXmlString(true);
privateKey = publicPrivateKeyXML; // sets the public variable privateKey to the new private key.
string publicOnlyKeyXML = rsa.ToXmlString(false);
publicKey = publicOnlyKeyXML; // sets the public variable publicKey to the new public key.
myCmd.CommandText = "UPDATE Settings SET PublicKey = #PublicKey";
myCmd.Parameters.AddWithValue("#PublicKey", publicOnlyKeyXML);
myConn.Open();
myComm.ExecuteScalar();
}
}
public static string EncryptData(string data2Encrypt, string salt)
{
AssignParameter();
using (SqlConnection myConn = new SqlConnection(Utilities.ConnectionString))
{
SqlCommand myCmd = myConn.CreateCommand();
myCmd.CommandText = "SELECT TOP 1 PublicKey FROM Settings";
myConn.Open();
using (SqlDataReader sdr = myCmd.ExecuteReader())
{
if (sdr.HasRows)
{
DataTable dt = new DataTable();
dt.Load(sdr);
rsa.FromXmlString(dt.Rows[0]["PublicKey"].ToString());
}
}
}
//read plaintext, encrypt it to ciphertext
byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt + salt);
byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
return Convert.ToBase64String(cipherbytes);
}
public static string DecryptData(string data2Decrypt, string privatekey, string salt)
{
AssignParameter();
byte[] getpassword = Convert.FromBase64String(data2Decrypt);
string publicPrivateKeyXML = privatekey;
rsa.FromXmlString(publicPrivateKeyXML);
//read ciphertext, decrypt it to plaintext
byte[] plain = rsa.Decrypt(getpassword, false);
string dataAndSalt = System.Text.Encoding.UTF8.GetString(plain);
return dataAndSalt.Substring(0, dataAndSalt.Length - salt.Length);
}
When you use a code like this:
using (var rsa = new RSACryptoServiceProvider(1024))
{
// Do something with the key...
// Encrypt, export, etc.
}
.NET (actually Windows) stores your key in a persistent key container forever.
The container is randomly generated by .NET
This means:
Any random RSA/DSA key you have EVER generated for the purpose of protecting data, creating custom X.509 certificate, etc. may have been exposed without your awareness in the Windows file system. Accessible by anyone who has access to your account.
Your disk is being slowly filled with data. Normally not a big concern but it depends on your application (e.g. it might generates hundreds of keys every minute).
To resolve these issues:
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
// Do something with the key...
// Encrypt, export, etc.
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
ALWAYS
The RSACryptoServiceProvider(CspParameters) constructor creates a keypair which is stored in the keystore on the local machine. If you already have a keypair with the specified name, it uses the existing keypair.
It sounds as if you are not interested in having the key stored on the machine.
So use the RSACryptoServiceProvider(Int32) constructor:
public static void AssignNewKey(){
RSA rsa = new RSACryptoServiceProvider(2048); // Generate a new 2048 bit RSA key
string publicPrivateKeyXML = rsa.ToXmlString(true);
string publicOnlyKeyXML = rsa.ToXmlString(false);
// do stuff with keys...
}
EDIT:
Alternatively try setting the PersistKeyInCsp to false:
public static void AssignNewKey(){
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "KeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
rsa.PersistKeyInCsp = false;
string publicPrivateKeyXML = rsa.ToXmlString(true);
string publicOnlyKeyXML = rsa.ToXmlString(false);
// do stuff with keys...
}
What I ended up doing is create a new KeyContainer name based off of the current DateTime (DateTime.Now.Ticks.ToString()) whenever I need to create a new key and save the container name and public key to the database. Also, whenever I create a new key I would do the following:
public static string ConvertToNewKey(string oldPrivateKey)
{
// get the current container name from the database...
rsa.PersistKeyInCsp = false;
rsa.Clear();
rsa = null;
string privateKey = AssignNewKey(true); // create the new public key and container name and write them to the database...
// re-encrypt existing data to use the new keys and write to database...
return privateKey;
}
public static string AssignNewKey(bool ReturnPrivateKey){
string containerName = DateTime.Now.Ticks.ToString();
// create the new key...
// saves container name and public key to database...
// and returns Private Key XML.
}
before creating the new key.