I have encrypted a text using RSACryptoServiceProvider. I exported the public and private key. Obviously I just want to expose the public key inside the decoder application, so I have written a code as follows :
private const string PublicKey = "<RSAKeyValue><Modulus>sIzQmj4vqK0QPd7RXKigD7Oi4GKPwvIPoiUyiKJMGP0qcbUkRPioe2psE/d3c1a2NY9oj4Da2y1qetjvKKFad2QAhXuql/gPIb1WmI+f6q555GClvHWEjrJrD/ho7SLoHbWd6oY6fY609N28lWJUYO97RLVaeg2jfNAUSu5bGC8=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
private string Decrypt()
{
byte[] encryptedKeyAsBytes = Convert.FromBase64String(_encryptedKey);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(PublicKey);
// read ciphertext, decrypt it to plaintext
byte[] plainBytes = rsa.Decrypt(encryptedKeyAsBytes, false);
string plainText = System.Text.Encoding.ASCII.GetString(plainBytes);
return plainText;
}
But an exception is thrown at line "byte[] plainBytes = rsa.Decrypt(encryptedKeyAsBytes, false);"
and says "Key does not exist." However if I expose the whole private and public key then it runns happily. So how can I decrypt the data using only the public key information?
You can't - that is the point of public/private key encryption. The public does the encryption; the private does the decryption.
It sounds like you need some sort of key exchange pattern. For example; if your decoder application is trying to decrypt information from another data source (Source Application), I would implement something like this:
The Source Application generates a symmetric key, like AES.
The Decoder application generates a public and private key pair.
The Source Application asks the Decoder application for the public key.
The Source application encrypts the symmetric key using the public key, and sends it back to the Decoder application.
The Decoder application uses the private key to decrypt the symmetric key.
The Decoder application gets data encrypted with the symmetric key from the Source Application.
The Decoder Application uses the exchanged symmetric key to decrypt the information it received.
There is just an example; but illustrates the basics of how to exchange data between two applications without any sensitive information transmitted over the wire. The symmetric key is not required at all; but is a very common pattern because RSA starts to introduce problems when encrypting large amounts of information. RSA is better to just encrypt an symmetric encryption key instead.
The short answer is: you can't. To decrypt messages you need the private key, that's the major principle of asymmetric cryptography.
You encrypt messages using someone's public key so that only the person in possession of the corresponding private key is able to decrypt them.
That's why the public key is called public - you may safely distribute it to the public so that they can encrypt messages to be read by you who is the sole owner of the corresponding private key.
The problem is that you're confusing encryption and signing.
Encryption is where anyone may write a message, but only the private key holder may read it. Signing is where anyone may read a message, but only the private key holder may write it.
When you call Decrypt, the RSACryptoServiceProvider is looking for encryption, that is, public write private read. Thus it looks for the private key.
You want to use the SignData and VerifyData functions to sign the payload so that people can't write it.
Related
I am working on a project where I need to implement encryption for sending quite large amounts of data through a tcp connection. I wish to write it in C#. The project is also a part of my learning, by implementing such encryption I'll get experience.
I've made some research and tested some things out and I've written how I think I have to go about implementing it below.
Basically, I'd like to ask if someone could please check what I've written below and warn me about any mistakes, security holes or incorrect statements I made. Pointing those out and explaining what I should do to rectify the problems is greatly appreciated.
We have host A and B connected via tcp.
Host A creates an instance of System.Security.Cryptography.RSA via RSA.Create(2048). By doing that, the RSA instance created random public and private keys. The private key can be used to decrypt data that was encrypted by the public key. The public key can be derived from the private key, but reverse is impossible.
A calls rsa.ExportParameters(includePrivateParameters: false); to export the public key and uses some serializer like XmlSerializer to serialize the RSAParameters object into bytes. A can then send the serialized object via TCP with no encryption to B.
B receives and deserializes the RSAParameters object and uses it for creating it's own instance of RSA: RSA.Create(deserializedRSApar);. The RSA object of B now has the public key of A's RSA and thus can encrypt data so that noone else but A can decrypt it because A has the private key.
B creates an instance of System.Security.Cryptography.Aes in CBC mode with 256 bit key and 16 byte IV.
B encrypts the AES key using the RSA object with the imported public key and sends it back to A.
A receives the encrypted data and decrypts it using it's private key and creates it's own instance of System.Security.Cryptography.Aes and sets the key it decrypted. The RSA objects can now be disposed because both hosts have the same AES key.
Further communication will be encrypted with AES in separate messages, the hosts need to use a different unpredictable IV for each of the messages, IV will be prepended in plain bytes before the encrypted data. I will also be putting the length of the given message after IV and before the encrypted data.. I've written it to write the length of the data before encryption because I've heard that AES does not change data length when encrypting.
A part of the method used for sending should look something like this:
aes.GenerateIV();
using(var writer = new BinaryWriter(networkStream))
using(var encryptor = aes.CreateEncryptor())
using(var cryptoWriter = new CryptoStream(networkStream, encryptor, CryptoStreamMode.Write))
{
writer.Write(aes.IV);
writer.Write((int)bytesToSend.Length);
cryptoWriter.Write(bytesToSend, 0, bytesToSend.Length);
}
And a part of the method for receiving should look something like this:
using(var reader = new BinaryReader(networkStream))
using(var decryptor = aes.CreateDecryptor(aes.Key, reader.ReadBytes(aes.IV.Length)))
using(var cryptoReader = new CryptoStream(networkStream, decryptor, CryptoStreamMode.Read))
{
int toRead = reader.ReadInt32();
if (toRead > bufferArray.Length)
throw new Exception("Throw an exception or handle in another way, unimportant for now");
cryptoReader.Read(bufferArray, 0, toRead);
}
I know I should be creating those BinaryReaders and Writers out of scope of the method, I wrote the usings here (as well as that needless (int) cast) to show my intent better than "There is a BinaryReader in the class this snippet of a method is in"
Another question I'd like to ask is: Does it matter how long the messages will be? Is there a limit to how big the messages should be? I don't intend on sending messages bigger than 32kB.
I have DER encoded RSA keypair created in Crypto++, as well as cipher. They are Base64Encoded string. I first decode the data from Base64 to byte array, but I am not sure how to load them into RSACryptoServiceProvider.
static void Main()
{
string pbkeystr = "mypublickey";
string pvkeystr = "myprivatekey";
string cipherstr = "mycipher";
byte[] pbkey = Convert.FromBase64String(pbkeystr);
byte[] pvkey = Convert.FromBase64String(pvkeystr);
byte[] cipher = Convert.FromBase64String(cipherstr);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//Set keys here..
//Decrypt the cipher using private key
rsa.Decrypt(pvkey, false);
}
There are no functions to set keys. The only thing I found was ImportParameters method, which takes RSAParameters class which consists of p, q, n, modulus, exponent etc. I don't have access to these.
Is there any way I can load the keys as string? How can I load the key into RSACryptoServiceProvider?
Is there any way I can load the keys as string? How can I load the key into RSACryptoServiceProvider?
From your other Crypto++ question, How to load Base64 RSA keys in Crypto++, it looks like you have only the public and private keys because you used DEREncode and BERDecode. That is, you have the RSA parameters, and not the subject public key info and the private key info. Your keys lack the OID identifiers and version numbers. Things are fine that way.
From Cryptographic Interoperability: Keys on the Code Project, you will need a C# class that parses the ASN.1/DER after you Base64 decode it. The CodeProject article provides a C# class called AsnKeyParser to read the ASN.1/DER and returns a RSAParameters to load into a CSP.
The code for the AsnKeyParser class is about 800 lines, and there are five other supporting files to make it all happen, so its not really appropriate to place it here. You should download it yourself. The file of interest is called CSInteropKeys.zip.
Once you wire-in the AsnKeyParser class, it will be as simple as the following for a RSA Public key. The private key will be similar, and the code is given on the CodeProject site.
// Your ASN.1/DER parser class
AsnKeyParser keyParser = new AsnKeyParser("rsa-public.der");
RSAParameters publicKey = keyParser.ParseRSAPublicKey();
// .Net class
CspParameters csp = new CspParameters;
csp.KeyContainerName = "RSA Test (OK to Delete)";
csp.ProviderType = PROV_RSA_FULL; // 1
csp.KeyNumber = AT_KEYEXCHANGE; // 1
// .Net class
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
rsa.PersistKeyInCsp = false;
rsa.ImportParameters(publicKey);
Linking to files on another site is frowned upon, but I don't know how to provide the information otherwise. There's too much source code involved to place in an answer.
For completeness, .Net does not make interop easy. They do not accept ASN.1/DER or PEM. Rather, .Net accepts some XML representation of the keys. I believe you can find it in RFC 3275, XML-Signature Syntax and Processing. Microsoft does not state that for you. I kind of pieced it together when I wrote the Code Project article.
Maybe we should add a class to Crypto++ to regurgitate XML in addition to ASN.1/DER and PEM.
I'm using C#.
I have a private key with size of 256 bytes,
I'm trying to use DPAPI as follow:
RijndaelManaged key = new RijndaelManaged();
byte[] buffer = new byte[32]
{
3,3,3,3,3,3,3,3,
5,5,5,5,5,5,5,57,
6,7,8,8,8,8,8,3,
1,33,36,39,39,39,31,37
};
byte[] secret = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// Encrypt a copy of the data to the stream.
byte[] output = ProtectedData.Protect(buffer, secret, DataProtectionScope.CurrentUser);
key.Key = output;//Throw an exception
My problem that output array thats return from ProtectData.Protect is with size that key.Key isn't supported (178 bytes) and when i'm trying to insert the output into that RijndaelManaged key i'm got an exception:
'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Additional information: Specified key is not a valid size for this algorithm.
How can i solve it? or any another solution to store my RijndaelManaged key?
I want also to access to my private key from another proccess
Thanks.
The output of ProtectedData.Protect is encrypted (not an encryption key). It grows to store whatever context and integrity checking it needs to prove that it can decrypt correctly. To get your original 256-bit key back you would need to call Unprotect.
If you're trying to derive a key (instead of encrypt it) use a key derivation routine, like PBKDF2 (in .NET this is implemented by Rfc2898DeriveBytes).
Alternatively, if you're trying to use DPAPI to protect data, it does that inherently; you don't get to customize a key for it... just pass it the data to protect.
I am trying to use the RSACryptoServiceProvider to encrypt/decrypt. Encrypting works fine, but the Decrypt method throws an exception with the message:
Unknown Error '80007005'.
This is the code:
Byte[] plainData = encoding.GetBytes(plainText);
Byte[] encryptedData;
RSAParameters rsap1;
Byte[] decryptedData;
using (RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider())
{
encryptedData = rsa1.Encrypt(plainData, false);
rsap1 = rsa1.ExportParameters(false);
}
using (RSACryptoServiceProvider rsa2 = new RSACryptoServiceProvider())
{
rsa2.ImportParameters(rsap1);
decryptedData = rsa2.Decrypt(encryptedData, false);
}
decryptedText = encoding.GetString(decryptedData, 0, decryptedData.Length);
Is anyone aware of a workaround?
Thanks!
Fixed the code! I guess I do not need to specify a container after all...
Byte[] plainData = encoding.GetBytes(plainText);
Byte[] encryptedData;
Byte[] decryptedData;
using (RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider())
{
RSAParameters rsap1 = rsa1.ExportParameters(false);
using (RSACryptoServiceProvider rsa2 = new RSACryptoServiceProvider())
{
rsa2.ImportParameters(rsap1);
encryptedData = rsa2.Encrypt(plainData, false);
}
decryptedData = rsa1.Decrypt(encryptedData, false);
}
decryptedText = encoding.GetString(decryptedData, 0, decryptedData.Length);
rsap1 = rsa1.ExportParameters(false);
By passing false to this method, you're choosing to not export the private key. Without the private key it will be difficult to decrypt the data. Try passing true to the export method.
When using RSA you need to understand the basics of key management. You did not specify what key container to use during encryption. What key do you expect to be used? The default user key? The machine key? Do you understand what the default user key and the machine keys are ? Not to mention the obvious question of why do you encrypt anything with RSA? RSA encryption is used solely for encrypting session keys, and there are dedicated key exchange protocols that take care of this out-of-the-box (stream oriented like TLS or document oriented like S/MIME). You should use one of these out-of-the-box protocols and not roll your own encryption scheme. You will screw up key management, that is guaranteed.
When you attempt to decrypt, does the decryptor has possession of the private key corresponding to the public key used during encryption?
See:
How to: Store Asymmetric Keys in a Key Container
Encrypting Data
Decrypting Data
Note that these are just simple code samples in MSDN and should never be used by anyone without a very deep understanding of cryptography, and specially key management.
I recommend you look into using a high level class like SslStream for encrypting data exchanges. For a document storage encryption scheme you better use the OS facilities or rely on ProtectedData class. Again, do not roll your own encryption unless you really know what you're doing (in which case you wouldn't be asking questions here).
Currently I am receiving the following error when using Java to decrypt a Base64 encoded RSA encrypted string that was made in C#:
javax.crypto.BadPaddingException: Not PKCS#1 block type 2 or Zero padding
The setup process between the exchange from .NET and Java is done by creating a private key in the .NET key store then from the PEM file extracted, created use keytool to create a JKS version with the private key. Java loads the already created JKS and decodes the Base64 string into a byte array and then uses the private key to decrypt.
Here is the code that I have in C# that creates the encrypted string:
public string Encrypt(string value) {
byte[] baIn = null;
byte[] baRet = null;
string keyContainerName = "test";
CspParameters cp = new CspParameters();
cp.Flags = CspProviderFlags.UseMachineKeyStore;
cp.KeyContainerName = keyContainerName;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
// Convert the input string to a byte array
baIn = UnicodeEncoding.Unicode.GetBytes(value);
// Encrypt
baRet = rsa.Encrypt(baIn, false);
// Convert the encrypted byte array to a base64 string
return Convert.ToBase64String(baRet);
}
Here is the code that I have in Java that decrypts the inputted string:
public void decrypt(String base64String) {
String keyStorePath = "C:\Key.keystore";
String storepass = "1234";
String keypass = "abcd";
byte[] data = Base64.decode(base64String);
byte[] cipherData = null;
keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(keyStorePath), storepass.toCharArray());
RSAPrivateKey privateRSAKey = (RSAPrivateKey) keystore.getKey(alias, keypass.toCharArray());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateRSAKey);
cipherData = cipher.doFinal(data);
System.out.println(new String(cipherData));
}
Does anyone see a step missing or where the padding or item needs to be changed? I have done hours of reading on this site and others but haven't really found a concrete solution.
You're help is vastly appreciated.
Thanks. -Matt
I had exactely the same problem and I finally find the solution!
I was stubborn using PKCS1Padding but I didn't manage to make it work.
The best result I got using "rsa.Encrypt(baIn, false)" on the C# side and "RSA/NONE/NoPadding" on the Java side was this kind of string : "☻?o+_>??5?l0Q*???*?R▲???♀7..." followed by my decrypted string. So in a way it got decrypted but since there is no padding specified, the data is shifted. So I tried all the paddings available in bouncycastle but I would alway get errors such as "block incorrect size" or "data hash wrong".
So I decided to start trying OAEP paddings and I finally managed to get it working by using "rsa.Encrypt(baIn, true)" on the C# side and "RSA/NONE/OAEPWithSHA1AndMGF1Padding" on the java side!
It worked for me, I hope it will work for you too! If it doesn't work make sure you're using the right key, very often the problem comes from the key.
Check that you have correctly exchanged the key.
Trying to decrypt with an incorrect key is indistinguishable from decrypting badly padded data.
I'm working through a similar problem operating between .Net and iPhone stuff in Objective - C, and I think the answer lies in this little gem from the RSACryptoServiceProvider documentation:
Unlike the RSA implementation in unmanaged CAPI, the RSACryptoServiceProvider class reverses the order of an encrypted array of bytes after encryption and before decryption. By default, data encrypted by the RSACryptoServiceProvider class cannot be decrypted by the CAPI CryptDecrypt function and data encrypted by the CAPI CryptEncrypt method cannot be decrypted by the RSACryptoServiceProvider class.
See here for more details: http://msdn.microsoft.com/en-us/library/s575f7e2(v=VS.90).aspx
I had the same problem when using Bouncy Castle 1.48 but it wasn't key-related. Instead, I found that I had to set the following system property:
-Dorg.bouncycastle.pkcs1.strict=false