Encrypting the X509Certificate public Key - c#

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.

Related

Update x509Certificate2 with correct Cryptographic Service Provider in Bouncy Castle

I am creating x509Certificate2 using CertificateGenerator.GenerateCertificate from this blog post of Wiktor Zychla (http://www.wiktorzychla.com/2012/12/how-to-create-x509certificate2.html)
Bouncy Castle crypto library is used to generate the certificate file. I need stronger signature algorithm to be used, so instead of SHA1withRSA (one in example) I am using SHA256withRSA.
Certificate is generated and exported successfully to .pfx file. When using the certificate later on I get error Invalid algorithm specified.
When run certutil -dump mycert.pfx, I see incorrect Cryptographic Service Provider (CSP) is set: Microsoft Base Cryptographic Provider v1.0
...
---------------- End Nesting Level 1 ----------------
Provider = Microsoft Base Cryptographic Provider v1.0
...
How can I tell Bouncy Castle API to use different CSP? The Microsoft Enhanced RSA and AES Cryptographic Provider, that actually can deal with SHA256withRSA.
There is very little resources on Bouncy Castle and C#, so any link to some documentation or related examples would be greatly appreciated.
List of CryptoAPI CSPs and algorithms, they support:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb931357(v=vs.85).aspx
The easiest way is to specify the CSP when you are importing the generated pfx. You can use this command
certutil -importPFX -csp "Microsoft Enhanced RSA and AES Cryptographic Provider" -v c:\yourpfx.pfx AT_KEYEXCHANGE,NoExport,NoProtect
which will
import into LocalMachine\My
set CSP to Microsoft Enhanced RSA and AES Cryptographic Provider
set private key usage to Exchange
set private key as non-exportable
set private key with no additional (password) protection
The CSP is windows specific field in PKCS#12 (PFX) and no one except windows is setting this. If you are using PFX from file new X509Certificate2(filename) then you have to change the private key. Convert PrivateKey property to RSACryptoServiceProvider and modify CspParameters (I don't have a snippet right now). Then set the modified RSACryptoServiceProvider back to PrivateKey property.
------- Edit
Here is the sample code that changes CSP on PFX read from file
// need to set exportable flag to be able to ... export private key
X509Certificate2 cert = new X509Certificate2(#"d:\test.pfx", "a", X509KeyStorageFlags.Exportable);
var privKey = cert.PrivateKey as RSACryptoServiceProvider;
// will be needed later
var exported = privKey.ToXmlString(true);
// change CSP
var cspParams = new CspParameters()
{
ProviderType = 24,
ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
};
// create new PrivateKey from CspParameters and exported privkey
var newPrivKey = new RSACryptoServiceProvider(cspParams);
newPrivKey.FromXmlString(exported);
// Assign edited private key back
cert.PrivateKey = newPrivKey;
// export as PKCS#12/PFX
var bytes = cert.Export(X509ContentType.Pfx, "a");

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.

Implement secure communication using RSA encryption in 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#

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