I want to make 2 applications. The first will crypt the file and sign it. The second will be encrypt and verify the data. I use the following code as example.
using System;
using System.Security.Cryptography;
using System.Text;
class RSACSPSample
{
static void Main()
{
try
{
// Create a UnicodeEncoder to convert between byte array and string.
ASCIIEncoding ByteConverter = new ASCIIEncoding();
string dataString = "Data to Sign";
// Create byte arrays to hold original, encrypted, and decrypted data.
byte[] originalData = ByteConverter.GetBytes(dataString);
byte[] signedData;
// Create a new instance of the RSACryptoServiceProvider class
// and automatically create a new key-pair.
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
// Export the key information to an RSAParameters object.
// You must pass true to export the private key for signing.
// However, you do not need to export the private key
// for verification.
RSAParameters Key = RSAalg.ExportParameters(true);
// Hash and sign the data.
signedData = HashAndSignBytes(originalData, Key);
// Verify the data and display the result to the
// console.
if(VerifySignedHash(originalData, signedData, Key))
{
Console.WriteLine("The data was verified.");
}
else
{
Console.WriteLine("The data does not match the signature.");
}
}
catch(ArgumentNullException)
{
Console.WriteLine("The data was not signed or verified");
}
}
public static byte[] HashAndSignBytes(byte[] DataToSign, RSAParameters Key)
{
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
RSAalg.ImportParameters(Key);
// Hash and sign the data. Pass a new instance of SHA1CryptoServiceProvider
// to specify the use of SHA1 for hashing.
return RSAalg.SignData(DataToSign, new SHA1CryptoServiceProvider());
}
catch(CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
public static bool VerifySignedHash(byte[] DataToVerify, byte[] SignedData, RSAParameters Key)
{
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
RSAalg.ImportParameters(Key);
// Verify the data using the signature. Pass a new instance of SHA1CryptoServiceProvider
// to specify the use of SHA1 for hashing.
return RSAalg.VerifyData(DataToVerify, new SHA1CryptoServiceProvider(), SignedData);
}
catch(CryptographicException e)
{
Console.WriteLine(e.Message);
return false;
}
}
}
The problem is I can not understand how can I get OriginalData (1-st parameter) for the function VerifySignedHash? I mean the application (signature checker) will read the signed data. How can I convert the signed data to original data for the data verifying?
Related
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 have two XML files with private and public keys generated by the RSACryptoServiceProvider class. I have turned a random string into a byte array, and using the private key, I have encrypted it. But how do I use the public key to decrypt the byte[] again? Here's what I have so far:
class Program
{
static void Main(string[] args)
{
RSACryptoServiceProvider encryptor = new RSACryptoServiceProvider();
encryptor.FromXmlString(GetPrivateKey());
string unencryptedString = "This string could only have been send by me.";
byte[] unencryptedByteArray = Encoding.Unicode.GetBytes(unencryptedString);
byte[] encryptedByteArray = encryptor.SignData(unencryptedByteArray, new SHA1CryptoServiceProvider());
byte[] decryptedByteArray; //how do I decrypt the array again?
string decryptedString = System.Text.Encoding.Unicode.GetString(decryptedByteArray);
Console.WriteLine(decryptedString);
Console.ReadKey();
}
private static string GetPrivateKey()
{
using (TextReader reader = new StreamReader(#"path to private key file generated by the ToXmlString method"))
{
string privateKey = reader.ReadToEnd();
reader.Close();
return privateKey;
}
}
private static string GetPublicKey()
{
using (TextReader reader = new StreamReader(#"path to public key file generated by the ToXmlString method"))
{
string privateKey = reader.ReadToEnd();
reader.Close();
return privateKey;
}
}
}
You can't decrypt back to the value of unencryptedString. According to the documentation, the method RSACryptoServiceProvider.SignData computes the hash value of the specified data and signs it. As hashes are non-reversible by design, you can't decrypt back to the original value.
However, you can use RSACryptoServiceProvider to encrypt and decrypt data. Below I have included an example application from the documentation on MSDN
using System;
using System.Security.Cryptography;
using System.Text;
class RSACSPSample
{
static void Main()
{
try
{
//Create a UnicodeEncoder to convert between byte array and string.
UnicodeEncoding ByteConverter = new UnicodeEncoding();
//Create byte arrays to hold original, encrypted, and decrypted data.
byte[] dataToEncrypt = ByteConverter.GetBytes("Data to Encrypt");
byte[] encryptedData;
byte[] decryptedData;
//Create a new instance of RSACryptoServiceProvider to generate
//public and private key data.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Pass the data to ENCRYPT, the public key information
//(using RSACryptoServiceProvider.ExportParameters(false),
//and a boolean flag specifying no OAEP padding.
encryptedData = RSAEncrypt(dataToEncrypt, RSA.ExportParameters(false), false);
//Pass the data to DECRYPT, the private key information
//(using RSACryptoServiceProvider.ExportParameters(true),
//and a boolean flag specifying no OAEP padding.
decryptedData = RSADecrypt(encryptedData, RSA.ExportParameters(true), false);
//Display the decrypted plaintext to the console.
Console.WriteLine("Decrypted plaintext: {0}", ByteConverter.GetString(decryptedData));
}
}
catch (ArgumentNullException)
{
//Catch this exception in case the encryption did
//not succeed.
Console.WriteLine("Encryption failed.");
}
}
static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
byte[] encryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This only needs
//toinclude the public key information.
RSA.ImportParameters(RSAKeyInfo);
//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
}
return encryptedData;
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
byte[] decryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);
//Decrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
return decryptedData;
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.ToString());
return null;
}
}
}
I'm quite new to all this encryption thing and I'm trying to do a simple app to encrypt a given string. Here's my code:
public static X509Certificate2 getPublicKey()
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
X509Certificate2 cert2 = new X509Certificate2("c:\\certificate.cer");
return cert2;
}
public static string cipherRequest(byte[] stringToEncrypt)
{
X509Certificate2 certificate = getPublicKey();
RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider;
byte[] cryptedData = rsa.Encrypt(stringToEncrypt, true);
return Convert.ToBase64String(cryptedData);
}
public static void Main()
{
try
{
ASCIIEncoding ByteConverter = new ASCIIEncoding();
byte[] test = ByteConverter.GetBytes("stringtoencrypt");
string first = cipherRequest(test);
string second= cipherRequest(test);
Console.WriteLine("first: {0}", first);
Console.WriteLine("second: {0}", second);
}
catch(CryptographicException e)
{
Console.WriteLine(e.Message);
}
}
So every time I call the cipherRequest it produces different results. I've checked the certificate is loaded but it produces different results.
Any thoughts?
Random padding is added before the actual encryption to avoid certain attacks. This is why you are getting different results each time you call the encryption method.
For more info, see this post:
RSA in C# does not produce same encrypted string for specific keys?
I'm trying to encrypt and decrypt data using RSA in C#. I have the following MSTest unit test:
const string rawPassword = "mypass";
// Encrypt
string publicKey, privateKey;
string encryptedPassword = RSAUtils.Encrypt(rawPassword, out publicKey, out privateKey);
Assert.AreNotEqual(rawPassword, encryptedPassword,
"Raw password and encrypted password should not be equal");
// Decrypt
string decryptedPassword = RSAUtils.Decrypt(encryptedPassword, privateKey);
Assert.AreEqual(rawPassword, decryptedPassword,
"Did not get expected decrypted password");
It fails during decryption, but only sometimes. It seems like whenever I set breakpoints and step through the test, it passes. This made me think perhaps something wasn't finishing in time for decryption to occur successfully, and me slowing stepping through it while debugging gave it enough time to complete. When it fails, the line it seems to fail at is decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); in the following method:
public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
if (string.IsNullOrEmpty(textToDecrypt))
{
throw new ArgumentException(
"Cannot decrypt null or blank string"
);
}
if (string.IsNullOrEmpty(privateKeyXml))
{
throw new ArgumentException("Invalid private key XML given");
}
byte[] bytesToDecrypt = ByteConverter.GetBytes(textToDecrypt);
byte[] decryptedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKeyXml);
decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here
}
return ByteConverter.GetString(decryptedBytes);
}
It fails with this exception:
System.Security.Cryptography.CryptographicException: Bad Data
My Encrypt method is as follows:
public static string Encrypt(string textToEncrypt, out string publicKey,
out string privateKey)
{
byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt);
byte[] encryptedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
publicKey = rsa.ToXmlString(false);
privateKey = rsa.ToXmlString(true);
}
return ByteConverter.GetString(encryptedBytes);
}
The ByteConverter used throughout is just the following:
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
I've seen a few questions on StackOverflow about RSA encryption and decryption with .NET. This one was due to encrypting with the private key and trying to decrypt with the public key, but I don't think I'm doing that. This question has the same exception as me, but the selected answer was to use OpenSSL.NET, which I would prefer not to do.
What am I doing wrong?
Could you replace ByteConverter.GetBytes with Convert.FromBase64String and replace ByteConverter.GetString with Convert.ToBase64String and see if that helps. Bad Data exception usually means that you have an invalid character in the data or that the length is not the correct length for decrypting. I think using the Convert functions might fix your problems.
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
public static string Encrypt(string textToEncrypt, out string publicKey,
out string privateKey)
{
byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt);
byte[] encryptedBytes;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
publicKey = rsa.ToXmlString(false);
privateKey = rsa.ToXmlString(true);
}
return Convert.ToBase64String(encryptedBytes);
}
public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
if (string.IsNullOrEmpty(textToDecrypt))
{
throw new ArgumentException(
"Cannot decrypt null or blank string"
);
}
if (string.IsNullOrEmpty(privateKeyXml))
{
throw new ArgumentException("Invalid private key XML given");
}
byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt);
byte[] decryptedBytes;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKeyXml);
decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here
}
return ByteConverter.GetString(decryptedBytes);
}
Your problem is with the conversion from bytes to string. Not all sequences of bytes are a valid UTF-16 encoding and you are using a UnicodeEncoding that silently ignores invalid bytes. If you used
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(false, false, true);
instead, your code would have failed when trying to convert the bytes instead of silently replacing the invalid byte-pairs with 0xFFFD.
The fact that the test worked while debugging was a coincidence. You are using a random RSA key-pair, so sometimes you will get a encryption that is a valid UTF-16 encoding.
The fix is, as SwDevMan81 suggests, to use an encoding that can convert all possible byte-arrays. F.x. Base64-encoding.
I would recommend using this class, sadly I don't remember the original author though..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace Encryption
{
class AsymmetricED
{
private static RSAParameters param = new RSAParameters();
/// <summary>
/// Get Parameters
/// </summary>
/// <param name="pp">Export private parameters?</param>
/// <returns></returns>
public static RSAParameters GenerateKeys(bool pp)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
if (param.Equals(new RSAParameters()))
{
param = RSA.ExportParameters(true);
}
RSA.ImportParameters(param);
return RSA.ExportParameters(pp);
}
static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Import the RSA Key information. This only needs
//toinclude the public key information.
RSA.ImportParameters(RSAKeyInfo);
//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
return RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);
//Decrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
return RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
ConsoleColor col = Console.BackgroundColor;
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine(e.ToString());
Console.BackgroundColor = col;
return null;
}
}
}
}
Use as:
Encryption.AsymmetricED.RSAEncrypt(Data, GenerateKeys(false), false);
Encryption.AsymmetricED.RSADecrypt(Data, GenerateKeys(true), false);
EDIT:
I also recommend that you don't use this for large data encryption. Usually you would encrypt the actual data with a symmetric algorithm (AES, etc), then encrypt the symmetric key (randomly generated) with the RSA algorithm, then send the rsa encrypted symmetric key, and the symmetric key data..
You should also look at RSA signing, to make sure the data is coming from where it says it is..
I am using this function which I pulled off MSDN
static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
byte[] encryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This only needs
//toinclude the public key information.
RSA.ImportParameters(RSAKeyInfo);
//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
}
return encryptedData;
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
I am calling the method from here:
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Pass the data to ENCRYPT, the public key information
//(using RSACryptoServiceProvider.ExportParameters(false),
//and a boolean flag specifying no OAEP padding.
encryptedData = RSAEncrypt(dataToEncrypt, RSA.ExportParameters(false), false);
But I am getting this error message:
Key not valid for use in specified state.
Any ideas what is going wrong?
The code works fine!! I tried it, I think you may have a problem in decryption as you should use the same keys
Unfortunately i have no idea about your Rsa problem but you may want to try this from here , which i use for a long time.
public static string Encrypt(this string stringToEncrypt, string key)
{
if (string.IsNullOrEmpty(stringToEncrypt))
{
throw new ArgumentException("An empty string value cannot be encrypted.");
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Cannot encrypt using an empty key. Please supply an encryption key.");
}
System.Security.Cryptography.CspParameters cspp = new System.Security.Cryptography.CspParameters();
cspp.KeyContainerName = key;
System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider(cspp);
rsa.PersistKeyInCsp = true;
byte[] bytes = rsa.Encrypt(System.Text.UTF8Encoding.UTF8.GetBytes(stringToEncrypt), true);
return BitConverter.ToString(bytes);
}
You can also find Decrypt Extension there. I hope it'll help.