I have to implement a RC4 cipher in NodeJS, here is the code:
function cipher (CRYPTO, str) {
const cipher = crypto.createCipher(CRYPTO.cipherAlgorithm, CRYPTO.password);
return Buffer.concat([
cipher.update(str, 'utf-8'),
cipher.final()
]).toString(CRYPTO.encoding);
}
const CRYPTO = {
cipherAlgorithm: 'rc4',
password: 'trololol',
encoding: 'base64'
};
cipher(CRYPTO, '0612345678');
// returns 'yTXp/PZzn+wYsQ=='
When i check my implementation with open ssl, i've got the same result:
echo -ne "0612345678" | openssl rc4 -pass "pass:trololol" -e -nosalt | base64
> yTXp/PZzn+wYsQ==
But with our partner implementation, the result is really different. It is written in Java so i tried to do one and i have the same result than him:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class Encryptor {
private static String algorithm = "RC4";
public static String encrypt(String key, String value) {
try {
SecretKeySpec rc4Key = new SecretKeySpec(key.getBytes(), algorithm);
Cipher rc4 = Cipher.getInstance(algorithm);
rc4.init(Cipher.ENCRYPT_MODE, rc4Key);
byte [] encrypted = rc4.update(value.getBytes());
return DatatypeConverter.printBase64Binary(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String key = "trololol";
String value = "0612345678";
System.out.println(encrypt(key, value));
}
}
Running the above gives:
javac Encryptor.java && java Encryptor
> LYlbWr0URiz+wA==
Is it possible that the RC4 algorithm in Java differs from the other ones or is there something wrong in the Java implementation?
The difference is "password" vs "key."
For example with node and OpenSSL, "password" means some value to hash (using MD5) to generate the key for encryption/decryption.
If you instead use the "password" value as the key (with an empty IV), you will match the value received from Java. For example with node, change to the createCipheriv() function:
crypto.createCipheriv(CRYPTO.cipherAlgorithm, CRYPTO.password, Buffer.alloc(0));
Related
I have a Java 6 function below:
import java.net.*;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.xml.bind.DatatypeConverter;
public class decryptSEK {
public static void main(String[] args) {
String encryptedSek = args[0];
String appKey = args[1];
byte[] appKey32b = DatatypeConverter.parseBase64Binary(appKey);
String decryptedSek = decryptBySymmetricKey(encryptedSek, appKey32b);
System.out.println(decryptedSek);
}
public static String decryptBySymmetricKey(String encryptedSek, byte[] appKey) {
Key aesKey = new SecretKeySpec(appKey, "AES"); // converts bytes(32 byte random generated) to key
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // encryption type = AES with padding PKCS5
cipher.init(Cipher.DECRYPT_MODE, aesKey); // initiate decryption type with the key
byte[] encryptedSekBytes = DatatypeConverter.parseBase64Binary(encryptedSek); //Base64.getDecoder().decode(encryptedSek); // decode the base64 encryptedSek to bytes
byte[] decryptedSekBytes = cipher.doFinal(encryptedSekBytes); // decrypt the encryptedSek with the initialized cipher containing the key(Results in bytes)
// String decryptedSek = Base64.getEncoder().encodeToString(decryptedSekBytes); // convert the decryptedSek(bytes) to Base64 StriNG
String decryptedSek = DatatypeConverter.printBase64Binary(decryptedSekBytes);
return decryptedSek; // return results in base64 string
} catch(Exception e) {
return "Exception; "+e;
}
}
}
When I build the above as class file and then run java -classpath . decryptSEK it works well and the output is as expected. I installed JCE for Java 6, so it works well.
But when I convert this into an Oracle program to run on server like below:
--DECRYPT SEK
create or replace JAVA SOURCE NAMED decryptSEK AS
import java.net.*;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.xml.bind.DatatypeConverter;
public class decryptSEK {
public static void main(String[] args) {
String encryptedSek = args[0];
String appKey = args[1];
byte[] appKey32b = DatatypeConverter.parseBase64Binary(appKey);
String decryptedSek = decryptBySymmetricKey(encryptedSek, appKey32b);
System.out.println(decryptedSek);
}
public static String decryptSEKcall(String encryptedSek,String appKey)
{
byte[] appKey32b = DatatypeConverter.parseBase64Binary(appKey);
String decryptedSek = decryptBySymmetricKey(encryptedSek, appKey32b);
System.out.println(decryptedSek);
return decryptedSek;
}
public static String decryptBySymmetricKey(String encryptedSek, byte[] appKey) {
Key aesKey = new SecretKeySpec(appKey, "AES");
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey);
byte[] encryptedSekBytes = DatatypeConverter.parseBase64Binary(encryptedSek);
byte[] decryptedSekBytes = cipher.doFinal(encryptedSekBytes);
String decryptedSek = DatatypeConverter.printBase64Binary(decryptedSekBytes);
return decryptedSek;
} catch(Exception e) {
return "Exception; "+e;
}
}
}
/
CREATE OR REPLACE FUNCTION decryptSEK_func(P_ENCRYPTSEK VARCHAR2,P_APPKEY VARCHAR2)
RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'decryptSEK2.decryptSEKcall( java.lang.String,java.lang.String )
return java.lang.String';
select decryptSEK_func( 's8U+CjS8zKEmwmpCs7HnmTYKpx6rMwEdXVk/g8fNBhVMzKlFxkA1WemvUX00evh8',
'SpRstt3iYywGQlI8U8SQfOA3jajkZpJGjlI4sPeVk7A=')encryptsek from dual;
It throws the following error:
Exception: java.security.InvalidKeyException: Illegal key size or default parameters
I have installed the JCE extension files and it seems to work on command prompt, but not when I change it to Oracle. Where I am going wrong?
Instead of creating your own Java code to do encryption, I would highly recommend using the encryption functionality available in the DBMS_CRYPTO package. It does support AES/ECB/PKCS5Padding encryption, you will just need to set the proper "type" when calling the encrypt/decrypt functions. Below is the same example from the documentation but with the encryption type modified to what you are using in your Java code.
DECLARE
input_string VARCHAR2 (200) := 'Secret Message';
output_string VARCHAR2 (200);
encrypted_raw RAW (2000); -- stores encrypted binary text
decrypted_raw RAW (2000); -- stores decrypted binary text
num_key_bytes NUMBER := 256 / 8; -- key length 256 bits (32 bytes)
key_bytes_raw RAW (32); -- stores 256-bit encryption key
encryption_type PLS_INTEGER
:= -- total encryption type
DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_ECB + DBMS_CRYPTO.PAD_PKCS5;
iv_raw RAW (16);
BEGIN
DBMS_OUTPUT.PUT_LINE ('Original string: ' || input_string);
key_bytes_raw := DBMS_CRYPTO.RANDOMBYTES (num_key_bytes);
iv_raw := DBMS_CRYPTO.RANDOMBYTES (16);
encrypted_raw :=
DBMS_CRYPTO.ENCRYPT (src => UTL_I18N.STRING_TO_RAW (input_string, 'AL32UTF8'),
typ => encryption_type,
key => key_bytes_raw,
iv => iv_raw);
-- The encrypted value "encrypted_raw" can be used here
decrypted_raw :=
DBMS_CRYPTO.DECRYPT (src => encrypted_raw,
typ => encryption_type,
key => key_bytes_raw,
iv => iv_raw);
output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw, 'AL32UTF8');
DBMS_OUTPUT.PUT_LINE ('Decrypted string: ' || output_string);
END;
/
I implemented a straight simple Java utility class to encrypt and decrypt using AES/GCM/NoPadding. I use this piece of code:
public byte[] encrypt(byte[] input, byte[] key, byte[] iv) throws Exception{
Cipher cipher = initAES256GCMCipher(key, iv, Cipher.ENCRYPT_MODE);
return cipher.doFinal(input);
}
public byte[] decrypt(byte[] input, byte[] key, byte[] iv) throws Exception{
Cipher cipher = initAES256GCMCipher(key, iv, Cipher.DECRYPT_MODE);
return cipher.doFinal(input);
}
private Cipher initAES256GCMCipher(byte[] key, byte[] iv, int encryptionMode) throws Exception{
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(encryptionMode, secretKey, gcmParameterSpec);
return cipher;
}
IV is always a 12-byte array, key is 32-byte array generated with SecureRandom taking a seed. I knowthat on different OS SecureRandom is different, but encryption and decryption are performed on the same OS, so there should be no problem.
Is it quite linear, right? It works perfectly on Windows, ciphering and deciphering return the same text. However, on a Docker image, the same JAR doesn not work: encryption works fine, but decryption throws "AEADBadTagException".
Can you help me, please?
Do not use SecureRandom to derive a key. You'd use a Key Derivation Function (KDF) such as HKDF to do that. But honestly, you have to think of a way to communicate the key - without using SecureRandom.
The problem is that the - relatively secure - SHA1PRNG algorithm is not well defined. The SUN provider does accept a seed that is then used as an only seed iff you seed it before retrieving any random data from it. However, it makes sense that other providers will just mix in the seed to the state of the underlying CSPRNG. This is also the default for most other SecureRandom implementations.
Few SecureRandom implementations fully specify the way that random bits are returned even if the underlying algorithm is specified (with the DRBG). That is usually not a problem if you are just expecting random values. However, if you are using it as a deterministic algorithm such as a KDF (or hash function, for instance) then this becomes a problem and you may get different keys for the same input.
Nowadays you should be able to store AES secret keys within a key store. Not sure if that is a solution for your use case, but it should solve your current issue. Unfortunately Java doesn't contain any official KDF's other than PBKDF1 and PBKDF2 which takes a password rather than a key. Just using a HMAC-SHA256 over some key identifying data using a master key is commonly a good "poor mans KDF".
Here is a quick, initial (but undocumented) implementation that I just created. It mimics the Java JCA without actually going into implementation (as no specific KDF class is defined for it, yet).
You can ask many keys from it with any algorithm specification, as long as the size is below 256 bits (the output of SHA-256) and the label is different for each key that you request. Call getEncoded() on the key if you need data for, for instance, an IV.
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import hex.Hex;
public class PoorMansKDF {
public interface KeyDerivationParameters extends AlgorithmParameterSpec {
String getDerivedKeyAlgorithm();
int getDerivedKeySize();
byte[] getCanonicalInfo();
}
public static class KeyDerivationParametersWithLabel implements KeyDerivationParameters {
private final String algorithm;
private final int keySize;
private final String label;
public KeyDerivationParametersWithLabel(String algorithm, int keySize, String label) {
this.algorithm = algorithm;
this.keySize = keySize;
this.label = label;
}
#Override
public byte[] getCanonicalInfo() {
if (label == null) {
// array without elements
return new byte[0];
}
return label.getBytes(StandardCharsets.UTF_8);
}
#Override
public String getDerivedKeyAlgorithm() {
return algorithm;
}
#Override
public int getDerivedKeySize() {
return keySize;
}
}
private enum State {
CONFIGURED,
INITIALIZED;
}
public static PoorMansKDF getInstance() throws NoSuchAlgorithmException {
return new PoorMansKDF();
}
private final Mac hmac;
private State state;
private PoorMansKDF() throws NoSuchAlgorithmException {
this.hmac = Mac.getInstance("HMACSHA256");
this.state = State.CONFIGURED;
}
public void init(Key key) throws InvalidKeyException {
if (key.getAlgorithm().equalsIgnoreCase("HMAC")) {
key = new SecretKeySpec(key.getEncoded(), "HMAC");
}
hmac.init(key);
this.state = State.INITIALIZED;
}
public Key deriveKey(KeyDerivationParameters info) {
if (state != State.INITIALIZED) {
throw new IllegalStateException("Not initialized");
}
final int keySize = info.getDerivedKeySize();
if (keySize < 0 || keySize % Byte.SIZE != 0 || keySize > hmac.getMacLength() * Byte.SIZE) {
throw new IllegalArgumentException("Required key incompatible with this KDF");
}
final int keySizeBytes = keySize / Byte.SIZE;
// we'll directly encode the info to bytes
byte[] infoData = info.getCanonicalInfo();
final byte[] fullHMAC = hmac.doFinal(infoData);
final byte[] derivedKeyData;
if (fullHMAC.length == keySizeBytes) {
derivedKeyData = fullHMAC;
} else {
derivedKeyData = Arrays.copyOf(fullHMAC, keySizeBytes);
}
SecretKey derivedKey = new SecretKeySpec(derivedKeyData, info.getDerivedKeyAlgorithm());
Arrays.fill(derivedKeyData, (byte) 0x00);
if (fullHMAC != derivedKeyData) {
Arrays.fill(fullHMAC, (byte) 0x00);
}
return derivedKey;
}
// test only
public static void main(String[] args) throws Exception {
var kdf = PoorMansKDF.getInstance();
// input key (zero byte key for testing purposes, use your own 16-32 byte key)
// do not use a password here!
var masterKey = new SecretKeySpec(new byte[32], "HMAC");
kdf.init(masterKey);
// here "enc" is a label, in this case for a derived key for ENCryption
var labeledParameters = new KeyDerivationParametersWithLabel("AES", 256, "enc");
var derivedKey = kdf.deriveKey(labeledParameters);
// use your own hex decoder, e.g. from Apache Commons
System.out.println(Hex.encode(derivedKey.getEncoded()));
var aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
var gcmParams = new GCMParameterSpec(128, new byte[12]);
aesCipher.init(Cipher.ENCRYPT_MODE, derivedKey, gcmParams);
var ct = aesCipher.doFinal();
System.out.println(Hex.encode(ct));
}
}
I have been struggling with this for a couple of days now. I'm required to consume an API that takes an encrypted parameter. The API was written in C#. The encryption requested is the following:
Algorithm: AES
Cipher mode: CBC
Padding mode: PKCS7
Block size: 128
Key size: 256
Key: String --> The key is generated by converting a provided string to a byte array of size 32: Encoding.ASCII.GetBytes(…). The API states that the String is generated by them using MD5 hashing function of a string.
IV: IV array is generated by converting a provided string to a byte array of size 16: Encoding.ASCII.GetBytes(…).
Representation of encrypted string: Base64
After searching and trying so many things that were suggested online, I'm still unable to produce the same encrypted value (Specially that PKCS7 is not supported by default and PKCS5 should be working the same, but it's not). Here are some things that I've tried:
1) Using bouncy castle jar to use PKCS7
2) Adding JCE compliance to be able to remove the limit on key and block sizes.
After contacting them, they sent me an android snippet that's working (which if I run in plain java 8 complains about the provider (NoSuchAlgorithmException: Cannot find any provider supporting AES/CBC/PKCS7Padding)):
public static String encrypt(String value) {
String plainText = value;
String escapedString;
try {
byte[] key = ENCRYPT_KEY.getBytes("UTF-8");
byte[] ivs = ENCRYPT_IV.getBytes("UTF-8");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivs);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
escapedString = Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")), Base64.DEFAULT).trim();
return escapedString;
} catch (Exception e) {
e.printStackTrace();
return value;
}
}
Please any help would be really appreciated.
Here's a code snippet from what I tried:
package com.melhem.TestJava;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class StringFunc {
final static String key = "API_KEY_32_CHARs";
final static String iv = "API_IV_16_CHARs";
final static String algorithm = "AES/CBC/PKCS7Padding";
private static Cipher cipher = null;
private static SecretKeySpec skeySpec = null;
private static IvParameterSpec ivSpec = null;
public static void main(String[] args) {
System.out.println(encrypt("STRING_TO_ENCODE"));
}
private static void setUp(){
try{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
skeySpec = new SecretKeySpec(key.getBytes("ASCII"), "AES");
ivSpec = new IvParameterSpec(iv.getBytes("ASCII"));
cipher = Cipher.getInstance(algorithm);
}catch(NoSuchAlgorithmException | NoSuchPaddingException ex){
ex.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String encrypt(String str){
try{
// Integer strL = (int) Math.ceil(str.length() / 8.0);
// Integer strB = strL*8;
// str = padRight(str, ' ', strB);
setUp();
try {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
System.out.println("Block size: " + cipher.getBlockSize() * 8);
System.out.println("Algorithm name: " + cipher.getAlgorithm());
System.out.println("Key size: " + skeySpec.getEncoded().length * 8);
} catch (InvalidAlgorithmParameterException ex) {
ex.printStackTrace();
return "";
}
byte[] enc = cipher.doFinal(str.getBytes("ASCII"));
String s = new String(Base64.getEncoder().encode(enc));
s = s.replace("+", "__plus__");
s = s.replace("/", "__slash__");
return s;
}catch(InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex){
ex.printStackTrace();
return "";
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
public static String padRight(String msg, char x, int l) {
String result = "";
if (!msg.isEmpty()) {
for (int i=0; i<(l-msg.length()); i++) {
result = result + x;
}
result = msg + result;
}
return result;
}
}
Java Cipher package only supports PKCS#7 padding with AES/CBC/PKCS5Padding. This is not a good naming since PKCS#5 padding supports 8-byte block sizes as DES and PKCS#7 supports up to 255 bytes. For Java use this;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
The #5 and #7 are not interchangeable for the most modern block ciphers as AES is a 128-bit block cipher. See the question on Crypto.StackExchange.
and, for using AES with 256-bit key size;
Java standard cipher library limited to 128-bit key size. You must go and download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6
Hi I have written nodejs encryption and java encryption by using same algorithm on both the side. But Java and NodeJS are returning different encrypted string. Please help me here.
//Here is my Java Code
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public enum AESUtil {
;
private static final String ENCRYPTION_KEY = "RwcmlVpg";
private static final String ENCRYPTION_IV = "4e5Wa71fYoT7MFEX";
public static String encrypt(String src) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, makeKey(), makeIv());
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(cipher.doFinal(src.getBytes()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
Base64.Decoder decoder = Base64.getDecoder();
decrypted = new String(cipher.doFinal(decoder.decode(src)));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
static AlgorithmParameterSpec makeIv() {
try {
return new IvParameterSpec(ENCRYPTION_IV.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
static Key makeKey() {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] key = md.digest(ENCRYPTION_KEY.getBytes("UTF-8"));
return new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
//Below is the code to test above code
public class AESMain {
/**
* #param args
*/
public static void main(String[] args) {
String src = "Hello,CryptWorld";
String encrypted = AESUtil.encrypt(src);
String decrypted = AESUtil.decrypt(encrypted);
System.out.println("src: " + src);
System.out.println("encrypted: " + encrypted);
System.out.println("decrypted: " + decrypted);
}
}
Response from the above code is
src: Hello,CryptWorld
encrypted: rh7ro9NH1XZeLX95paLETDgYxRbnDoOIrxarO0Sy73s=
decrypted: Hello,CryptWorld
//Node JS Code
var Encrypt, crypto;
crypto = require("crypto");
Encrypt = module.exports = (function() {
var b64dec, b64enc, cipher, decrypt, encrypt, iv, key;
key = crypto.createHash("sha256").update("RwcmlVpg").digest();
iv = '4e5Wa71fYoT7MFEX';
cipher = function(mode, data) {
var encipher, encoded;
encipher = crypto[mode]("aes-256-cbc", key, iv);
encoded = encipher.update(data);
encoded += encipher.final();
return encoded;
};
encrypt = function(data) {
return b64enc(cipher("createCipheriv", data));
};
decrypt = function(data) {
return cipher("createDecipheriv", b64dec(data));
};
b64enc = function(data) {
var b;
b = new Buffer(data, "binary");
return b.toString("base64");
};
b64dec = function(data) {
var b;
b = new Buffer(data, "base64");
return b.toString("binary");
};
return {
encrypt: encrypt,
decrypt: decrypt
};
})();
var expected = Encrypt.encrypt("Hello,CryptWorld");
console.log("expected " + expected);
The Response from Node JS is
expected /R79/f1H/XZeLX95/f39TDgY/Rb9Dv39/Rb9O0T9/Xs=
The node js version is v6.10.1 and JDK version 1.8.0_77.
I really don't know what I am missing.
I'm not a javascript or node.js expert, but I think the problem is that the cipher.update() and cipher.final() are returning instances of Buffer, not string. Therefore you must use Buffer.concat(...) to concatenate them, i.e
cipher = function (mode, data) {
var encipher, encoded;
encipher = crypto[mode]("aes-256-cbc", key, iv);
cipher1 = encipher.update(data);
cipher2 = encipher.final();
return Buffer.concat([cipher1, cipher2]);
};
Additionally, you should never use this String.getBytes() method nor this String(byte[]) constructor in any code that strives to achieve portability or interoperability. Instead, always specify the charset explicitly. I would recommend UTF_8 exclusively, e.g. so use String.getBytes(StandardCharsets.UTF_8) and new String(byte[], StandardCharsets.UTF_8).
My first hint would be to make sure that you exactly the same:
character encoding
line endings
in both of your programs, for the text being encrypted and for the keys. Try printing the text buffers and keys as hex and compare those before you even do any encryption. If they differ then there's your problem. If they're the same then it may be a problem with the encryption itself.
Note that Node uses UTF-8 by default while Java uses UCS-2 internally as far as I know. I see you're making some attempts to convert the encoding but double check the results in both envirements for bot the keys and the clear text just before the encryption step.
Also make sure that you're not encrypting base64 representation of the string instead of the string itself, or that if you use base64 for the key then you do it consistently.
Also, print both keys before the encryption. You're building them programmatically so make sure you know what they are at the end.
If you make sure that you have the same keys, the same messages, the same encoding, line endings and representation (hex, base64 etc.) then use some online tool for AES encryption like:
http://aesencryption.net/
http://aes.online-domain-tools.com/
https://www.browserling.com/tools/aes-encrypt
and compare which of your program does the job correctly.
I'm trying to implement a function that receives a string and returns the encoded values of the String in CAST-256. The following code is what i implement following the example on BoncyCastle official web page (http://www.bouncycastle.org/specifications.html , point 4.1).
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.CAST6Engine;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
public class Test {
static{
Security.addProvider(new BouncyCastleProvider());
}
public static final String UTF8 = "utf-8";
public static final String KEY = "CLp4j13gADa9AmRsqsXGJ";
public static byte[] encrypt(String inputString) throws UnsupportedEncodingException {
final BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CAST6Engine());
byte[] key = KEY.getBytes(UTF8);
byte[] input = inputString.getBytes(UTF8);
cipher.init(true, new KeyParameter(key));
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
try {
cipher.doFinal(cipherText, outputLen);
} catch (CryptoException ce) {
System.err.println(ce);
System.exit(1);
}
return cipherText;
}
public static void main(String[] args) throws UnsupportedEncodingException {
final String toEncrypt = "hola";
final String encrypted = new String(Base64.encode(test(toEncrypt)),UTF8);
System.out.println(encrypted);
}
}
But , when i run my code i get
QUrYzMVlbx3OK6IKXWq1ng==
and if you encode hola in CAST-256 with the same key ( try here if you want http://www.tools4noobs.com/online_tools/encrypt/) i should get
w5nZSYEyA8HuPL5V0J29Yg==.
What is happening? Why im getting a wront encrypted string?
I'm tired of find that on internet and didnt find a answer.
Bouncy Castle uses PKCS #7 padding by default, while PHP's mcrypt (and the web site you linked) uses zero padding by default. This causes the different ciphertexts.
Please note that the ECB mode used here is not secure for almost any use. Additionally, I hope the secret key you posted is not the real key, because now that it's not secret anymore, all this encryption is useless.
This doesn't really answer your question, but it does provide some pointers.
You need to do a little digging to ensure you are decrypting in exactly the same way as PHP's mcrypt(). You need to make sure your key generation, encoding/decoding and cipher algorithm match exactly.
Keys
"CLp4j13gADa9AmRsqsXGJ".getBytes("UTF-8");
is probably not the right way to create the key source bytes. The docs seem to indicate that mcrypt() pads the key and data with \0 if it isn't the right size. Note that your method produces a 168 bit key, which is not a valid key size and I'm not sure what java is going to do about it.
Algorithm
Make sure the cipher mode and padding are the same. Does mcrypt() use ECB, CBC, something else?
Encoding
Ciphers work on bytes, not Strings. Make sure your conversion between the two is the same in java and PHP.
Here is a reference test for CAST6 using test vectors from https://www.rfc-editor.org/rfc/rfc2612#page-10. Note the key, ciphertext and plaintext are hex encoded.
import java.security.Provider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Cast6 {
static final String KEY_ALGO = "CAST6";
static final String CIPHER_ALGO = "CAST6/ECB/NOPADDING";
static String keytext = "2342bb9efa38542c0af75647f29f615d";
static String plaintext = "00000000000000000000000000000000";
static String ciphertext = "c842a08972b43d20836c91d1b7530f6b";
static Provider bc = new BouncyCastleProvider();
public static void main(String[] args) throws Exception {
System.out.println("encrypting");
String actual = encrypt();
System.out.println("actual: " + actual);
System.out.println("expect: " + ciphertext);
System.out.println("decrypting");
actual = decrypt();
System.out.println("actual: " + actual);
System.out.println("expect: " + plaintext);
}
static String encrypt() throws Exception {
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, bc);
byte[] keyBytes = Hex.decodeHex(keytext.toCharArray());
SecretKeySpec key = new SecretKeySpec(keyBytes, KEY_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] input = Hex.decodeHex(plaintext.toCharArray());
byte[] output = cipher.doFinal(input);
String actual = Hex.encodeHexString(output);
return actual;
}
static String decrypt() throws Exception {
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, bc);
byte[] keyBytes = Hex.decodeHex(keytext.toCharArray());
SecretKeySpec key = new SecretKeySpec(keyBytes, KEY_ALGO);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] output = cipher.doFinal(Hex.decodeHex(ciphertext.toCharArray()));
String actual = Hex.encodeHexString(output);
return actual;
}
}