SHA1 with RSA encryption: bad length error - c#

Probably I have several misunderstandings.
AFAIK signing a byte array with RSA-SHA1 generates a byte array (signature) of the same lenght as the RSA key used. Is that right?
From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?
Later on, in order to recover this hash (with or without padding schema on it) I would need to encrypt the signature with the public key. Is that right?
Something is broken in my logic because I'm not able to encrypt the signature with the public key.
Or my code is wrong. I'm using .net RSACryptoServiceProvider and it raises a bad data length error when trying to encrypt a signature... I assume encrypt means apply RSA using public key, right?
When trying to decrypt it raises a Key Not found exception. As expected because I only have the public key.
EDIT:
Given a byte array and RSACryptoServiceProvider I could Encrypt, Decrypt and SignData. I thought that SignData (without padding schema to simplify the question) is a shortcut of apply SHA, then Decrypt. For Encrypt I mean applying the RSA formula using public key as input, and for Decrypt I mean applying the RSA formula (the very same formula) using private key as input. Are this definitions ok?
EDIT2:
For exemple have a look at the next signed xml: http://www.facturae.gob.es/formato/Versiones/factura_ejemplo2_32v1.xml
And the next powershell script:
$signb64="oYR1T06OSaryEDv8VF9/JgWmwf0KSyOXKpBWY4uAD0YoMh7hedEj8GyRnKpVpaFanqycIAwGGCgl vtCNm+qeLvZXuI0cfl2RF421F8Ay+Q0ani/OtzUUE49wuvwTCClPaNdhv2vfUadR8ExR7e/gI/IL 51uc3mEJX+bQ8dxAQ2w=";
$certB64="MIIDtDCCAx2gAwIBAgICAIcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMCRVMxDzANBgNVBAgT Bk1hZHJpZDEPMA0GA1UEBxMGTWFkcmlkMQ4wDAYDVQQKEwVNSVR5QzEbMBkGA1UECxMSTUlUeUMg RE5JZSBQcnVlYmFzMRQwEgYDVQQDEwtDQSB1c3VhcmlvczAeFw0wOTEwMTUxNjA5MzRaFw0xMDEw MTUxNjA5MzRaMHExCzAJBgNVBAYTAkVTMQ8wDQYDVQQIEwZNYWRyaWQxDzANBgNVBAcTBk1hZHJp ZDEOMAwGA1UEChMFTUlUeUMxGzAZBgNVBAsTEk1JVHlDIEROSWUgUHJ1ZWJhczETMBEGA1UEAxMK VXN1YXJpbyA1NDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAroms65axKuQK18YDfD/x6DIn 0zKZ+6bv1K2hItJxel/JvU3JJ80/nY5o0Zbn+PrvlR2xF3poWYcPHLZpesgxhCMfnP7Jb5OUfceL g44m6T9P3PG1lSAZs3H6/TabyWGJy+cNRZMWs13KnB9fDAjJ5Jw0HVkwYNwmb1c7sHCuyxcCAwEA AaOCAVgwggFUMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTYhqU2tppJoHl+S1py BOH+dliYhzCBmAYDVR0jBIGQMIGNgBT1oWqod09bsQSMp35I8Q6fxXaPG6FypHAwbjEPMA0GA1UE CBMGTWFkcmlkMQ8wDQYDVQQHEwZNYWRyaWQxDjAMBgNVBAoTBU1JVHlDMRswGQYDVQQLExJNSVR5 QyBETkllIFBydWViYXMxEDAOBgNVBAMTB1Jvb3QgQ0ExCzAJBgNVBAYTAkVTggEDMAkGA1UdEQQC MAAwNgYDVR0SBC8wLYYraHR0cDovL21pbmlzdGVyLThqZ3h5OS5taXR5Yy5hZ2UvUEtJL0NBLmNy dDA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vbWluaXN0ZXItOGpneHk5Lm1pdHljLmFnZS9QS0kv Y3JsLmNybDANBgkqhkiG9w0BAQsFAAOBgQAhAN/KVouQrHOgd74gBJqGXyBXfVOeTVW+UTthhfCv DatXzTcrkYPQMfBAQMgGEa5KaQXcqKKhaoCUvrzFqE0HnAGX+ytX41oxZiM2fGNxRZcyUApLEX67 m8HOA/Cs2ZDlpU2W7wiOX5qr+ToTyfXsnRwPWvJ8VUmmXwyMEKcuzg==";
$signb=[System.Convert]::FromBase64String($signB64);
$certb=[System.Convert]::FromBase64String($certB64);
$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList #(,$certb)
$rsacsp = [System.Security.Cryptography.RSACryptoServiceProvider] $cert.PublicKey.Key;
$signb.Length*8;
$rsacsp;
$rsacsp.Encrypt($signb,0);
I tried:
$rsacsp.Encrypt($signb,[System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1);
instead of
$rsacsp.Encrypt($signb,0);
But I always get a bad length error:
Exception calling "Encrypt" with "2" argument(s): "Bad Length.
EDIT 3:
After reading, I can see my main issue was "From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?".
RSA sign (with a n bits key length) could be viewed as an operation that takes an arbitraty byte array and outputs n bits. In order to do that, it uses a hash function like SHA1 that takes an arbitrary byte array and produces a fixed output (160 bits for SHA1). Now in theory I could "encrypt" with the private key but then the output would be 160 bits long too it is not the way RSA is implemented. RSA Signing needs to apply padding function after the hash in order to produces an n bits text before "encrypting" it.
Another source of confusion is the meaning of the Encrypt method of .NET RSACryptoProvider. It turns out that this method has two parameters: a byte array and a flag indicating the padding function. It takes the byte array, applies the padding and then "encrypts" with the public key. It is of no use for a signature scenario. The operations decrypt and encrypt in RSACryptoProvider are not simmetrical. You can "decrypt" whatever has been "encrypt", but not the other way around.
At the end the confusion lies in that "atomic" functions used when encrypting/decrypting and the ones used when signin are the same, but they are used in incompatible ways.

AFAIK signing a byte array with RSA-SHA1 generates a byte array (signature) of the same lenght as the RSA key used. Is that right?
Usually yes, although the size will of course be encoded as octet stream (aka byte array) it is possible that the size of the signature is actually up to 7 bits larger. The key size is normally a multiple of 8 (bits) so this doesn't come up much.
From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?
No, you should never perform modular exponentiation in RSA without padding; a padding scheme is required for security. Note that you should not talk about encryption here. Encryption is used to provide confidentiality. That RSA signature generation and encryption both uses modular exponentiation - although with different keys - doesn't mean one equates the other.
It is important to note that the padding scheme for PKCS#1 v1.5 encryption is different from the one used for signature generation. Furthermore there are also the newer OAEP padding scheme for encryption and the PSS padding scheme for signature generation which are rather distinct.
Later on, in order to recover this hash (with or without padding schema on it) I would need to encrypt the signature with the public key. Is that right?
Perform modular exponentiation and then verify the result, yes. But as the verification requires verifying the padding in a secure way you should really let an API handle this.
Something is broken in my logic because I'm not able to encrypt the signature with the public key.
Try something written for verification instead, like the method VerifyHash as seen in this example.
You can try and find a raw RSA implementation to find out what is within the RSA signature. You should only do this to analyze the signature.
So if you "encrypt" the data with the public key (i.e. just perform modular exponentiation) you would get:
0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a05000414a2304127e2fe3b8a8203b219feafdd9b58558310
as result. This is clearly PCKS#1 v1.5 padding for signature generation. It includes an encoded hash value:
SEQUENCE(2 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER1.3.14.3.2.26
NULL
OCTET STRING(20 byte) A2304127E2FE3B8A8203B219FEAFDD9B58558310

Related

How to encrypt client private key using server public key rsa in C#

This is a piece of my code that encrypts a private key:
string pemContent = File.ReadAllText(pemPath);
csp.ImportFromPem(pemContent);
string test = rsa.GetPrivateKey();
var data = Encoding.UTF8.GetBytes(test);
var cypher = csp.Encrypt(data, false);
Console.WriteLine(Convert.ToBase64String(cypher));
This is the GetPrivateKey() function:
public string GetPrivateKey()
{
return rsa.ToXmlString(true);
}
I get this error:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'Bad Length.'
I know by now that private key is to big to encrypt it with client public key and the question is how to make is possible?
I can't find anything similar to what I am doing, the only clue I have is that wannary used the same technique while its file encryption process.
I use RSACryptoServiceProvider to handle rsa encryption.
EDIT:
Maybe i should describe my case more in detail. I am building a simple ransomware (i (i am a cybersecurity enthusiast and i do it just to learn how this viruses works in depth, so it's for educational purposes only). More advanced ransomware uses hybrid encryption. The scheme i am trying to implement is well described in this video. I am stuck in the last step which is encrypting client private key with server public key.
I'm presuming RSA here, it's not directly in the question, but it can be concluded from the code. Also, because of the second parameter of Encrypt being false, I'll assume PKCS#1 v1.5 padding.
There are two ways to do this. One you have already mentioned, and it is the best option: use hybrid encryption. You first create a random encryption key, encrypt the RSA key, and then encrypt that key.
The second way is to simply use a larger RSA key pair for the server. PKCS#1 v1.5 padding has a minimum overhead of 11 bytes, 8 of which are non-zero random. It's better to use 16 bytes of random data though, so then you'd have 19 bytes / 152 bits of overhead. For more information on the overhead of RSA encryption see my answer here.
The encoded private key needs to be in the remaining bits. Now it is best to use the minimum amount of bits to encode the private key. The best way to do this is to encode the modulus and only the private exponent (i.e. without the CRT parameters). Each of these will take as many bits as the key size if you use a constant sized, unsigned big endian encoding. So the key pair needs to be klen * 2 + 152 bits.
Note that this is not necessarily the best option as there could be schemes that allow you to never generate the private key on the client in the first place, until the private key needs to be released for decryption that is.

Is MGF1 padding assumed in System.Security.Cryptography.RSA.Encrypt method with RSAEncryptionPadding.OaepSHA256?

I need to encrypt plain bytes with RSA public key using OaepSHA256 and MGF1 padding. So I figured that I can write following code (using .net framework 4.7):
var encryptionCert = new X509Certificate2(certBytes);
using (var rsaPublicKey = encryptionCert.GetRSAPublicKey()) // Get an instance of RSA derived class
{
var encryptedKeyBytes = rsaPublicKey.Encrypt(plainBytes, RSAEncryptionPadding.OaepSHA256);
}
I have no idea if MGF1 padding and Optimal Asymmetric Encryption Padding (OAEP) are related or not.
Here are my questions:
In .net framework, does the above code automatically account for MGF1 padding (in addition to supplied OAEP)?
If not, then what are my options to achieve my goal (other than going to bouncy castle library)?
OAEP is padding scheme, which needs 2 hash functions with different properties to operate. One hash function should map arbitrary sized input to fixed size output. This type of hash functions are well known, SHA-256, MD5 and so on are all of this type. Specification allows different functions to be used for OAEP padding, such as SHA-256, SHA-1 and so on.
Another hash function should map arbitrary sized input to arbitrary sized output. Such hash function is called "mask generation function" (MGF). The related RFC defines only one such function, MGF1:
One mask generation function is given here: MGF1, which is based on a
hash function.
...
Future versions of this document may define other mask generation
functions.
Because there is just one defined mask generation function, the .NET api does not allow you to choose it (nothing to choose from) and just always uses it (MGF1) when you use RSA with OAEP padding. But, it is possible to parameterize MGF1 with a hash. For example see the MGF1ParameterSpec class in Java SE. It seems the .NET API always uses a particular hash function, not clear if it is SHA-1 or SHA-256.

Public key decyption using RSACryptoServiceProvider [duplicate]

I'm having a problem with C# encrypting and decrypting using RSA. I have developed a web service that will be sent sensitive financial information and transactions. What I would like to be able to do is on the client side, Encrypt the certain fields using the clients RSA Private key, once it has reached my service it will decrypt with the clients public key.
At the moment I keep getting a "The data to be decrypted exceeds the maximum for this modulus of 128 bytes." exception. I have not dealt much with C# RSA cryptography so any help would be greatly appreciated.
This is the method i am using to generate the keys
private void buttonGenerate_Click(object sender, EventArgs e)
{
string secretKey = RandomString(12, true);
CspParameters param = new CspParameters();
param.Flags = CspProviderFlags.UseMachineKeyStore;
SecureString secureString = new SecureString();
byte[] stringBytes = Encoding.ASCII.GetBytes(secretKey);
for (int i = 0; i < stringBytes.Length; i++)
{
secureString.AppendChar((char)stringBytes[i]);
}
secureString.MakeReadOnly();
param.KeyPassword = secureString;
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);
rsaProvider = (RSACryptoServiceProvider)RSACryptoServiceProvider.Create();
rsaProvider.KeySize = 1024;
string publicKey = rsaProvider.ToXmlString(false);
string privateKey = rsaProvider.ToXmlString(true);
Repository.RSA_XML_PRIVATE_KEY = privateKey;
Repository.RSA_XML_PUBLIC_KEY = publicKey;
textBoxRsaPrivate.Text = Repository.RSA_XML_PRIVATE_KEY;
textBoxRsaPublic.Text = Repository.RSA_XML_PUBLIC_KEY;
MessageBox.Show("Please note, when generating keys you must sign on to the gateway\n" +
" to exhange keys otherwise transactions will fail", "Key Exchange", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Once i have generated the keys, i send the public key to the web service which stores it as an XML file.
Now i decided to test this so here is my method to encrypt a string
public static string RsaEncrypt(string dataToEncrypt)
{
string rsaPrivate = RSA_XML_PRIVATE_KEY;
CspParameters csp = new CspParameters();
csp.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(csp);
provider.FromXmlString(rsaPrivate);
ASCIIEncoding enc = new ASCIIEncoding();
int numOfChars = enc.GetByteCount(dataToEncrypt);
byte[] tempArray = enc.GetBytes(dataToEncrypt);
byte[] result = provider.Encrypt(tempArray, true);
string resultString = Convert.ToBase64String(result);
Console.WriteLine("Encrypted : " + resultString);
return resultString;
}
I do get what seems to be an encrypted value. In the test crypto web method that i created, i then take this encrypted data, try and decrypt the data using the clients public key and send this back in the clear. But this is where the exception is thrown. Here is my method responsible for this.
public string DecryptRSA(string data, string merchantId)
{
string clearData = null;
try
{
CspParameters param = new CspParameters();
param.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);
string merchantRsaPublic = GetXmlRsaKey(merchantId);
rsaProvider.FromXmlString(merchantRsaPublic);
byte[] asciiString = Encoding.ASCII.GetBytes(data);
byte[] decryptedData = rsaProvider.Decrypt(asciiString, false);
clearData = Convert.ToString(decryptedData);
}
catch (CryptographicException ex)
{
Log.Error("A cryptographic error occured trying to decrypt a value for " + merchantId, ex);
}
return clearData;
}
If anyone could help me that would be awesome, as i have said i have not done much with C# RSA encryption/decryption.
Allow me a bit of terminology. There is asymmetric encryption and there is digital signature.
Asymmetric encryption is about keeping confidentiality. Some sensitive data is transformed into something unreadable, save for the entity who knows the decryption key. The decryption key is necessarily the private key: if the decryption key is the public key, then everybody can decrypt the data (the public key is, well, public) and there is no confidentiality anymore. In asymmetric encryption, one encrypts with the public key and decrypts with the corresponding private key.
Digital signatures are meant to prove integrity. Someone computes a kind of keyed checksum over the data, in such a way that the link between the checksum and the data can be verified later. This is a "signature" only because the power to compute that checksum requires knowledge of something which is not public -- in plain words, signing uses the private key. Verification, however, should be doable by anybody, and thus use the public key.
A fair bit of confusion is implied by the fact that "the" RSA algorithm is actually a mathematical operation which can be declined into both an asymmetric encryption system, and a digital signature system. The confusion is further enhanced by the RSA standard, aka PKCS#1, which implicitly relies on how RSA digital signatures were first described, i.e. as a "reversed encryption" ("the signer encrypts the data with his private key"). Which leads to things like RSA signatures called "sha1WithRSAEncryption". This is quite unfortunate.
Therefore, you must first decide whether you want confidentiality or signatures. For confidentiality, for data sent from clients to the server, the server shall own a private key, and the clients use the server public key to encrypt the data. For signatures, each client shall have his own private key and use it to sign the data, and the server verifies the signatures. From your description I cannot tell what you are really after, thanks to the confusion I allude to above.
Also, there is something called authentication which may look like digital signatures, but is weaker. The point of signatures is than anybody can verify the signature. In particular, the signature can be shown to a judge and thus serve as legal weapon against the signer (the signature is legally binding -- at least if you do it right, and in the current state of regulations over electronic signatures, this is not easy). In most situations you only need something weaker and simpler, in which the server is convinced that it talks to the right client, but cannot afterwards convince anybody else that this client was really there. Any web site with user passwords is using such authentication.
With that being said...
RSA asymmetric encryption covers only short messages. For a 1024-bit RSA key (i.e. a key where the most important part, the "RSA modulus", is a big number with a value between 2^1023 and 2^1024, and encrypted messages will be of length 128 bytes), the maximum size of an encrypted message is 117 bytes (that's the actual source of your error message). When we want to send longer messages, we use an hybrid system, in which we only encrypt a small bunch of random bits (say 128 bits) and use that bunch as a key for a symmetric encryption system (e.g. AES) which can process much longer messages (and much faster, too).
RSA signatures, similarly, can be computed only on short messages, hence the PKCS#1 standard mandates that a signature is actually computed over a hash value. The hash value is the output of a specific hash function, which is computed over the message to sign. The hash function has a fixed-sized output (e.g. 256 bits for SHA-256) but accepts input messages of (almost) arbitrary length. Hash functions are public (there is no key in them) and, for proper security, must have some special properties. SHA-256 is, right now, not a bad choice. SHA-1 (a predecessor of SHA-256) has been proven to have some weaknesses and should be avoided. MD5 has (a kind-of uncle of SHA-1) has bigger weaknesses and shall not be used.
Proper use of asymmetric encryption, especially in an hybrid scheme, and digital signatures, is trickier than what the text above may suggest. It is awfully easy to get it wrong at some point, invisibly, i.e. the code will appear to work but will leak data useful for an attacker. The right way to use asymmetric encryption or digital signatures is to rely on existing, well-thought protocols. A protocol is an assembly of cryptographic elements into a coherent system, where leaks are taken care of. The prime example is TLS, also known as SSL. It is a protocol which ensures confidential data transmission, with integrity and authentication (possibly mutual authentication). The HTTPS protocol is a mixture of HTTP and SSL. The bright side is that HTTPS has existing implementations, notably in C#. The code which is easiest to implement and debug is the code which has already been implemented and debugged. So use HTTPS and you will live longer and happier.
I understand why you are asking the question. The problem is that RSA is not used like a typical block cypher (like AES or 3DES) that encrypts 8 bytes at a time, all day long. RSA is a math operation that returns the remainder of a division (the modulus). Back in grade school, when you learned long division, remember that the remainder can never be greater than the divisor:if you are dividing 20 by 7, your remainder is 6. No matter what integer you divide by 7, the remainder cannot be greater than six.
RSA math is the same way. For example, if you are using a 1024-bit RSA public key, the remainder can never be greater than 2^1024, which is only 128 bytes. So you can only encrypt 128 bytes at a time with this key. (That's one reason we measure the size of RSA keys by the number of bits.)
Technically you could use this RSA key in a loop to encrypt 128 byte chunks of your data at a time. In reality, we almost never do this because RSA math is BIG and SLOW. Instead, we use what is called "two-phase" encryption. We use RSA to encrypt only a short "session key", and then use that session key in a fast symmetric-keyed block cypher (like AES) to encrypt the actual data.
The whole protocol is:
Obtain the RSA public key of your destination. This is often delivered embedded in a certificate; if it is, be sure to validate the certificate to make sure the key is genuine. Let's say the RSA key is 2048 bits long.
Generate a cryptographically strong pseudo-random number to use as a key for the block cypher (you need 256 bits as the key for AES-256, for example.) Note that 256 < 2048, the max that RSA-2048 can encrypt at once. We call this random number the "session key".
Encrypt the session key using the RSA 2048-bit public key. It will give you 2048 bits of encrypted session key. Note that this operation is very slow.
Encrypt all the secret data using AES-256, using the session key. Note that this is much faster than step 3.
Bundle the public key ID from the certificate, the RSA encrypted session key, and the AES encrypted data together. I'd also tag it with a format identifier and version number, so you know what format it is in and how to decrypt it.
Send the bundle to the destination.
At the destination you use the format identifier and version to take apart the bundle.
Retrieve the private key whose identity is in the public key ID field.
Use this private key in RSA to decrypt the session key.
Use the session key in AES to decrypt the data.
If you are going to do this, you should know that it is exactly what the CMS (PKCS#7) format is for. I would encourage you to learn about the standard and adopt it, rather than trying to invent your own format. Microsoft's CSP supports it, so it should be easy.
If you don't follow a standard you will have to make your own decisions about things like "what format should the AES key bits be in in the RSA encryption process?" More likely, you would almost certainly make security mistakes, weakening your system. Besides, you will find that tools such as the CSP will be very difficult to work with if you don't follow a standard.
In DecryptRSA, is "data" base 64 encoded? If it is, you have to undo that first.
Honestly I think you shouldn't implement that routine yourself to protect "sensitive financial information", unless you have a lot of experience with cryptography. There are too many ways to make errors. Better use some ready solution - maybe SSL and certificates, or just PGP or GnuPG?
RSA is primarily used to validate secure hashes of data - rather than encrypting the data itself. So, given a large blob of data, you might use SHA512 to create a hash of that data, then use RSA to sign that hash.
You'll want to use a symmetric encryption algorithm for large blocks of data - something like AES or 3DES.
Managing secure transactions isn't easy and really ought to be left to those guys that spend all day and night thinking about it. If you're exposing the service as over the web, just use SSL which already encrypts and secures your data.
First decide what you are trying to protect against. If you "encrypt" something using the private key, anyone can "decrypt" it with the public key, since the public key is - well - public.
If you actually want to sign it, you should (as Paul Alexander explains) sign a hash with the private key which can then be verified on the server.
To encrypt data using RSA you should first generate a random symmetric key (f.x. AES), encrypt the key using a public key and encrypt the data using the symmetric key. You can then transmit the encrypted key together with the encrypted data to the holder of the private key, who can then first decrypt the encrypted key with the private key and then decrypt the data with the symmetric key.
You might also consider using SSL, but remember to carefully consider the authentication. You will probably need client authentication and have to decide which certificates to trust (you should not just blindly accept any certificate issued by Verisign).

Converting Laravel's AES 256 Encryptor to C#

I need to create the same results when using Crypt::Encrypt('secret') from Laravel, in C#.
I found this thread Rijndael 256 Encrypt/decrypt between c# and php?
and it seemed to be what I need, but I'm having some trouble with the third argument the, initialization vector :(.
Laravel using Rijndael AES to encrypt the data. All the user has to input is a secret key, in the config folder, that is totally random and 32 characters long.
The encyrption method looks like this:
public function encrypt($value)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
return base64_encode(json_encode(compact('iv', 'value', 'mac')));
}
The full Encryptor.php can be found here: http://pastebin.com/yfWLPxGn
Any idea as to what I would have to input to get the same results? :)
Initialization vector is an input that is typically random. So, algorithm always creates a different value using the same input, key and the different IV. If you'd like to generate same result using both PHP and C# code, you need to use the same IV value.
Laravel's encrypt() does not return the encrypted value only. The value encrypt() generates a base64 encoded string which has json encoded values of iv, mac and encrypted value.
So the steps you need to apply in your C# encode() method:
Encode the string using the code in the link you gave.
base64_encode() the encrypted value. We will use this value in the further steps.
Create MAC (Message Authentication Code) using base64_encoded IV as the value, encrypted value as the key and sha256 as the algorithm. Take a look at this one
Now we have encrypted value, mac and iv.
Create a json string like this:
{
iv: iv value (base64 encoded),
value: encrypted value (base64 encoded),
mac: mac value created in 3rd step
}
base64 encode your this json string.
You're all set.
You would want to not apply padding and not apply any specific mode of operation. There is a pseudo-mode called ECB which basically applies the bare cipher over many blocks and applies no padding. It requires full blocks to be used.
If you don't have a full block to encrypt, you need to figure out what padding mode is used.
If that doesn't work, then you need to figure out what mode and what initialization vector is used. An initialization vector is usually prepended to a message as a unique value that varies per message, as a way to prevent some mathematical attacks on bare ciphers applied over many blocks.

RSA Encrypt / Decrypt Problem in .NET

I'm having a problem with C# encrypting and decrypting using RSA. I have developed a web service that will be sent sensitive financial information and transactions. What I would like to be able to do is on the client side, Encrypt the certain fields using the clients RSA Private key, once it has reached my service it will decrypt with the clients public key.
At the moment I keep getting a "The data to be decrypted exceeds the maximum for this modulus of 128 bytes." exception. I have not dealt much with C# RSA cryptography so any help would be greatly appreciated.
This is the method i am using to generate the keys
private void buttonGenerate_Click(object sender, EventArgs e)
{
string secretKey = RandomString(12, true);
CspParameters param = new CspParameters();
param.Flags = CspProviderFlags.UseMachineKeyStore;
SecureString secureString = new SecureString();
byte[] stringBytes = Encoding.ASCII.GetBytes(secretKey);
for (int i = 0; i < stringBytes.Length; i++)
{
secureString.AppendChar((char)stringBytes[i]);
}
secureString.MakeReadOnly();
param.KeyPassword = secureString;
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);
rsaProvider = (RSACryptoServiceProvider)RSACryptoServiceProvider.Create();
rsaProvider.KeySize = 1024;
string publicKey = rsaProvider.ToXmlString(false);
string privateKey = rsaProvider.ToXmlString(true);
Repository.RSA_XML_PRIVATE_KEY = privateKey;
Repository.RSA_XML_PUBLIC_KEY = publicKey;
textBoxRsaPrivate.Text = Repository.RSA_XML_PRIVATE_KEY;
textBoxRsaPublic.Text = Repository.RSA_XML_PUBLIC_KEY;
MessageBox.Show("Please note, when generating keys you must sign on to the gateway\n" +
" to exhange keys otherwise transactions will fail", "Key Exchange", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Once i have generated the keys, i send the public key to the web service which stores it as an XML file.
Now i decided to test this so here is my method to encrypt a string
public static string RsaEncrypt(string dataToEncrypt)
{
string rsaPrivate = RSA_XML_PRIVATE_KEY;
CspParameters csp = new CspParameters();
csp.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(csp);
provider.FromXmlString(rsaPrivate);
ASCIIEncoding enc = new ASCIIEncoding();
int numOfChars = enc.GetByteCount(dataToEncrypt);
byte[] tempArray = enc.GetBytes(dataToEncrypt);
byte[] result = provider.Encrypt(tempArray, true);
string resultString = Convert.ToBase64String(result);
Console.WriteLine("Encrypted : " + resultString);
return resultString;
}
I do get what seems to be an encrypted value. In the test crypto web method that i created, i then take this encrypted data, try and decrypt the data using the clients public key and send this back in the clear. But this is where the exception is thrown. Here is my method responsible for this.
public string DecryptRSA(string data, string merchantId)
{
string clearData = null;
try
{
CspParameters param = new CspParameters();
param.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);
string merchantRsaPublic = GetXmlRsaKey(merchantId);
rsaProvider.FromXmlString(merchantRsaPublic);
byte[] asciiString = Encoding.ASCII.GetBytes(data);
byte[] decryptedData = rsaProvider.Decrypt(asciiString, false);
clearData = Convert.ToString(decryptedData);
}
catch (CryptographicException ex)
{
Log.Error("A cryptographic error occured trying to decrypt a value for " + merchantId, ex);
}
return clearData;
}
If anyone could help me that would be awesome, as i have said i have not done much with C# RSA encryption/decryption.
Allow me a bit of terminology. There is asymmetric encryption and there is digital signature.
Asymmetric encryption is about keeping confidentiality. Some sensitive data is transformed into something unreadable, save for the entity who knows the decryption key. The decryption key is necessarily the private key: if the decryption key is the public key, then everybody can decrypt the data (the public key is, well, public) and there is no confidentiality anymore. In asymmetric encryption, one encrypts with the public key and decrypts with the corresponding private key.
Digital signatures are meant to prove integrity. Someone computes a kind of keyed checksum over the data, in such a way that the link between the checksum and the data can be verified later. This is a "signature" only because the power to compute that checksum requires knowledge of something which is not public -- in plain words, signing uses the private key. Verification, however, should be doable by anybody, and thus use the public key.
A fair bit of confusion is implied by the fact that "the" RSA algorithm is actually a mathematical operation which can be declined into both an asymmetric encryption system, and a digital signature system. The confusion is further enhanced by the RSA standard, aka PKCS#1, which implicitly relies on how RSA digital signatures were first described, i.e. as a "reversed encryption" ("the signer encrypts the data with his private key"). Which leads to things like RSA signatures called "sha1WithRSAEncryption". This is quite unfortunate.
Therefore, you must first decide whether you want confidentiality or signatures. For confidentiality, for data sent from clients to the server, the server shall own a private key, and the clients use the server public key to encrypt the data. For signatures, each client shall have his own private key and use it to sign the data, and the server verifies the signatures. From your description I cannot tell what you are really after, thanks to the confusion I allude to above.
Also, there is something called authentication which may look like digital signatures, but is weaker. The point of signatures is than anybody can verify the signature. In particular, the signature can be shown to a judge and thus serve as legal weapon against the signer (the signature is legally binding -- at least if you do it right, and in the current state of regulations over electronic signatures, this is not easy). In most situations you only need something weaker and simpler, in which the server is convinced that it talks to the right client, but cannot afterwards convince anybody else that this client was really there. Any web site with user passwords is using such authentication.
With that being said...
RSA asymmetric encryption covers only short messages. For a 1024-bit RSA key (i.e. a key where the most important part, the "RSA modulus", is a big number with a value between 2^1023 and 2^1024, and encrypted messages will be of length 128 bytes), the maximum size of an encrypted message is 117 bytes (that's the actual source of your error message). When we want to send longer messages, we use an hybrid system, in which we only encrypt a small bunch of random bits (say 128 bits) and use that bunch as a key for a symmetric encryption system (e.g. AES) which can process much longer messages (and much faster, too).
RSA signatures, similarly, can be computed only on short messages, hence the PKCS#1 standard mandates that a signature is actually computed over a hash value. The hash value is the output of a specific hash function, which is computed over the message to sign. The hash function has a fixed-sized output (e.g. 256 bits for SHA-256) but accepts input messages of (almost) arbitrary length. Hash functions are public (there is no key in them) and, for proper security, must have some special properties. SHA-256 is, right now, not a bad choice. SHA-1 (a predecessor of SHA-256) has been proven to have some weaknesses and should be avoided. MD5 has (a kind-of uncle of SHA-1) has bigger weaknesses and shall not be used.
Proper use of asymmetric encryption, especially in an hybrid scheme, and digital signatures, is trickier than what the text above may suggest. It is awfully easy to get it wrong at some point, invisibly, i.e. the code will appear to work but will leak data useful for an attacker. The right way to use asymmetric encryption or digital signatures is to rely on existing, well-thought protocols. A protocol is an assembly of cryptographic elements into a coherent system, where leaks are taken care of. The prime example is TLS, also known as SSL. It is a protocol which ensures confidential data transmission, with integrity and authentication (possibly mutual authentication). The HTTPS protocol is a mixture of HTTP and SSL. The bright side is that HTTPS has existing implementations, notably in C#. The code which is easiest to implement and debug is the code which has already been implemented and debugged. So use HTTPS and you will live longer and happier.
I understand why you are asking the question. The problem is that RSA is not used like a typical block cypher (like AES or 3DES) that encrypts 8 bytes at a time, all day long. RSA is a math operation that returns the remainder of a division (the modulus). Back in grade school, when you learned long division, remember that the remainder can never be greater than the divisor:if you are dividing 20 by 7, your remainder is 6. No matter what integer you divide by 7, the remainder cannot be greater than six.
RSA math is the same way. For example, if you are using a 1024-bit RSA public key, the remainder can never be greater than 2^1024, which is only 128 bytes. So you can only encrypt 128 bytes at a time with this key. (That's one reason we measure the size of RSA keys by the number of bits.)
Technically you could use this RSA key in a loop to encrypt 128 byte chunks of your data at a time. In reality, we almost never do this because RSA math is BIG and SLOW. Instead, we use what is called "two-phase" encryption. We use RSA to encrypt only a short "session key", and then use that session key in a fast symmetric-keyed block cypher (like AES) to encrypt the actual data.
The whole protocol is:
Obtain the RSA public key of your destination. This is often delivered embedded in a certificate; if it is, be sure to validate the certificate to make sure the key is genuine. Let's say the RSA key is 2048 bits long.
Generate a cryptographically strong pseudo-random number to use as a key for the block cypher (you need 256 bits as the key for AES-256, for example.) Note that 256 < 2048, the max that RSA-2048 can encrypt at once. We call this random number the "session key".
Encrypt the session key using the RSA 2048-bit public key. It will give you 2048 bits of encrypted session key. Note that this operation is very slow.
Encrypt all the secret data using AES-256, using the session key. Note that this is much faster than step 3.
Bundle the public key ID from the certificate, the RSA encrypted session key, and the AES encrypted data together. I'd also tag it with a format identifier and version number, so you know what format it is in and how to decrypt it.
Send the bundle to the destination.
At the destination you use the format identifier and version to take apart the bundle.
Retrieve the private key whose identity is in the public key ID field.
Use this private key in RSA to decrypt the session key.
Use the session key in AES to decrypt the data.
If you are going to do this, you should know that it is exactly what the CMS (PKCS#7) format is for. I would encourage you to learn about the standard and adopt it, rather than trying to invent your own format. Microsoft's CSP supports it, so it should be easy.
If you don't follow a standard you will have to make your own decisions about things like "what format should the AES key bits be in in the RSA encryption process?" More likely, you would almost certainly make security mistakes, weakening your system. Besides, you will find that tools such as the CSP will be very difficult to work with if you don't follow a standard.
In DecryptRSA, is "data" base 64 encoded? If it is, you have to undo that first.
Honestly I think you shouldn't implement that routine yourself to protect "sensitive financial information", unless you have a lot of experience with cryptography. There are too many ways to make errors. Better use some ready solution - maybe SSL and certificates, or just PGP or GnuPG?
RSA is primarily used to validate secure hashes of data - rather than encrypting the data itself. So, given a large blob of data, you might use SHA512 to create a hash of that data, then use RSA to sign that hash.
You'll want to use a symmetric encryption algorithm for large blocks of data - something like AES or 3DES.
Managing secure transactions isn't easy and really ought to be left to those guys that spend all day and night thinking about it. If you're exposing the service as over the web, just use SSL which already encrypts and secures your data.
First decide what you are trying to protect against. If you "encrypt" something using the private key, anyone can "decrypt" it with the public key, since the public key is - well - public.
If you actually want to sign it, you should (as Paul Alexander explains) sign a hash with the private key which can then be verified on the server.
To encrypt data using RSA you should first generate a random symmetric key (f.x. AES), encrypt the key using a public key and encrypt the data using the symmetric key. You can then transmit the encrypted key together with the encrypted data to the holder of the private key, who can then first decrypt the encrypted key with the private key and then decrypt the data with the symmetric key.
You might also consider using SSL, but remember to carefully consider the authentication. You will probably need client authentication and have to decide which certificates to trust (you should not just blindly accept any certificate issued by Verisign).

Categories

Resources