RSA encryption by supplying modulus and exponent - c#
I am creating a C# Winforms application which POSTs data to a server over HTTPS.
The login mechanism is supposed to be like this:
I send the username to the server, it responds with rsa-modulus and rsa-exponent
I encrypt the password using these given parameters and send username + password to the server for authentication
I have tried the RSACryptoServiceProvider class, but I cannot find samples or anything said on how we can do the encryption using a given modulus and exponent?.
I think that without specifying any values, its doing default encryption parameters..
So if anybody has done this before, can they give me some hints please? thanks
UPDATE: according to the suggestion by Mr. Carsten Konig, . I have tried to do it with RSAParameters and RSA.ImportParameters, but it returns a "BAD DATA" error with cryptographic exception. My code is given below.
I have also tried RSA.FromXmlString(mykey); (where mykey contains an xml string with modulus and exp) but I also get a "BAD DATA" errror with cryptographic exception... any idea anybody? or if its some microsoft bug, can anyone suggest some other decent library to do this easily?
RSAParameters rsaparam = new RSAParameters();
rsaparam.Modulus = modbytes;
rsaparam.Exponent = expbytes;
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider() ;
RSA.ImportParameters(rsaparam);
byte[] encryptedData = RSA.Encrypt(dataToEncrypt, false)
You can do this by using the RSACryptoServiceProvider.Encrypt method. You will also need to use the RSACryptoServiceProvider.ImportParameters method and pass it an RSAParameters structure (this is where you set the exponent, modulus, etc).
Please have a look at the documentation in the link for the RSAParameters - it's very well documented what parameter you have to pass for what structure-field - should be no problem if you now the algorithm.
EDIT: here is the example straight from the MSDN-site:
class RSACSPSample
{
static void Main()
{
try
{ //initialze the byte arrays to the public key information.
byte[] PublicKey = {214,46,220,83,160,73,40,39,201,155,19,202,3,11,191,178,56,
74,90,36,248,103,18,144,170,163,145,87,54,61,34,220,222,
207,137,149,173,14,92,120,206,222,158,28,40,24,30,16,175,
108,128,35,230,118,40,121,113,125,216,130,11,24,90,48,194,
240,105,44,76,34,57,249,228,125,80,38,9,136,29,117,207,139,
168,181,85,137,126,10,126,242,120,247,121,8,100,12,201,171,
38,226,193,180,190,117,177,87,143,242,213,11,44,180,113,93,
106,99,179,68,175,211,164,116,64,148,226,254,172,147};
byte[] Exponent = {1,0,1};
//Values to store encrypted symmetric keys.
byte[] EncryptedSymmetricKey;
byte[] EncryptedSymmetricIV;
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Create a new instance of RSAParameters.
RSAParameters RSAKeyInfo = new RSAParameters();
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = PublicKey;
RSAKeyInfo.Exponent = Exponent;
//Import key parameters into RSA.
RSA.ImportParameters(RSAKeyInfo);
//Create a new instance of the RijndaelManaged class.
RijndaelManaged RM = new RijndaelManaged();
//Encrypt the symmetric key and IV.
EncryptedSymmetricKey = RSA.Encrypt(RM.Key, false);
EncryptedSymmetricIV = RSA.Encrypt(RM.IV, false);
Console.WriteLine("RijndaelManaged Key and IV have been encrypted with RSACryptoServiceProvider.");
}
//Catch and display a CryptographicException
//to the console.
catch(CryptographicException e)
{
Console.WriteLine(e.Message);
}
}
}
Please note that only the key/iv gets encrypted - not arbitrary bytes - the length of those bytes is important too!
The allowed length is described in MSDN an depends on the OS!
If you are using RSACryptoServiceProvider.ToXmlString to export the modulus and exponent that the server sends, you need to use Convert.FromBase64String.
public RSAParameters SetPublicKey(string modulus, string exponent)
{
RSAParameters result = new RSAParameters();
result.Modulus = Convert.FromBase64String(modulus);
result.Exponent = Convert.FromBase64String(exponent);
return result;
}
One additional hint that was very useful for me:
In this line,
//Set RSAKeyInfo to the public key values.
SAKeyInfo.Modulus = PublicKey;
PublicKey can also be a direct, straightforward, array of bytes that you can get from the "Public Key" field of a X509 Certificate (directly).
Related
Compare two RSA encrypted strings which uses different encoding
I have a string "test_string", which is encrypted using RSA algorithm using two different encodings as shown in the below C# code. I want to check if the encrypted value in both the cases are the same, since I am using the same key in both cases. But due to RSA padding, the encrypted value changes every time even if I am using the same key and the same data "test_string" and same encoding. So, is there any way I can make sure that the encrypted value is same/different by avoid the padding? Any other recommendations are welcome. string encryptedUTF16; string encryptedUTF8; RSA rsa = RSA.Create(); RSAParameters rsaParams = rsa.ExportParameters(true); using (var rsa1 = new RSACryptoServiceProvider()) { rsa1.ImportParameters(rsaParams); byte[] bytesEncrytedKeyUTF16 = Encoding.Unicode.GetBytes("test_string".ToCharArray()); byte[] encrytedKeyUTF16 = rsa1.Encrypt(bytesEncrytedKeyUTF16, true); encryptedUTF16 = Convert.ToBase64String(encrytedKeyUTF16); Console.WriteLine(encryptedUTF16); } using (var rsa2 = new RSACryptoServiceProvider()) { rsa2.ImportParameters(rsaParams); byte[] bytesEncrytedKeyUTF8 = Encoding.UTF8.GetBytes("test_string".ToCharArray()); byte[] encrytedKeyUTF8 = rsa2.Encrypt(bytesEncrytedKeyUTF8, true); encryptedUTF8 = Convert.ToBase64String(encrytedKeyUTF8); Console.WriteLine(encryptedUTF8); } I tried using OpenSSl and BouncyCastle as well. But ran into technical issues running them.
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException when Decrypting RSA in C#
I'm testing RSA in C# dotnet core. I create two RSA objects, one for encrypting and the other for decrypting. I export the public key from the first rsa object and import it for the other object. When the second one decrypt the cipher array, it throws Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException. The code is below: String plainstr = "Hello World"; RSA rsa1 = RSA.Create(); RSA rsa2 = RSA.Create(); rsa1.KeySize = 1024; rsa2.KeySize = 1024; byte[] cipherbytes = rsa1.Encrypt(Encoding.ASCII.GetBytes(plainstr), RSAEncryptionPadding.Pkcs1); //If the parameter is true, it works well. But when I use it in an actual project, I won't pass the private key. RSAParameters parameters = rsa1.ExportParameters(false); rsa2.ImportParameters(parameters); //Exception is here. byte[] plaintbytes = rsa2.Decrypt(cipherbytes, RSAEncryptionPadding.Pkcs1); Console.WriteLine(Encoding.ASCII.GetString(plaintbytes)); Console.ReadKey();
This is how RSA Encryption works. You can Encrypt with the public key but you can only Decrypt with the private key. In your example you are encrypting the string with the private key of the rsa1 object, you are copying the public parameters of it to rsa2 and you are trying to decrypt with it. Maybe you want to do the opposite?
Purpose of RijndaelManaged instance?
I have Found following Code Sample on MSDN when i was searching for RSACypthyServiceProvider.I couldn't understand some part of thecode with the help of comment. What is modulus and Exponent ? What is IV? Why they are using RijndaelManagedclass to do asymmetric encryption? Based on my search RSACryptoServiceProvider provides Asymmetric encryption functionality and it will automatically creates Private and Public key when we create the object .So What is the Purpose of RijndaelManaged instance here? Can any one please explain? Code Sample: class Class1 { static void Main() { //Initialize the byte arrays to the public key information. byte[] PublicKey = {Somethink in byte} byte[] Exponent = {1,0,1}; //Create values to store encrypted symmetric keys. byte[] EncryptedSymmetricKey; byte[] EncryptedSymmetricIV; //Create a new instance of the RSACryptoServiceProvider class. RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); //Create a new instance of the RSAParameters structure. RSAParameters RSAKeyInfo = new RSAParameters(); //Set RSAKeyInfo to the public key values. RSAKeyInfo.Modulus = PublicKey; RSAKeyInfo.Exponent = Exponent; //Import key parameters into RSA. RSA.ImportParameters(RSAKeyInfo); //Create a new instance of the RijndaelManaged class. RijndaelManaged RM = new RijndaelManaged(); //Encrypt the symmetric key and IV. EncryptedSymmetricKey = RSA.Encrypt(RM.Key, false); EncryptedSymmetricIV = RSA.Encrypt(RM.IV, false); } }
RSA is very slow, and has overhead for padding. So it's common to generate a random symmetric key, encrypt it with RSA, and encrypt the message with the symmetric key. This approach is known as hybrid cryptosystem. IVs are important if a single key is used to encrypt multiple messages, but since this code creates a new key for each message, the IV isn't really important here. Still using an IV can prevent multi-target attacks, so it's not completely useless with unique keys, especially if the key only has 128 bits. This code is pretty inefficient too: It encrypts IV and key separately, instead of concatenating them. This doubles the RSA overhead. Modulus and exponent are the two parts of an RSA public key. Look up wikipedia for details. The exponent is often chosen to be 2^16 + 1 = 65537, which corresponds to the {1,0,1} in this code.
CryptographicException "Key not valid for use in specified state." while trying to export RSAParameters of a X509 private key
I am staring at this for quite a while and thanks to the MSDN documentation I cannot really figure out what's going. Basically I am loading a PFX file from the disc into a X509Certificate2 and trying to encrypt a string using the public key and decrypt using the private key. Why am I puzzled: the encryption/decryption works when I pass the reference to the RSACryptoServiceProvider itself: byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider); string foo1 = DecryptRSA(ed1, x.PrivateKey as RSACryptoServiceProvider); But if the export and pass around the RSAParameter: byte[] ed = EncryptRSA("foo", (x.PublicKey.Key as RSACryptoServiceProvider).ExportParameters(false)); string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true)); ...it throws a "Key not valid for use in specified state." exception while trying to export the private key to RSAParameter. Please note that the cert the PFX is generated from is marked exportable (i.e. I used the pe flag while creating the cert). Any idea what is causing the exception? static void Main(string[] args) { X509Certificate2 x = new X509Certificate2(#"C:\temp\certs\1\test.pfx", "test"); x.FriendlyName = "My test Cert"; X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadWrite); try { store.Add(x); } finally { store.Close(); } byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider); string foo1 = DecryptRSA(ed1, x.PrivateKey as RSACryptoServiceProvider); byte[] ed = EncryptRSA("foo", (x.PublicKey.Key as RSACryptoServiceProvider).ExportParameters(false)); string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true)); } private static byte[] EncryptRSA(string data, RSAParameters rsaParameters) { UnicodeEncoding bytConvertor = new UnicodeEncoding(); byte[] plainData = bytConvertor.GetBytes(data); RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider(); publicKey.ImportParameters(rsaParameters); return publicKey.Encrypt(plainData, true); } private static string DecryptRSA(byte[] data, RSAParameters rsaParameters) { UnicodeEncoding bytConvertor = new UnicodeEncoding(); RSACryptoServiceProvider privateKey = new RSACryptoServiceProvider(); privateKey.ImportParameters(rsaParameters); byte[] deData = privateKey.Decrypt(data, true); return bytConvertor.GetString(deData); } private static byte[] EncryptRSA(string data, RSACryptoServiceProvider publicKey) { UnicodeEncoding bytConvertor = new UnicodeEncoding(); byte[] plainData = bytConvertor.GetBytes(data); return publicKey.Encrypt(plainData, true); } private static string DecryptRSA(byte[] data, RSACryptoServiceProvider privateKey) { UnicodeEncoding bytConvertor = new UnicodeEncoding(); byte[] deData = privateKey.Decrypt(data, true); return bytConvertor.GetString(deData); } Just to clarify in the code above the bold part is throwing: string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider)**.ExportParameters(true)**);
I believe that the issue may be that the key is not marked as exportable. There is another constructor for X509Certificate2 that takes an X509KeyStorageFlags enum. Try replacing the line: X509Certificate2 x = new X509Certificate2(#"C:\temp\certs\1\test.pfx", "test"); With this: X509Certificate2 x = new X509Certificate2(#"C:\temp\certs\1\test.pfx", "test", X509KeyStorageFlags.Exportable);
For the issue I encountered a code change was not an option as the same library was installed and working elsewhere. Iridium's answer lead me to look making the key exportable and I was able to this as part of the MMC Certificate Import Wizard. Hope this helps someone else. Thanks heaps
I've met some similar issue, and X509KeyStorageFlags.Exportable solved my problem.
I'm not exactly an expert in these things, but I did a quick google, and found this: http://social.msdn.microsoft.com/Forums/en/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce "if you have more than 245 bytes in your byte array that you pass to your RSACryptoServiceProvider.Encrypt(byte[] rgb, bool fOAEP) method then it will throw an exception."
For others that end up here through Google, but don't use any X509Certificate2, if you call ToXmlString on RSACryptoServiceProvider but you've only loaded a public key, you will get this message as well. The fix is this (note the last line): var rsaAlg = new RSACryptoServiceProvider(); rsaAlg.ImportParameters(rsaParameters); var xml = rsaAlg.ToXmlString(!rsaAlg.PublicOnly);
AFAIK this should work and you're likely hitting a bug/some limitations. Here's some questions that may help you figure out where's the issue. How did you create the PKCS#12 (PFX) file ? I've seen some keys that CryptoAPI does not like (uncommon RSA parameters). Can you use another tool (just to be sure) ? Can you export the PrivateKey instance to XML, e.g. ToXmlString(true), then load (import) it back this way ? Old versions of the framework had some issues when importing a key that was a different size than the current instance (default to 1024 bits). What's the size of your RSA public key in your certificate ? Also note that this is not how you should encrypt data using RSA. The size of the raw encryption is limited wrt the public key being used. Looping over this limit would only give you really bad performance. The trick is to use a symmetric algorithm (like AES) with a totally random key and then encrypt this key (wrap) using the RSA public key. You can find C# code to do so in my old blog entry on the subject.
Old post, but maybe can help someone. If you are using a self signed certificate and make the login with a different user, you have to delete the old certificate from storage and then recreate it. I've had the same issue with opc ua software
How to use c# to decrypt an MD5 hash that is encrypted using RSA1024
I trying to verify the integrity of a file at work and an having a hard time of it. I'm not very well versed with encryption and hashing, so bear with me. I have some files that have an MD5 hash located at the end of them. I have written code to grab the bytes that I think are the hash and they seen to be uniformly 128 bytes long. In the file, just before the hash, is the keyword "RSA1024", which I have taken to mean the hash is encrypted using RSA 1024. I have what I know is the RSA key in a file, and have read out the bytes (always 258 bytes long). I have seen many tutorials which use FromXmlString() to pull in the key, but this RSA key was not generated using the .net framework, and is not in an XML format. I have written the following method to decrypt the hash data using the key, and it throws this error when executing ImportCspBlob() - System.Security.Cryptography.CryptographicException: Bad Version of provider. Any ideas? public byte[] DecryptRSA(byte[] encryptedData, byte[] keyData) { CspParameters param = new CspParameters(); param.Flags = CspProviderFlags.UseExistingKey; RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param); rsaProvider.ImportCspBlob(keyData); byte[] decryptedData = rsaProvider.Decrypt(encryptedData, false); return decryptedData; } Basic Algorithm It may sound strange to want to "decrypt an MD5 hash", and especially when one says that they want to "decrypt it with a public key". But that is how digital signatures work. With RSA you can: encrypt with private key decrypt with the public key The message digest is encrypted with the private key, and can then only be decrypted with the public key. That way you know that only the person with the private key could have signed the message.
Your key is most likely not a CSP-type key (it is most likely DER encoded). You can decrypt it using Bouncy Castle with the DER key like this: RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(key); byte[] rv = null; RsaEngine eng = new RsaEngine(); eng.Init(false, privateKey); int size = eng.GetOutputBlockSize(); rv = eng.ProcessBlock(cipher, 0, cipher.Length); EDIT: to addressing GregS scenario that it may be a signature verify operation If you are trying to verify a signature, you would need a certificate used to verify a message, the original message text, and the existing message signature to compare against. What you do is pass in the original message text (minus the signature), the bytes of the message signature, and the path to the certificate you will use to verify the passed in signature. Then, you will hash the original message and compare the result against the passed in signature. Here is some code to illustrate: private bool VerifySignature(string messageText, byte[] messageSignature, string certificatePath) { // Load the certificate from a file X509Certificate2 cert = new X509Certificate2(certificatePath); // Get public key RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key; // Next, hash the messageText SHA1Managed sha1 = new SHA1Managed(); byte[] messageBytes = Encoding.Unicode.GetBytes(messageText); byte[] hash = sha1.ComputeHash(messageBytes); // Verify the signature with the hash return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), messageSignature); }
MD5 is one-way hash. But you might check out hashing algorithm. There are some ways to break this hash, just do some research ;)