I have a client who needs us to use Blowfish ECB encryption with cipherMode 0, output 1. I have tried to solve for this, but I'm getting stuck. How do I fix my code?
Here are the full client instructions:
Algorithm: Blowfish
・ Mode: ECB
・ Padding: PKCS5Padding
*Initial vector is unnecessary because we use ECB mode.
Example
・Encrypt Key: 2fs5uhnjcnpxcpg9
→ Plain Text : 3280:99:20120201123050
→ Cipher Text : daa745f1901364c0bd42b9658db3db96336758cd34b2a576
* Please keep Cipher Text with 16 hexadecimal characters .
* Please generate Cipher Text without “salt”.
I need to write this in C#. Here's what I did, but it doesn't seem to be working:
string member_id = "3280";
string panelType = "99";
string RandomString = "20120201123050";
string encryptionKey = "2fs5uhnjcnpxcpg9";
string cryptstr = member_id + ":" + panelType + ":" + RandomString;
string plainText = cryptstr;
BlowFish b = new BlowFish(encryptionKey);
string cipherText = b.Encrypt_ECB("3280:99:20120201123050");
The result is not daa745f1901364c0bd42b9658db3db96336758cd34b2a576. Where did I go wrong?
Encrypt_ECB() so I assume its Schneier's class.
The ctor expects a hexadecimal string if one is passed, you need the overload for a byte array:
BlowFish b = new BlowFish(Encoding.UTF8.GetBytes(encryptionKey));
The output is still not correct, lets see what it really should be by decrypting their example:
string clear = b.Decrypt_ECB("daa745f1901364c0bd42b9658db3db96336758cd34b2a576");
gives us:
"3280:99:20120201123050\u0002\u0002"
Which is good but there are 2 0x2 bytes on the end, the N x 0xN is due to the PKCS padding. To get the match you need to pad the input:
// input to bytes
List<byte> clearBytes = new List<byte>(Encoding.UTF8.GetBytes("3280:99:20120201123050"));
// how many padding bytes?
int needPaddingBytes = 8 - (clearBytes.Count % 8);
// add them
clearBytes.AddRange(Enumerable.Repeat((byte)needPaddingBytes, needPaddingBytes));
// encrypt
byte[] cipherText = b.Encrypt_ECB(clearBytes.ToArray());
// to hex
string cipherTextHex = BitConverter.ToString(cipherText).Replace("-", "").ToLowerInvariant();
Related
So im trying to encrypt data using C# DES
have the following code
static public string Encrypt(string _dataToEncrypt) {
SymmetricAlgorithm algorithm = DES.Create();
ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
byte[] inputbuffer = Encoding.Unicode.GetBytes(_dataToEncrypt);
byte[] outputBuffer = transform.TransformFinalBlock(inputbuffer, 0, inputbuffer.Length);
return Convert.ToBase64String(outputBuffer);
}
static public string Decrypt(string _dataToDecrypt) {
SymmetricAlgorithm algorithm = DES.Create();
ICryptoTransform transform = algorithm.CreateDecryptor(key, iv);
byte[] inputbuffer = Convert.FromBase64String(_dataToDecrypt); // Here is the problem.
byte[] outputBuffer = transform.TransformFinalBlock(inputbuffer, 0, inputbuffer.Length);
return Encoding.Unicode.GetString(outputBuffer);
}
And im getting an error System.FormatException: 'Invalid length for a Base-64 char array or string.'
It works when string has an even number of characters.
Is it even real to encrypt/decrypt data with an odd number of characters ?
DES, as well as AES does not have limit on what can be encrypted, the problem is elsewhere.
It looks like it is a Bas64 encoding problem given the line the error occurs on.
Perhaps trailing "=" characters were stripped from the Base64.
Information:
DES is a block based encryption algorithm, as such the input must be an exact multiple of the block size, 8-bytes for DES. When the input is not always an exact multiple padding must be added, the easiest way to to let the implementation do that for you by specifying padding, generally PKCS#5 for DES.
For SymmetricAlgorithm use Padding Property PKCS7, it is always best to fully specify everything and not rely on defaults.
algorithm.Padding = PaddingMode.PKCS7;
i implement AES 256 bit algorithm in C# but i am encrypting 128bit block of plain text which require padding so i dont want to pad and want use to stream cipher
use stream cipher instead of using 128 bit block
encrypt stream byte by byte
CryptLib _crypt = new CryptLib();
//string plainText = "This is the text to be encrypted";
String iv = CryptLib.GenerateRandomIV(16); //16 bytes = 128 bits
string key = CryptLib.getHashSha256("my secret key", 31); //32 bytes = 256 bits
MessageBox.Show(arm);//////////////////////
String cypherText = _crypt.encrypt(string1, key, iv);
Console.WriteLine("iv=" + iv);
Console.WriteLine("key=" + key);
Console.WriteLine("Cypher text=" + cypherText);
MessageBox.Show(cypherText);
textBox1.Text = cypherText;
Console.WriteLine("Plain text =" + _crypt.decrypt(cypherText, key, iv));
MessageBox.Show(_crypt.decrypt(cypherText, key, iv));
String dypher = _crypt.decrypt(cypherText, key, iv);
string outp = string.Empty;
char[] value = dypher.ToCharArray();
If the input data is always an exact multiple of the block size you can just specify no padding.
if you have data of unknown non-uniform block lengths padding is the general way to handle that.
Why do you not want to use padding.
Additionally:
It is common to prefix the encrypted data with the IV for use during decryption. The IV does not need to be secret and with this method the IV does not need to be shared in some other way and can easily be a different random value for each encryption.
Deriving a key from a password (string) with a hash function is not considered secure, instead use a key derivarion function such as PBKDF2.
I have this peace of code that encrypts stuff using AES, to be more precise Rijndael algorithm to mimic (http://dcx.sybase.com/index.html#sa160/en/dbreference/encrypt-function.html) the behaviour of SQL Anywhere 16, for sake of examples simplicity keys are fake:
var Key = Encoding.ASCII.GetBytes("1234567812345678");
var IV = Encoding.ASCII.GetBytes("1234567812345678");
var text = "stuff";
string encrypted;
var aes = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7, BlockSize = 128, KeySize = 128, Key = Key, IV = IV };
using (var encryptor = aes.CreateEncryptor())
{
var tmp = Encoding.ASCII.GetBytes(text);
encrypted = Convert.ToBase64String(encryptor.TransformFinalBlock(tmp, 0, tmp.Length));
}
Console.WriteLine("Encrypted text: " + encrypted);
And the result I get: do3BgGEeCWS5+mruUU1Czg== nXnrIX9m4zCxupbPsw3zsg==
Decrypting it in SQL Anywhere 16:
select cast(decrypt(base64_decode('do3BgGEeCWS5+mruUU1Czg=='), '1234567812345678', 'AES(format=RAW;padding=PKCS5)', '1234567812345678') as varchar)
I get this result: s t u f f stuff
So it almost works, comparing in hex it is 0x73007400750066006600 instead of 0x7374756666. Furthermore, if I decrypt same text in C# (decryptor source can be found bellow), I also get same spaces, what am I doing wrong?
Also I tried it other way around, encrypted in SQL Anywhere:
select base64_encode(encrypt('stuff', '1234567812345678', 'AES(format=RAW;padding=PKCS5)', '1234567812345678'))
Got this line: nXnrIX9m4zCxupbPsw3zsg==
Trying to decrypt in C# using same procedure:
string decrypted;
using (var decryptor = aes.CreateDecryptor())
{
var tmp = System.Convert.FromBase64String(encrypted);
decrypted = Encoding.ASCII.GetString(decryptor.TransformFinalBlock(tmp, 0, tmp.Length));
};
Console.WriteLine("Decrypted text: " + decrypted);
I get the correct result: stuff with no unnecessary spaces in it.
So it works with a mirror drawback, any ideas where from are those the extra spaces?
Update: Error was in var tmp = Encoding.Unicode.GetBytes(text); line, changed Unicode to ASCII.
If you are using SQL Anywhere version 16.0 or later, you can do this using the 'format=raw' option of the decrypt function. If you are using a version earlier than that, the decrypt function will not be able to decrypt data encrypted outside the database server.
Update: Since you updated your question, I'll address that too. I ran through the decryption in the SQL Anywhere server, and the data that comes out has the embedded NULLs in it, which means that the data that's encrypted already contains the embedded NULLs. I'm not a C# guy so I can't tell you for sure, but I suspect that var text = "stuff"; stores the data in UTF-16.
Full disclosure: I work for SAP in SQL Anywhere engineering.
I was given this C# code written by the web service team that exposes some web service that I'm planning to consume. My password needs to be encrypted with this code so that the web service knows how to decrypt it on their end.
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(publicKey);
byte[] plainBytes = Encoding.Unicode.GetBytes(clearText);
byte[] encryptedBytes = rsa.Encrypt(plainBytes, false);
return Convert.ToBase64String(encryptedBytes);
}
I'm using Java to consume this web service and right now, I'm having problem translating that #C code into Java code because that web service can't decrypt my password properly.
Here's my current failed attempt:-
// my clear text password
String clearTextPassword = "XXXXX";
// these values are provided by the web service team
String modulusString = "...";
String publicExponentString = "...";
BigInteger modulus = new BigInteger(1, Base64.decodeBase64(modulusString.getBytes("UTF-8")));
BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(publicExponentString.getBytes("UTF-8")));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encodedEncryptedPassword = new String(Base64.encodeBase64(cipher.doFinal(clearTextPassword.getBytes("UTF-8"))));
What did I do wrong? Thanks much.
2013-08-07 - UPDATE
I was reading this website and I realized that my modulus value and public exponent value are not in Hex. So, I modified my code a little bit and tried with RSA/ECB/PKCS1PADDING as mentioned by #Dev.
// my clear text password
String clearTextPassword = "XXXXX";
// these are the actual values I get from the web service team
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
Base64 base64Encoder = new Base64();
String modulusHex = new String(Hex.encodeHex(modulusString.getBytes("UTF-8")));
String publicExponentHex = new String(Hex.encodeHex(publicExponentString.getBytes("UTF-8")));
BigInteger modulus = new BigInteger(modulusHex, 16);
BigInteger publicExponent = new BigInteger(publicExponentHex);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encodedEncryptedPassword = new String(base64Encoder.encode(cipher.doFinal(clearTextPassword.getBytes("UTF-8"))));
When I hit the webservice, I'm getting this error: "The data to be decrypted exceeds the maximum for this modulus of 128 bytes." It seems like the clear text password is still not encrypted properly.
Any help or suggestion is greatly appreciated. Thanks.
2013-08-09 - SOLUTION
I posted my final working solution below.
Found the solution.
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(modulusString);
byte[] exponentBytes = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = clearTextPassword.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.encodeBase64String(cipherData);
According to MSDN docs on RSACryptoServiceProvider.Encrypt, when the second argument is false the cipher uses PKCS#1 v1.5 padding. So right off the bat your cipher spec is incorrect.
Try RSA/ECB/PKCS1PADDING instead.
You are converting your key material to much in your second code example and you corrupted it which ends up making your cipher think you have more key material than you actually have and makes your message too long (which is triggering your error) as well as unintelligible to the decryption cipher on the other end. Convert directly to byte arrays and pass those to BigInteger.
String modulusString = "...";
String publicExponentString = "...";
byte[] mod = Base64.decodeBase64(modulusString);
byte[] e = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, mod);
BigInteger publicExponent = new BigInteger(1, e);
Using RSA/ECB/OAEPWithSHA-1AndMGF1Padding seems to be the solution for this issue.
Don't forget these replacements.
Encoding to Base64
Use System.Convert to convert the input to Base64.
Replace + by - and / by _. Example: Foo+bar/=== becomes Foo-bar_===.
Replace any number of = at the end of the string, with an integer denoting how many they were. Example: Foo-bar_=== becomes Foo-bar_3.
Decoding from Base64
Replace the digit at the end of the string by the same number of = signs. Example: Foo-bar_3 becomes Foo-bar_===.
Replace - by + and _ by /. Example: Foo-bar_=== becomes Foo+bar/===.
Use System.Convert to decode the preprocessed input from Base64.
On my Blackberry I am creating an AES key and encrypting data. I am then encrypting the AES key with RSA before sending to the client c#.net
The AES key is a byte array. How can I convert this to a string so that it can be encrypted by RSA and then decrypted on the .net side?
Do I have to convert to string?
I am transmitting the data via JSON. I guess my question is really how to transmit a but array in JSON? What character encoding would I use?
Thanks.
You can use the following, which is URL safe and relatively easy to visually inspect. This takes a little more storage than Convert.ToBase64String, but shouldn't be an issue with a fixed width encryption key.
string MyKey = BitConverter.ToString(MyAESKey); // dash removal is trivial here
OR
string MyKey = Convert.ToBase64String(MyAESKey);
Code Sample
byte[] a = new byte[256/8];
Random random = new Random();
random.NextBytes(a);
string base64 = Convert.ToBase64String(a);
byte [] b = Convert.FromBase64String(base64);
if (a.SequenceEqual(b))
// true
string c = BitConverter.ToString(a);
string[] c1 = c.Split('-');
byte[] d = new byte[arr.Length];
for (int i = 0; i < arr.Length; i++) d[i] = Convert.ToByte(c1[i], 16);
if (a.SequenceEqual(d))
// true