Implement secure communication using RSA encryption in C# - c#

I want to implement a scenario where two endpoints can securely communicate with each other using public/private key encryption. The scenario is following:
For A to send a message to B:
A encrypts the message using A's private key.
A encrypts the message using B's public key.
A sends the message.
B receives the message.
B decrypts the message using A's public key.
B decrypts the message using B's private key.
B reads the message.
Here is what I have in C# using RSA encryption:
// Alice wants to send a message to Bob:
String plainText = "Hello, World!";
Byte[] plainData = Encoding.Default.GetBytes(plainText);
Byte[] cipherData = null;
RSACryptoServiceProvider alice = new RSACryptoServiceProvider();
RSACryptoServiceProvider bob = new RSACryptoServiceProvider();
var alicePrivateKey = alice.ExportParameters(true);
var alicePublicKey = alice.ExportParameters(false);
var bobPrivateKey = bob.ExportParameters(true);
var bobPublicKey = bob.ExportParameters(false);
RSACryptoServiceProvider messenger = new RSACryptoServiceProvider();
messenger.ImportParameters(alicePrivateKey);
cipherData = messenger.Encrypt(plainData, true);
messenger.ImportParameters(bobPublicKey);
cipherData = messenger.Encrypt(cipherData, true);
messenger.ImportParameters(alicePublicKey);
cipherData = messenger.Decrypt(cipherData, true);
messenger.ImportParameters(bobPrivateKey);
cipherData = messenger.Decrypt(cipherData, true);
String result = Encoding.Default.GetString(alice.Decrypt(cipherData, true));
Clearly, there is something wrong with the following lines:
messenger.ImportParameters(bobPublicKey);
cipherData = messenger.Encrypt(cipherData, true);
Which throws System.Security.Cryptography.CryptographyException with message { "Bad Length" }.
As I can see it is not able to encrypt the data using just the public part of bob's key.
Can someone throw some light on how to properly accomplish what I want to do in C#?

Two problems here:
A) Your protocol design is wrong. If you want to use RSA to exchange messages, the algorithm is this:
A encrypts message using B's public key
A sends the message
B decrypts the message using B's private key
(B does processing)
B encrypts message using A's public key
B sends the message
A decrypts the message using A's private key
and so on. Notice how A does not know B's private key, and vice versa. The public and private keys are related in such a way that a message encrypted with a public key (known to everyone) can only be decrypted with the corresponding private key (known only to the intendent recipient of the encrypted message). This is the whole point of RSA, actually.
As for implementation in C#, it is quite trivial to do with the Crypto classes once you really understand the underlying concepts. See for example here and here.
B) RSA is good for exchanging small amounts of data. It is meant for key exchange over an insecure channel without the need for a shared secret. For exchanging "normal" data, a symmetric algorithm such as AES is used. So the idea would be generating a random passphrase and IV from A, and sending that to B via RSA as discussed in A; after both parties know the passphrase and IV, they can just encrypt data using AES with the shared key.
This is what SSL does, and you should have a really good reason to roll your own instead of using a standard SSL stream.

RSA is used to encrypt data which are smaller than the key. You use symmetric key to encrypt large amount of data and then use the RSA to share the symmetric key.
For further details you might refer to this question : how to use RSA to encrypt files (huge data) in C#

Related

Encrypting the X509Certificate public Key

All, I am working on IDP initiated web sso for a project and the Service Provider has this requirement to RSA encrypt the AES-128 symmetric key(ECB with PKCS1 padding) and Base64 encode that before adding this to the AttributeStatement section of SAML 2.0 response .My understanding is RSA encryption is asymmetric and the key we have to encrypt is the Service Providers public key.But we dont have SP's public certificate ,so I decided to RSA encrypt our X509Certicate's public key
This is the code I have and it throws a Bad Length Cryptographic exception on calling the Encrypt method on RSACryptoServiceProvider class.Does anyone know what the issue?
using(var rsa = cert.PublicKey.Key as RSACryptoServiceProvider)
{
rsa.KeySize = 1024;
byte[] encryptedKey = rsa.Encrypt(cert.GetPublicKey(),false);
encodedPublicKey = Convert.ToBase64String(encryptedKey);
}
The amount of data that you are able to encrypt using RSA public key must be (I don't know the exact equation) less than the length of the public key.
In case of encrypting AES-128 you will be encrypting 128 bits.

Implementing Hybrid Encryption?

I already have an asymmetric algorithm implemented in an MVC C# Application, however I would like to modify the encryption method so that I make use of both symmetric and asymmetric encryption (AKA Hybrid encryption). Any idea how I can do this?
Asymmetric encrypt:
public string AsymmEncrypt(int accId, string input, string publickey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
publickey = new UserRepository().PublicKeyByAccountId(accId);
cspParams.KeyContainerName = publickey;
myAlg = new RSACryptoServiceProvider(cspParams);
byte[] cipher = myAlg.Encrypt(UTF8Encoding.UTF8.GetBytes(input), true);
return Convert.ToBase64String(cipher);
}
Asymmetric decrypt:
public string AsymmDecrypt(int accId, string input, string privatekey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
privatekey = new UserRepository().PrivateKeyByAccountId(accId);
byte[] cipher = myAlg.Decrypt(Convert.FromBase64String(input), true);
return UTF8Encoding.UTF8.GetString(cipher);
}
You should probably not try to reinvent the wheel here. The System.Security.Cryptography namespace in .net alrady provides a large array of cryptography functionality that is quite well vetted. Don't try to use your own Asymmetric functions to accomplish this.
If you want to do private key distribution through public key encryption, you should use something like RSAPKCS1KeyExchangeFormatter or maybe even RSAOAEPKeyExchangeFormatter if you have the flexibility to support PKCS#1v2
I would suggest reading how SSL or OpenPGP are implemented.
I'm not sure what part you are struggling with.
In short, the asymmetric algorithm is used for symmetric key exchange.
The symmetric algorithm is used for the bulk data (stream/block) crypto. You won't get it done with simply modifying your 2 functions, you will need to implement a handshake and key exchange.
Since you have an MVC.NET app, you can host it within a web server and gain HTTPS/SSL transport. You can also do the same with WCF. Any reason why aren't using what is provided by the underlying transport? You can even configure your application (web.config) to require client certificates.
PS: I agree about not re-inventing the wheel, even Microsoft's article that Erik linked to provides a warning about it.
Caution We recommend that you do not attempt to create your own key exchange method from the basic functionality provided, because many details of the operation must be performed carefully in order for the key exchange to be successful.

Best practice for encrypting on the client

I am currently using a web API that allows an "Encryption" option.
I can setup my account to have a "shared key", and using this key i should encrypt all data on the client before submitting to the server.
Details from their website:
Encryption Algorithm: DES
Block Mode: ECB
Padding: PKCS7 or PKCS5
(they are interchangeable)
"Shared key" in this meaning i believe is a symmetric algorithm - same key used to decrypt/encrypt, although i may be wrong on this one.
I would like to know what is the best practice of handling this scenario on the client side?
If my application's logic should be using this key to encrypt data, how is it safe from a hacker ?
Note that my app is written in C#, meaning it can be decompiled practically for free.
Unless your key is compromised, then the transmission of your data is safe – anyone eavesdropping on your client–server connection would not be able to decrypt your data unless they have your key.
Your main challenge lies in the secure storage of the key locally on both the client and the server. For this end, I would suggest looking into the Windows Data Protection API (DPAPI) exposed through the ProtectedData class in .NET.
If shared key means public key then you are, most probaly, using one of the algorithms known as asymmetric encryption. This way you are safe to hacker since public key can't be used to decrypt data.
If it's symmetric then all depends on how secure key is. You can store it separately from a program (so user can store it securely on a flash drive). So each user must have it's own key, it's not possible to use one symmetric key for all.
In this manner, client will encrypt the data with different key and server will decrypt with different key. This is called asymmetric encryption/decryption.
The .NET Framework provides the RSACryptoServiceProvider and DSACryptoServiceProvider classes for asymmetric encryption. These classes create a public/private key pair when you use the default constructor to create a new instance. Asymmetric keys can be either stored for use in multiple sessions or generated for one session only. While the public key can be made generally available, the private key should be closely guarded.
For example [VB.NET]:
Dim cspParam as CspParameters = new CspParameters()
cspParam.Flags = CspProviderFlags.UseMachineKeyStore
Dim RSA As System.Security.Cryptography.RSACryptoServiceProvider
= New System.Security.Cryptography.RSACryptoServiceProvider(cspParam)
The key information from the cspParam object above can be saved via:
Dim publicKey as String = RSA.ToXmlString(False) ' gets the public key
Dim privateKey as String = RSA.ToXmlString(True) ' gets the private key
The above methods enable you to convert the public and / or private keys to Xml Strings.
And of course, as you would guess, there is a corresponding FromXmlString method to get them back.
So to encrypt some data with the Public key. The no-parameter constructor is used as we are loading our keys from XML and
do not need to create a new cspParams object:
Dim str as String = "HelloThere"
Dim RSA2 As RSACryptoServiceProvider = New RSACryptoServiceProvider()
' ---Load the private key---
RSA2.FromXmlString(privateKey)
Dim EncryptedStrAsByt() As Byte =RSA2.Encrypt(System.Text.Encoding.Unicode.GetBytes(str),False)
Dim EncryptedStrAsString = System.Text.Encoding.Unicode.GetString(EncryptedStrAsByt)
and as a "proof of concept", to DECRYPT the same data, but now using the Public key:
Dim RSA3 As RSACryptoServiceProvider = New RSACryptoServiceProvider(cspParam)
'---Load the Public key---
RSA3.FromXmlString(publicKey)
Dim DecryptedStrAsByt() As Byte =RSA3.Decrypt(System.Text.Encoding.Unicode.GetBytes(EncryptedStrAsString), False)
Dim DecryptedStrAsString = System.Text.Encoding.Unicode.GetString(DecryptedStrAsByt)

C# Private/Public Encryption using AES

I am trying to figure out how to make public/private keys that are AES encrypted. I'd like to be able to use it like so:
byte[] BytesToEncrypt = { 0x01, 0x02, 0x03, 0x04, 0x05 };
byte[] PublicKey;
byte[] PrivateKey;
byte[] EncryptedBytes;
byte[] UnencryptedBytes;
PrivateKey = CreatePrivateKey();
PublicKey = CreatePublicKey(PrivateKey);
EncryptedBytes = EncryptBytes(PrivateKey);
// This line should return unencrypted bytes
UnencryptedBytes = UnencryptBytes(EncryptedBytes, PrivateKey);
// This line should also return the unencrypted bytes
UnencryptedBytes = UnencryptBytes(EncryptedBytes, PublicKey);
How can I implement something like this? I've seen public/private encryption, but all the examples I've seen seem to use RSA encryption. I want to use AES. Is this possible?
AES is an algorithm for symmetric-key encryption, so it doesn't make sense to talk about public and private AES keys. If your AES key is public there is no security at all.
You can encrypt an RSA public key using AES encryption if you wish, but it is unnecessary as it is not something you need to keep secret.
You can encrypt an RSA private key using AES encryption. This could be useful if you want to password protect your key so that if your computer is stolen they cannot use your key.
You can also use an asymmetric encryption to transfer a symmetric key to another party and then afterwards communicate with them using symmetric encryption. TLS uses this principle. It can be useful because symmetric algorithms can be faster to compute, but sending a symmetric key to someone in plain text would be insecure so you need the asymmetric encryption to keep the symmetric key safe.
While AES encryption is symmetric, I assume that your goal is to encrypt data (and have to use AES for whatever reason), give that data to someone else, allow them to read it, but only if they have your public key.
You could consider this workflow:
Generate an AES encryption key.
Encrypt your data using AES encryption key.
Generate a public and private key.
Use the private key to encrypt your AES encryption key.
Distribute the AES encrypted data, your public key, and your AES key as encrypted by the private key.
This is similar to what SSL does (not exactly though because the there is a handshake procedure with data being encrypted using public keys, not private keys), but might meet your needs.
This workflow ensures that your AES key can only be discovered if someone has your public key, but given that they then have the correct AES key, doesn't prevent someone from replacing your original data with other data. That's what public/private key data signing is for.

How do I decrypt RSA data in C#.NET appropriately?

My server creates a RSACryptoServiceProvider and exports its parameters to a variable (RSAKeyInfo).
Then, the public key is sent to the client, and the client encrypts something with that public key.
Now, I need to be able to decrypt this very data when sent back to the server - hence why RSA is useful in my case.
However, I get a "Bad Data" exception when trying to recreate a RSACryptoServiceProvider with imported parameters from the first RSACryptoServiceProvider created previously.
... Code might be clearer.
Creating the crypto:
class Cryptograph
{
public Cryptograph()
{
this.RSAKeyInfo = new RSACryptoServiceProvider(2048, new CspParameters(1)).ExportParameters(true);
}
}
Accessing it later for decryption:
byte[] encrypted = ...;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(this.Cryptograph.RSAKeyInfo);
byte[] decrypted = rsa.Decrypt(encrypted, false);
Console.WriteLine(Utilities.ByteArrayToHexString(decrypted));
I get the "Bad Data" exception at this line:
byte[] decrypted = rsa.Decrypt(encrypted, false);
What am I doing wrong? How can I do it properly?
Thank you :)
P.S.: Please don't send MSDN or obvious Google results links, I've read all these pages and still can't get it to work.
When something is encrypted with a public key, you need to use the private key for the decryption. I don't see where you are using the private key for decryption.
I realize you have already read this, but you may want to read the Encrypt page and this Decrypt page, and make certain that you are following the steps:
http://msdn.microsoft.com/en-us/library/te15te69.aspx
Unless you are encrypting very short messages, such as a password, RSA encryption should generally be used for encrypting a symmetric key, which is faster to encrypt/decrypt longer messages.
The size of what you can encrypt with a public key is tied to the length of the key.
I needed an encryption/decryption that used no padding, and C#.NET doesn't provide it by default. OpenSSL.NET will do the job, however, I'm stuck while trying to use it. (See this question if you want to help me make it work). :(

Categories