I'm busy trying to port Java code that looks like this
Cipher rsa = Cipher.getInstance("RSA/ECB/nopadding");
rsa.init(Cipher.DECRYPT_MODE, RSAPrivateKey);
decryptedData = rsa.doFinal(data, 0, 128);
to C#, but as it seems the RSACryptoServiceProvider, forces you to either use OEAP or PKCS1 padding. I know no padding isn't secure, but in this case Im working with a closed source client, so I can't do anything about that. Is there any way around this padding issue?
You might want to get the code from BouncyCastle, http://www.bouncycastle.org/csharp/, and modify the code from the link below, and ensure that it can use the encryption that you list above.
http://www.java2s.com/Code/Java/Security/Whatisinbouncycastlebouncycastle.htm
BouncyCastle will help us to make nopadding RSA encryption.
public string RsaEncryptWithPublic(string clearText, string publicKey)
{
// analogue of Java:
// Cipher rsa = Cipher.getInstance("RSA/ECB/nopadding");
try
{
var bytesToEncrypt = Encoding.ASCII.GetBytes(clearText);
var encryptEngine = new RsaEngine(); // new Pkcs1Encoding (new RsaEngine());
using (var txtreader = new StringReader("-----BEGIN PUBLIC KEY-----\n" + publicKey+ "\n-----END PUBLIC KEY-----"))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
catch
{
return "";
}
}
also dont forget to put it at top:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
Related
This might be a possible duplicate but I am unable to fix it.
Below is my code in C# for tripleDES:
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
class MainClass {
public static void Main (string[] args) {
String encrypt="5241110000602040";
SymmetricAlgorithm sa= SymmetricAlgorithm.Create("TripleDES");
sa.Key= Convert.FromBase64String("FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0");
sa.IV=Convert.FromBase64String("YFKA0QlomKY=");
byte[] iba=Encoding.ASCII.GetBytes(encrypt);
MemoryStream mS=new MemoryStream();
ICryptoTransform trans=sa.CreateEncryptor();
byte[] buf= new byte[2049];
CryptoStream cs=new CryptoStream(mS,trans,CryptoStreamMode.Write);
cs.Write(iba,0,iba.Length);
cs.FlushFinalBlock();
Console.WriteLine(Convert.ToBase64String(mS.ToArray()));
}
}
Encrypted value is
Nj7GeyrbJB93HZLplFZwq5HRjxnvZSvU
I want to achieve the same thing with crypto-js library of nodejs. Here is nodejs code of what I tried:
var CryptoJS = require("crypto-js");
var text = "5241110000602040";
var key = "FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0";
var options = {
// mode: CryptoJS.mode.ECB,
// padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Hex.parse("YFKA0QlomKY=")
};
var textWordArray = CryptoJS.enc.Utf8.parse(text);
var keyHex = CryptoJS.enc.Hex.parse(key);
var encrypted = CryptoJS.TripleDES.encrypt(textWordArray, keyHex, options);
var base64String = encrypted.toString();
console.log('encrypted val: ' + base64String);
Expected output
Nj7GeyrbJB93HZLplFZwq5HRjxnvZSvU
Actual Output
NXSBe9YEiGs5p6VHkzezfdcb5o08bALB
Encrypted value in nodejs is different than C#. What am I doing wrong?
You differently decode key and iv.
In c# you use base64:
sa.Key= Convert.FromBase64String("FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0");
sa.IV=Convert.FromBase64String("YFKA0QlomKY=");
in node.js hex:
iv: CryptoJS.enc.Hex.parse("YFKA0QlomKY=")
var key = "FRSF1P3b6fHiW/DXrK8ZJks5KAiyNpP0";
var keyHex = CryptoJS.enc.Hex.parse(key);
Try to use base64 in both cases.
I try to encrypt a string (json) with Rijndael in C# and come up with a string, which I can offer to a PHP web service. This web service in turn decodes the string using the IV and masterkey (known to them). I have to write the C# code that can talk to the PHP service, I do not control/own the PHP service.
The PHP code for encrypting is as follows:
function encrypt($plaintext) {
$masterkey = 'masterKeyOfLength29Characters';
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $masterkey, $iv);
$crypttext = mcrypt_generic($td, $plaintext);
mcrypt_generic_deinit($td);
return base64_encode($iv.$crypttext);
}
$param = array("key" => "value");
$encryptedString = rawurlencode(encrypt(json_encode($param)))
The code above I'll have to convert to C#, so I can encrypt my JSON and offer it to the PHP web service.
There are two problems. The first was with the masterkey length, the second (might be related) is with the rawurlencode of the encrypted data (hard for me to test at this point).
var masterkey = "masterKeyOfLength29Characters";
var data = EncryptData(json, masterkey);
// Some code to URL Encode the data, I haven't gotten as far to test this
// since I can't encrypt with the key used in PHP, so I can't call the service
// to test the encoded string from my C# code.
data = HttpUtility.UrlEncode(data);
data = data.Replace("+", "%20");
public static string EncryptData(string json, string encryptionKey) {
Rijndael rj = Rijndael.Create();
rj.Mode = CipherMode.CBC;
rj.Padding = PaddingMode.PKCS7;
rj.BlockSize = 256;
rj.KeySize = 256;
rj.Key = Encoding.UTF8.GetBytes(encryptionKey); // ERROR here
rj.GenerateIV();
var encryptedJSON = EncryptStringToBytes(json, rj.Key, rj.IV);
var r1 = Convert.ToBase64String(rj.IV);
var r2 = Convert.ToBase64String(encryptedJSON);
return r1 + r2;
}
The EncryptStringToBytes does some checks and uses this code (plucked from the many examples on the internet):
using (Rijndael rijAlg = Rijndael.Create()) {
// Basically I do the same here as above, and I could also generate
// the IV here, but then I'd had to return it too. I know I can clean this
// code up quite a bit, but I'd rather focus on getting it to work first ;)
rijAlg.Mode = CipherMode.CBC;
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.BlockSize = 256;
rijAlg.KeySize = 256;
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
The error I'll get:
Specified key is not a valid size for this algorithm.
So, the problems in short:
1) How come the PHP code accepts the key of length 29 in the Rijndael 256 (CBC mode), and my C# doesn't? I've played around with the Mode, added the Padding later, set the KeySize (was 256 default already), and I just can't see what I'm doing wrong here.
2) When I use a key of length 32, this one is accepted and my code works. I can also decrypt it in C# (but can't test this in PHP). I would like to solve problem 1, and then continue on problem 2, but maybe someone can give me some understanding here. The encrypted string contains 1 '=' in the IV, and 2x '==' (at the end) in the encrypted json. I've read about padding and such, but I was wondering why no '=' signs are visible in the PHP examples I received. Again, maybe after fixing problem 1 this won't be an issue.
Many thanks for reading and I hope I'm not being too stupid here. After a day of trying yesterday I kind of get the feeling I've tried many different approaches and non seem to work.
Just thought I'd add a tiny bit to what #artjom-b has said.
Firstly, it does work :-)
But in addition you need to change your
rj.Padding = PaddingMode.PKCS7
to use
rj.Padding = PaddingMode.Zeros
Also, technically, your two functions aren't returning the same thing. The PHP returns base 64 of two concatenated bits of binary data whereas the C# returns a concatenation of separate b64 strings. The result will be different in the second half of the returned string.
EDIT: The rough and ready decryption routine:
public string DecryptRijndael(byte[] cipherText, string password, byte[] iv)
{
var key = new byte[32];
Encoding.UTF8.GetBytes(password).CopyTo(key, 0);
var cipher = new RijndaelManaged();
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.None;
cipher.KeySize = 256;
cipher.BlockSize = 256;
cipher.Key = key;
cipher.IV = iv;
byte[] plain;
using (var decryptor = cipher.CreateDecryptor())
{
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
cs.FlushFinalBlock();
plain = ms.ToArray();
}
}
}
return Encoding.UTF8.GetString(plain);
}
NB: All the caveats and warnings from Artjom B still apply.
You're using an old version of PHP which happily accepts keys that have an invalid length. Rijndael supports key sizes of 16, 24 and 32 bytes and nothing inbetween. The mcrypt extension in PHP silently pads the key with 0x00 bytes up to the next valid key size which is 32 bytes. You will have to do the same thing in C#:
byte[] key = new byte[32];
byte[] password = Encoding.UTF8.GetBytes(encryptionKey);
Array.Copy(password, key, password.Length);
rj.Key = key;
Keep in mind that in order to provide some security a key must have high entropy. A password is not a key and therefore doesn't provide much entropy, because of the limited character set and possible use words. Always derive a key from the password with available derivation functions such as Argon2, scrypt, bcrypt or PBKDF2 with a high cost factor/iteration count and a random salt.
You should also add authentication to your ciphertexts. Otherwise, an attacker might change the ciphertext without you knowing it. This either done by using an authenticated mode like GCM/EAX or running HMAC over the ciphertext to produce the authentication tag.
I am using aes for encryption/decryption of the text but sometime its giving me exact value after decryption while some times i am getting error. I referred to different answers over but didn't get the root cause of my problem .
private static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv)
{
// Declare the string used to hold the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new System.Security.Cryptography.RijndaelManaged())
{
//Settings
rijAlg.Mode = System.Security.Cryptography.CipherMode.CBC;
rijAlg.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
rijAlg.FeedbackSize = 128;
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
try
{
// Create the streams used for decryption.
using (var msDecrypt = new System.IO.MemoryStream(cipherText))
{
using (var csDecrypt = new System.Security.Cryptography.CryptoStream(msDecrypt, decryptor, System.Security.Cryptography.CryptoStreamMode.Read))
{
using (var srDecrypt = new System.IO.StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
catch
{
plaintext = "keyError";
}
}
return plaintext;
}
It throws error "Padding is invalid and cannot be removed"
I seen some suggestion like to remove padding but it didn't seems proper solution.
I am not able to find the cause behind this as sometimes it runs perfectly without throwing error .
Any help or suggestion is really appreciated.
For Encryption - The encryption is being done on to client side in js and passing encryped text to server.
var key = CryptoJS.enc.Utf8.parse("16 digit number here");
var iv = CryptoJS.enc.Utf8.parse("16 digit number here");
var EncryptedString = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse("entered string to encrypt"), key,
{ keySize: 128 / 8, iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
By using a similar encryption routine in .NET to the decryption function you give I was able to successfully round-trip plaintext to ciphertext and back to plaintext, so it seems that the decryption function itself is ok. It therefore seems very likely that the key and/or IV you're using to encrypt does not match byte-for-byte with the values you're using when decrypting.
Given that your encryption code is using the UTF-8 encoded version of string values to form the key and IV, it would be worth doing the same in your decryption code (using Encoding.UTF8.GetBytes()).
However, it would be worth noting that whilst this might resolve the immediate issue, it is in itself a bad practice to use string values directly for keys without some form of key-derivation process (e.g. Rfc2898DeriveBytes), and IVs should be generated randomly for every application of the encryption function. Those are just a few issues with your use of cryptography (and are independent of whether the code works or not).
Here is the code used to encrypt in coldfusion
<cfset strBase64Value = encrypt(strValue,24 character key,AES) />
It is generating encrypted values like 714FEA9A9A2184769CA49D5133F08580 which seems odd to me considering it is only uppercase and numbers.
What C# library should I use to properly decrypt it ?
Also looking at this information, it seems that by default it uses the UUEncode algorithm to encode.
Should I ask the encrypter to use Base64 as encoding parameter ?
It is generating encrypted values like 714FEA9A9A2184769CA49D5133F08580
Then they are using "Hex", not the default "UUEncode". Either "hex" or "base64" is fine. As long as you both agree upon the encoding, it does not really matter.
You can use RijndaelManaged to decrypt the strings. However, the default encryption settings for ColdFusion and C# differ slightly. With the encrypt function:
"AES" is short for "AES/ECB/PKCS5Padding"
"ECB" mode does not use an IV
Key strings are always base64 encoded
NB: Despite the name difference, for the SUN provider, PKCS5Padding (CF/Java) corresponds to PaddingMode.PKCS7 (C#). As mentioned in this thread, the "... SUN provider in Java indicate[s] PKCS#5 where PKCS#7 should be used - "PKCS5Padding" should have been "PKCS7Padding". This is a legacy from the time that only 8 byte block ciphers such as (triple) DES symmetric cipher were available."
So you need to ensure your C# settings are adjusted to match. With that in mind, just decode the encrypted text from hex and the key string from base64. Using the slightly ugly example in the API, just adjust the algorithm settings to match those used by the encrypt() function:
Encrypt with ColdFusion
<cfscript>
plainText = "Nothing to see";
// 128 bit key base64 encoded
keyInBase64 = "Y25Aju8H2P5DR8mY6B0ezg==";
// "AES" is short for "AES/ECB/PKCS5Padding"
encryptedText = encrypt(plainText, keyInBase64, "AES", "hex");
WriteDump( encryptedText );
// result: 8889EDF02F181158AAD902AB86C63951
</cfscript>
Decrypt with C#
byte[] bytes = SomeMethodToConvertHexToBytes( encryptedText );
byte[] key = Convert.FromBase64String( keyInBase64 );
string decryptedText = null;
using (RijndaelManaged algorithm = new RijndaelManaged())
{
// initialize settings to match those used by CF
algorithm.Mode = CipherMode.ECB;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
ICryptoTransform decryptor = algorithm.CreateDecryptor();
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decryptedText = srDecrypt.ReadToEnd();
}
}
}
}
Console.WriteLine("Encrypted String: {0}", encryptedText);
Console.WriteLine("Decrypted String: {0}", decryptedText);
Keep in mind you can (and probably should) adjust the settings, such as using the more secure CBC mode instead of ECB. You just need to coordinate those changes with the CF developer.
If anyone had similar problem with JAVA I just implemented encryption and decryption of string previously encrypted/decrypted in coldfusion with "Hex" and "tripledes". Here is my code:
private static final String PADDING = "DESede/ECB/PKCS5Padding";
private static final String UTF_F8 = "UTF-8";
private static final String DE_SEDE = "DESede";
private String secretKey;
public String encrypt(String message) throws Exception {
secretKey = getSecretKey();
final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.ENCRYPT_MODE, key);
final byte[] plainTextBytes = message.getBytes();
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return Hex.encodeHexString(cipherText);
}
public String decrypt(String keyToDecrypt) throws Exception {
secretKey = getSecretKey();
byte[] message = DatatypeConverter.parseHexBinary(keyToDecrypt);
final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
final Cipher decipher = Cipher.getInstance(PADDING);
decipher.init(Cipher.DECRYPT_MODE, key);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, UTF_F8);
}
I'm trying to use public key encryption for communication with a client and server. The server is supposed to generate a 1024-bit public key and send it to the client, where the client will use that key to send encrypted data back to the server. So far, I've initialized the RSACryptoServiceProvider with this:
RSACryptoServiceProvider rsaEncryption = new RSACryptoServiceProvider(1024);
Now, I'm aware that I can use ExportParameters to get the exponent and modulus from the RSACryptoServiceProvider. However, I'm wondering, how can I use this data to send a public key back to the client (which would also be using an RSACryptoServiceProvider), and how can the client use this data to encrypt something to send back to me?
Or am I doing this completely wrong?
Your logic seems ok and it seems that you only need some sample code.
using System;
using System.Security.Cryptography;
using System.Text;
namespace RSA
{
class Program
{
static void Main(string[] args)
{
try
{
var rsaServer = new RSACryptoServiceProvider(1024);
var publicKeyXml = rsaServer.ToXmlString(false);
var rsaClient = new RSACryptoServiceProvider(1024);
rsaClient.FromXmlString(publicKeyXml);
var data = Encoding.UTF8.GetBytes("Data To Be Encrypted");
var encryptedData = rsaClient.Encrypt(data, false);
var decryptedData = rsaServer.Decrypt(encryptedData, false);
Console.WriteLine(Encoding.UTF8.GetString(decryptedData));
Console.WriteLine("OK");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}
}
}
1 - Please use >= 2048 Bits in your key to be protected til ~2025.
The above is from 2012, but I came across it while trying to create a ssh-rsa key for Putty / Linux server connections.
I've just solved similar problem of creating a public ssh-rsa key in the proper format, to match PuttyGen.
For Microsoft .net RSACryptoServiceProvider it would look like this
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(3072);
byte[] sshrsa_bytes = Encoding.Default.GetBytes("ssh-rsa");
byte[] n = RSA.ExportParameters(false).Modulus;
byte[] e = RSA.ExportParameters(false).Exponent;
string buffer64;
using (MemoryStream ms = new MemoryStream())
{
ms.Write(ToBytes(sshrsa_bytes.Length), 0, 4);
ms.Write(sshrsa_bytes, 0, sshrsa_bytes.Length);
ms.Write(ToBytes(e.Length), 0, 4);
ms.Write(e, 0, e.Length);
ms.Write(ToBytes(n.Length+1), 0, 4); //Remove the +1 if not Emulating Putty Gen
ms.Write(new byte[] { 0 }, 0, 1); //Add a 0 to Emulate PuttyGen (remove it not emulating)
ms.Write(n, 0, n.Length);
ms.Flush();
buffer64 = Convert.ToBase64String(ms.ToArray());
}
string pubssh = string.Format("ssh-rsa {0} generated-key", buffer64);
You can see my Private Key I used for testing & the putty gen source code link https://www.cameronmoten.com/2017/12/21/rsacryptoserviceprovider-create-a-ssh-rsa-public-key/
I work at Microsoft but this is a personal answer not a Microsoft one.
Original Post for BouncyCastle (Link)