My output that I have to match is from Java DESede using a BouncyCastle Jar from 2005 ... I am very close...
Here is my output in Java (which is correct) followed by my output in C# ... if you view in an editor, you will see they ALMOST match, except in C# where it has a forward slash "/" in Java it has "%2F", "%2D" and at the end where C# has is an "=" , Java has "%3D". Any ideas? (I added spaces to show they match up - but you will only see them in an editor.)
F3e8sdZ%2F951IRiguIAVqfDLyWptqlbWik5tvFzItcxJCEmupzD9wXp%2BDzIbrf2J2dPpXyEXL2QU%3D (Java - Correct)
F3e8sdZ/ 951IRiguIAVqfDLyWptqlbWik5tvFzItcxJCEmupzD9wXp+ DzIbrf2J2dPpXyEXL2QU= (C# - Close?)
Here is my C# Code:
public static string DoubleTrippleDESede(string strToEncode, ref string symKey, ref ICryptoTransform cipher)
{
try
{
//byte[] input = Encoding.UTF8.GetBytes("DESede (3DES) Encryption in RAILO CFML");
byte[] input = Encoding.UTF8.GetBytes(strToEncode);
//byte[] key = Convert.FromBase64String("ru8femXhTm9jwdGdhb/4Sw==");
byte[] key = Convert.FromBase64String(symKey);
TripleDESCryptoServiceProvider algorithm = new TripleDESCryptoServiceProvider();
algorithm.Mode = CipherMode.ECB;
algorithm.BlockSize = 64;
algorithm.KeySize = 192; // 24 byte key
algorithm.Key = key; //Original
//algorithm.Key = key.CopyTo(algorithm.Key,)
cipher = algorithm.CreateEncryptor();
byte[] encrypted = cipher.TransformFinalBlock(input, 0, input.Length);
Debug.WriteLine("encrypted (.NET): {0}", Convert.ToBase64String(encrypted));
return Convert.ToBase64String(encrypted);
}
catch (Exception ex)
{
return ex.Message;
}
}
Any guidance would be greatly appreciated!!!! I've been at this for 2 weeks and finally can taste victory (I think!?)
Your Java output appears to have additionally been urlencoded. You should be able to call System.Uri.EscapeDataString() to match your present output.
Related
I want to convert this below piece of code into java but I am unable to do, Basically I have to implement 'crypto' module in Java. Thanks in advance!
let encKey = "0Z8ZUcy1Qh8lnt199MTwTPEe2g1E2tE3";
encKey = crypto.createHash('sha256').update(encKey).digest('bin').slice(0, 32);
let char = String.fromCharCode(0x0);
let iv = char + char + char + char + char + char + char + char + char + char + char + char + char + char + char + char;
let decryptor = crypto.createDecipheriv("aes-256-cbc", encKey, iv);
let dec = decryptor.update(someAuthString, 'base64', 'utf8') + decryptor.final('utf8');
dec = removePKCS5Padding(dec);
removePKCS5Padding
function removePKCS5Padding(text) {
let pad = ord(text[text.length - 1]);
pad = text.substr(0, -1 * pad)
if (_.isEmpty(pad)) {
return text;
} else {
return pad;
}
}
First you don't need to implement the whole module, just particular algorithms from it.
Second whoever wrote that code didn't know what they were doing. SHA-256 already produces a 32-byte value (always) so .slice(0,32) accomplishes nothing. And createCipher[iv] and createDecipher[iv] for a block mode already add and remove 'PKCS5' padding automatically unless explicitly disabled. (Prior to PKCS5v2.1 it was technically more correct to say PKCS7 or PKCS5/7, but in practice people often don't bother, and Java calls it PKCS5. OpenSSL, which nodejs crypto uses internally, punts and calls it PKCS padding -- although there are several PKCS1 paddings which are quite different, and which OpenSSL also implements.)
byte[] keyIn = "0Z8ZUcy1Qh8lnt199MTwTPEe2g1E2tE3" .getBytes("ASCII");
// if any non-ASCII char(s) must select same encoding nodejs does, I believe utf8
// instead of string form can use e.g. StandardCharsets.US_ASCII
byte[] keyHash = MessageDigest.getInstance("SHA-256") .doFinal(keyIn);
// or "sha-256" Java crypto names are case-insensitive
// can separate steps with hasher = .getInstance(); hasher.update(keyIn); result = hasher.doFinal()
// but cannot do fluent-style result = .getInstance() .update(keyIn) .doFinal()
byte[] iv = new byte[16]; // Java automatically fills numeric array with (binary) zeros
Cipher dec = Cipher.getInstance("AES/CBC/PKCS5Padding");
dec.init (Cipher.DECRYPT_MODE, new SecretKeySpec(keyHash,"AES"), new IvParameterSpec(iv));
String clear = new String( dec.doFinal (Base64.getDecoder().decode( someAuthString )), "UTF-8");
// or StandardCharsets.UTF_8
I have created a simple java method that encrypts and decrypts text using the bouncy castle library. Encryption works as expected but when I decrypt something I get these extra square symbols at the end:
I think this might be something to do with padding but I've followed the example featured on bouncy castle's website, so I really can't understand why I would be getting this sort of output. Here is the code I am using:
[Main]
public static void main(String[] argv) {
String ciphertext = "PlJR5pzbowsuzHIc9iTKHg==";
String decrypted;
CryptoCodec codec = new CryptoCodec();
decrypted = codec.exec("AES", "xxxxooooxxxxoooo", ciphertext, false);
System.out.println("Ciphertext: " + ciphertext);
System.out.println("Decrypted: " + decrypted);
}
[CryptoCodec]
// Eod: (true) Encrypt or (false) decrypt.
public String exec(String algorithm, String key, String data, boolean eod) {
// Using AESEngine();
BlockCipher engine = CipherEngine.getBlockCipher(algorithm);
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine));
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
byte[] dataBytes;
if(eod) {
dataBytes = data.getBytes(StandardCharsets.UTF_8);
} else {
dataBytes = Base64.decode(data);
}
cipher.init(eod, new KeyParameter(keyBytes));
byte[] outputText = new byte[cipher.getOutputSize(dataBytes.length)];
int outputTextLen = cipher.processBytes(dataBytes, 0, dataBytes.length, outputText, 0);
try {
cipher.doFinal(outputText, outputTextLen);
} catch (CryptoException err) {
err.printStackTrace();
}
if(eod) {
return new String(Base64.encode(outputText));
} else {
return new String(outputText);
}
}
Please keep in mind I am still learning about cryptography and would love to hear any sort of explanation to why this may be happening. Thanks in advance.
During decryption cipher.getOutputSize(dataBytes.length) doesn't know how many bytes it will remove from padding (it doesn't even know that you're telling it about the last part of the data). So it tells you the maximum it could be.
Your destination array is therefore larger than it needs to be, and you need to respect how much data got filled in.
How do you know how much got filled in? Capture the return value from doFinal. What do you do with it then? Tell the String constructor when to stop reading.
You then end up with something like
try {
outputTextLen += cipher.doFinal(outputText, outputTextLen);
} catch (CryptoException err) {
err.printStackTrace();
}
if(eod) {
return new String(Base64.encode(outputText));
} else {
return new String(outputText, 0, outputTextLen);
}
Which also fixes your bug that if you encrypt 16 bytes of data right now you won't decrypt successfully.
All,I am posting some encrypted xml data(Using AES-128 ) to another application that uses Java to decrypt.When the Java code decrypts the xml,the start tag of the xml is getting truncated and fails validation.I don't have access to their code base .I can decrypt the same data using C# without any data loss.Please see the code I use to encrypt and Decrypt the data . I have researched this and based on the research ,I added the FlushFinalBlocks() and Close() to the CryptoStream in the encryption logic ,but this doesnt seem to fix the issue.
Encryption Code:
public static string Aes128Encrypt(string plainText)
{
string encodedPayload = null;
string base64Iv = null;
string base64Key = null;
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
using (RijndaelManaged aesAlg = new RijndaelManaged())
{
aesAlg.KeySize = 128;
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.BlockSize = 128;
base64Iv = Convert.ToBase64String(aesAlg.IV);
base64Key = Convert.ToBase64String(aesAlg.Key);
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(plainBytes, 0, plainBytes.Length);
csEncrypt.FlushFinalBlock();
encodedPayload = Convert.ToBase64String(msEncrypt.ToArray());
csEncrypt.Close();
}
msEncrypt.Flush();
msEncrypt.Close();
}
}
return encodedPayload ;
}
Decryption Code:
public static string Aes128Decrypt(string base64Key, string base64IV, string encodedPayload)
{
string plainText = null;
byte[] key = Convert.FromBase64String(base64Key);
byte[] iv = Convert.FromBase64String(base64IV);
byte[] encryptedBytes = Convert.FromBase64String(encodedPayload);
using (RijndaelManaged aesAlg = new RijndaelManaged())
{
aesAlg.KeySize = 128;
aesAlg.Mode = CipherMode.CBC;
aesAlg.BlockSize = 128;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = key;
aesAlg.IV = iv;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(encryptedBytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plainText = srDecrypt.ReadToEnd();
}
}
}
}
return plainText;
}
Testing Code:
string textXml = #"<person>
<firstName>Rennish</firstName>
<lastName>Joseph</lastName>
<accountNumber>12345678910</accountNumber>
<ssn>123456</ssn>
</person>";
Aes128Encrypt(textXml);
string encodedPayload = "4p6uU7SiqB0uCzsrWXMOStP02HM7mKA6QVzcKoNdu3w1+MYLjYVbW/Ig3XPKRRafeu+WKDMuKJJaEREkrZt/Ycvc50wfe2naJ9d0UT5B7Fre1gIsNfZUIK3SF304+WF8zX730mVsluJABKT3JCkk9AkOGCQWPYzcZvH9dojIqGP7V+2j1+IMOPMWWFIitkAi8B7ALxMuMcepzX2/cxHxH7NeID0ytEGUzGfJXSAzQcvBX9dWwUqdMX3Eip5SRPMsotnWWsFTjDuOiZk/q5fuxxWbS6cuYn/64C/vQjEIuheQKn0ZOIDLNPCUavvWD2u6PWNKMNgW/qUIq13W9PQxzIiQxrT7ZqPFJu75C1KdXXUG5lghU7EBAGehHC/5BqFjs9SuYJkV1RrchMEzytrJIQ7Zp4CnOU6Q1rEhFTaMk/s=";
string encodedKey = "2zpVbIxqvjSfJo7zkXzl2A==";
string encodedIV = "5WOQPdmB/BkECmuPdNTaLw==";
Aes128Decrypt(encodedKey, encodedIV, encodedPayload);
Data after encryption at the JAVA application looks like this
<rson>
<firstName>Rennish</firstName>
<lastName>Joseph</lastName>
<accountNumber>12345678910</accountNumber>
<ssn>123456</ssn>
</person>
Interesting problem.
I think the encryption and decryption works fine on both sides.
If part of the encrypted message was lost in transmission you would not be able to decrypt it due to the avalanche effect. So it appears that characters go missing in the plain text.
This might be an encoding issue in the plain text message. The bytes you have encoded and the bytes they decoded are probably the same. The way they are interpreted might not be.
Now there are two options here:
Either <person> becomes <rson> or it becomes rson> and there was a copy-paste mistake.
If the latter case is true then we're missing 3 bytes. This makes me think that the protocol might presume the presence of a byte order marker andsimply removes the first 3 bytes to get rid of it.
If the former case you'd have some very weird encoding issues. As all missing characters appear to be in the ascii range so they shouldn't have these issues.
Easy to test though:
1. Try sending with a byte order marker.
2. Try sending with <XXperson>
3. Try sending some characters with accents and the like.
This question might probably be a duplicate. But so far I haven't seen any response that could solve mine issue.
I have this piece of Java code doing encryption with SHA-256:
public static String hashIt(String msg, String key) {
MessageDigest m = null;
String hashText = null;
byte[] actualKeyBytes = TripleDES.hexStringToBytes(key);
try {
m = MessageDigest.getInstance("SHA-256");
m.update(actualKeyBytes, 0, actualKeyBytes.length);
try {
m.update(msg.getBytes("UTF-8"), 0, msg.length());
} catch (UnsupportedEncodingException ex) {
}
hashText = TripleDES.bytesToHexString( m.digest() ); //new BigInteger(1, m.digest()).toString(16);
} catch (NoSuchAlgorithmException ex) {
}
return hashText;
}
Using d38a5cd5 as key with "ewo10kalavanda" as the string to hash.
Utils.hashIt("ewo10kalavanda", "d38a5cd5");
I have the following output:
fc87c73012e11de3a57faabe4d852ce89ec3337504531c16
Using the same SHA256 in PHP
hash_hmac('SHA256', "ewo10kalavanda", "d38a5cd5", $raw=false)
The output is 1839412f79b9e33c2f810650f79f23f46173792f885dd8d8c9633675e28e792f which does not match that of Java.
Is there anything done wrong here? Been on this for some hours now.
In your PHP code you used HMAC which is more than just hashing the string obtained by joining key and the message body. I found a diagram from Wikipedia which explains how HMAC-SHA1 works:
I did manage to get a working version in Java:
public static String hashIt(String msg, String key) {
try {
byte[] keyBytes = key.getBytes("UTF-8");
SecretKeySpec spec = new SecretKeySpec(keyBytes, HMAC_SHA256);
Mac mac = Mac.getInstance(HMAC_SHA256);
mac.init(spec);
return TripleDES.bytesToHexString(mac.doFinal(msg.getBytes("UTF-8")));
} catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
I still think there is something wrong with msg.length(). If you ever hash a two byte character it will be buggy. I tested it and it's different. For example try use your previous code to hash 111111錒(message) and 1111(key) and then use my previously suggested code to hash the same string. Your code output 81e385eb2bf89f7494a4b0927a4f5d4105450eb4a21152d53d52ddb9c08ed0e1 and my code output ef7f82833c865ef4d6089ba7dfbec8ad4f05b58e3fd77ca242c5fd7e7757d8b4.
That chinese character is intended. It shows how the OP's code fails with two byte characters. DO NOT REMOVE.
The other answer from glee8e should have got you a long way. But just to be sure, here is how to generate the output:
$k = hex2bin("d38a5cd5");
$m = "ewo10kalavanda";
$in = $k.$m;
$h = hash ("SHA256", $in);
print $h;
It would be a bit better to first encode to UTF-8, but I haven't got the right module installed:
$m = mb_convert_encoding("ewo10kalavanda", "UTF-8");
for the test sting this of course doesn't matter as long as the platform encoding is compatible with UTF-8 for the input characters.
That's however half of the answer though: there is a reason why HMAC was defined, and the major reason is that hash functions on their own are not that secure for keyed hash or Message Authentication Code (MAC). So the use of HMAC as in the PHP function should be preferred.
My problem is: what I encrypt in Java I can decrypt perfectly in Java, but PHP mcrypt can't decrypt. What I encrypt with mcrypt I can decrypt with mcrypt, but can't in Java.
I want to send and receive encrypted data from a Java application to a PHP page, so I need it to be compatible.
Here's what I have...
JAVA...
public static String crypt(String input, String key){
byte[] crypted = null;
try{
SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes());
}catch(Exception e){
}
return Base64.encodeBase64String(crypted);
}
public static String decrypt(String input, String key){
byte[] output = null;
try{
SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skey);
output = cipher.doFinal(Base64.decodeBase64(input));
}catch(Exception e){
}
return new String(output);
}
Running:
public static void main(String[] args) {
String key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=";
String data = "example";
System.out.println(Cpt.decrypt(Cpt.crypt(data, key), key));
}
Output:
example
PHP...
function getEncrypt($sStr, $sKey) {
return base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$sKey,
$sStr,
MCRYPT_MODE_ECB
)
);
}
function getDecrypt($sStr, $sKey) {
return mcrypt_decrypt(
MCRYPT_RIJNDAEL_256,
$sKey,
base64_decode($sStr),
MCRYPT_MODE_ECB
);
}
Running:
$crypt = getDecrypt(getEncrypt($str, $key), $key);
echo "<p>Crypt: $crypt</p>";
Output:
Crypt: example�������������������������
Using PHP to crypt "example" with key "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=" I get "YTYhgp4zC+w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY=".
Using Java to crypt the same thing with the same key I get "+tdAZqTE7WAVPXhB3Tp5+g==".
I'm encoding and decoding to base64 in the right order and I tested base64 encode and decode compatibility between Java and PHP and it's working.
BUG#1
MCRYPT_RIJNDAEL_256 is not AES. The 256 in that constant refers to the blocksize, not the keysize. Use MCRYPT_RIJNDAEL_128 to get the same algorithm as AES. The keysize is set just by the number of bytes in the key argument you supply. So supply 32 bytes and you get AES with a 256-bit key.
BUG#2
These two lines are never correct in Java and indicate a fundamental misunderstanding of the nature of the arbitrary binary data produced by cryptographic transforms:
output = cipher.doFinal(Base64.decodeBase64(input));
return new String(output);
There is nothing wrong with transmitting and storing byte[] directly, but if you must use only printable strings then you should base64 encode/decode to do so. As you are already using base64 extensively that would seem like the way to go. I would guess that the correct two lines would be:
output = cipher.doFinal(Base64.decodeBase64(input));
return new String(Base64.encodeBase64(output), "UTF-8");
EDIT:
Just kidding about bug #2. Really, I was wrong, I didn't notice it was the decrypt direction. Of course, if you know the decrypted byte[] is a valid string then it is perfectly correct to do what your code does.
I know this is an old topic, but I will add my working solution.
You have to rewrite PHP side of the script:
function getEncrypt($sStr, $sKey) {
return base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
base64_decode($sKey),
$sStr,
MCRYPT_MODE_ECB
)
);
}
function getDecrypt($sStr, $sKey) {
return mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
base64_decode($sKey),
base64_decode($sStr),
MCRYPT_MODE_ECB
);
}
You should base64_decode($sKey) because your key is base64 encoded.
$key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=";
Then, you need to create this function (credit goes to beltrachi from http://www.php.net/manual/en/function.mcrypt-decrypt.php):
function pkcs5_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
Use this code do encode/decode:
$decrypt = getDecrypt("6XremNEs1jv/Nnf/fRlQob6oG1jkge+5Ut3PL489oIo=", $key);
echo $decrypt;
echo "\n\n";
echo getEncrypt(pkcs5_pad("My very secret text:)", 16), $key);
I hope this will be useful for someone! :)
Please see here:
Difference in PHP encryption from iOS and .NET
AES Encrypt in C#, decrypt in PHP
DES Encryption in PHP and C#
The problem you're encountering is a padding-issue. I don't know Java, but AES/ECB/PKCS5Padding looks like you're using a PKCS#5 (that's essentially the same as PKCS#7) padding while PHP natively only support NULL-padding. That's what PKCS#5/7 does:
Pad the input with a padding string of
between 1 and 8 bytes to make the
total length an exact multiple of 8
bytes. The value of each byte of the
padding string is set to the number of
bytes added - i.e. 8 bytes of value
0x08, 7 bytes of value 0x07, ..., 2
bytes of 0x02, or one byte of value
0x01.
So the PHP code to do the padding right is trivial:
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$padding = $blockSize - (strlen($data) % $blockSize);
$data .= str_repeat(chr($padding), $padding);
Keep in mind to have the same encoding for the strings. Try to convert the strings in both languages to UTF-8, e.g., and than convert to binary data that is encoded:
PHP (s. utf8_encode() function):
$strAndBlob = utf8_encode("My string");
Java:
String str = "My string";
byte[] blob = str.getBytes("utf-8");
PHP, e.g., must not use UTF-8 by default.