I am trying to write have the following java function in ruby:
public static byte[] hmac_sha1(byte[] keyBytes, byte[] text)
throws NoSuchAlgorithmException, InvalidKeyException
{
// try {
Mac hmacSha1;
try {
hmacSha1 = Mac.getInstance("HmacSHA1");
} catch (NoSuchAlgorithmException nsae) {
hmacSha1 = Mac.getInstance("HMAC-SHA-1");
}
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmacSha1.init(macKey);
System.out.println("Algorithm [" + macKey.getAlgorithm() + "] key [" + Helper.bytesToString(macKey.getEncoded()) + "]");
System.out.println("Final text: " + Helper.bytesToString(text));
byte[] hash = hmacSha1.doFinal(text);
System.out.println("Hash: " + Helper.bytesToString(hash));
return hash;
}
I added the System.out.println, here is the output:
Algorithm [RAW] key [3132333435363738393031323334353637383930]
Final text: 0000000000000000
Hash: cc93cf18508d94934c64b65d8ba7667fb7cde4b0
Now in ruby I try
require 'openssl'
#
# text: 0000000000000000
# Key bytes: 3132333435363738393031323334353637383930
# Wanted hash = cc93cf18508d94934c64b65d8ba7667fb7cde4b0
digest = OpenSSL::Digest::Digest.new('sha1')
secret = "12345678901234567890"
secret2 = "3132333435363738393031323334353637383930"
text = "0000000000000000"
puts OpenSSL::HMAC.hexdigest(digest, secret, text)
puts OpenSSL::HMAC.hexdigest(digest, secret, "0")
puts OpenSSL::HMAC.hexdigest(digest, secret2, "0")
puts OpenSSL::HMAC.hexdigest(digest, secret2, text)
puts "Wanted hash: cc93cf18508d94934c64b65d8ba7667fb7cde4b0"
None of the hashes match, I know it's something to do with encodings etc. How can I match the java HMAC ?
Actually, I ended up using an utility class from from commons-codec-1.5.jar, as follows:
import org.apache.commons.codec.binary.Hex;
// (...)
Hex.encodeHexString(rawBytes);
Java code:
import java.io.IOException;
import java.io.File;
import java.io.DataInputStream;
import java.io.FileInputStream ;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Stack
{
public static String hashToHexString(byte[] hash)
{
StringBuffer hexString = new StringBuffer();
for (int i = 0; i 0) {
hexString.append('0');
}
hexString.append(hexByte);
}
return hexString.toString();
}
public static void main(String[] args)
throws NoSuchAlgorithmException, InvalidKeyException
{
String secret = "12345678901234567890";
byte[] keyBytes = secret.getBytes();
String movingFact = "0";
byte[] text = movingFact.getBytes();
Mac hmacSha1;
try {
hmacSha1 = Mac.getInstance("HmacSHA1");
} catch (Exception nsae) {
hmacSha1 = Mac.getInstance("HMAC-SHA-1");
}
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmacSha1.init(macKey);
byte[] hash = hmacSha1.doFinal(text);
String hexString = hashToHexString(hash);
System.out.println(hexString);
}
}
Ruby code:
require 'openssl'
digest = OpenSSL::Digest::Digest.new('sha1')
secret = "12345678901234567890"
movingFactor = "0"
hash = OpenSSL::HMAC.hexdigest(digest, secret, movingFactor)
puts "Hash: #{hash}"
output:
Java:
DANIELs-MacBook-Air:del dani$ javac Stack.java
DANIELs-MacBook-Air:del dani$ java Stack
32a67f374525d32d0ce13e3db42b5b4a3f370cce
Ruby:
DANIELs-MacBook-Air:del dani$ ruby Stack.rb
Hash: 32a67f374525d32d0ce13e3db42b5b4a3f370cce
Done, the problem was that the java version was not translated into hexstrings properly.
Just a compiling version of the accepted answer if anybody else needs it.
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException {
String secret = "12345678901234567890";
byte[] text = "0".getBytes();
Mac hmacSha1 = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(secret.getBytes(), "HmacSHA1");
hmacSha1.init(macKey);
byte[] hash = hmacSha1.doFinal(text);
String hexString = toHexString(hash);
System.out.println(hexString);
}
private static String toHexString(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
return formatter.toString();
}
Related
I am developing a Java application and I want to add a special encryption method - NTRU. Before that I used symmetric and asymmetric methods supplied by the bouncycastle provider however now I can't understand how works Post-Quantum algorithms in bouncycastle due to lack of any information on the network. Please help me implement NTRU text encryption code.
I tried using the light-weight API but it didn't work, my code refuses to do anything
This is a complete running example for NTRU, it is not a general encryption algorithm but a key exchange algorithm (KEM). To get this to work you need an actual Bouncy Castle lib available (I tested with BC verson 1.71 and 1.72).
It is taken from my Android app to test some Post Quantum Algorithms (PQC) with complete code here: https://github.com/MichaelsPlayground/PostQuantumCryptographyBc172
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
import org.bouncycastle.jcajce.spec.KEMExtractSpec;
import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionKeyGenerationParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionKeyPairGenerator;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionPrivateKeyParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEncryptionPublicKeyParameters;
import org.bouncycastle.pqc.legacy.crypto.ntru.NTRUEngine;
import org.bouncycastle.util.Arrays;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.KeyGenerator;
public class PqcNtruKem {
public static void main(String[] args) {
//Security.addProvider(new BouncyCastleProvider());
// we do need the regular Bouncy Castle file that includes the PQC provider
// get Bouncy Castle here: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
// tested with BC version 1.71
if (Security.getProvider("BCPQC") == null) {
Security.addProvider(new BouncyCastlePQCProvider());
}
System.out.println("PQC NTRU kem");
System.out.println("\n************************************\n" +
"* # # SERIOUS SECURITY WARNING # # *\n" +
"* This program is a CONCEPT STUDY *\n" +
"* for the algorithm *\n" +
"* NTRU [key exchange mechanism] *\n" +
"* The program is using an *\n" +
"* parameter set that I cannot *\n" +
"* check for the correctness of the *\n" +
"* output and other details *\n" +
"* *\n" +
"* DO NOT USE THE PROGRAM IN *\n" +
"* ANY PRODUCTION ENVIRONMENT *\n" +
"************************************");
// as there are 7 parameter sets available the program runs all of them
NTRUEncryptionKeyGenerationParameters[] ntruEncryptionKeyGenerationParameterSets = {
NTRUEncryptionKeyGenerationParameters.EES1087EP2,
NTRUEncryptionKeyGenerationParameters.EES1171EP1,
NTRUEncryptionKeyGenerationParameters.EES1499EP1,
NTRUEncryptionKeyGenerationParameters.APR2011_439,
NTRUEncryptionKeyGenerationParameters.APR2011_439_FAST,
NTRUEncryptionKeyGenerationParameters.APR2011_743,
NTRUEncryptionKeyGenerationParameters.APR2011_743_FAST,
};
// short name of the parameters for the summary print out
String[] ntruEncryptionKeyGenerationParameterNames = {
"EES1087EP2",
"EES1171EP1",
"EES1499EP1",
"APR2011_439",
"APR2011_439_FAST",
"APR2011_743",
"APR2011_743_FAST"
};
// statistics
int nrOfSpecs = ntruEncryptionKeyGenerationParameterSets.length;
String[] parameterSpecName = new String[nrOfSpecs];
int[] privateKeyLength = new int[nrOfSpecs];
int[] publicKeyLength = new int[nrOfSpecs];
int[] encryptedKeyLength = new int[nrOfSpecs];
boolean[] encryptionKeysEquals = new boolean[nrOfSpecs];
// data to encrypt is usually a 32 bytes long (randomly generated) AES key
String keyToEncryptString = "1234567890ABCDEF1122334455667788";
byte[] keyToEncrypt = keyToEncryptString.getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < nrOfSpecs; i++) {
// generation of the NTRU key pair
NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters = ntruEncryptionKeyGenerationParameterSets[i];
String ntruParameterSpecName = ntruEncryptionKeyGenerationParameterNames[i];
parameterSpecName[i] = ntruParameterSpecName;
System.out.println("\nNTRU KEM with parameterset " + ntruParameterSpecName);
AsymmetricCipherKeyPair keyPair = generateNtruKeyPair(ntruEncryptionKeyGenerationParameters);
// get private and public key
AsymmetricKeyParameter privateKey = keyPair.getPrivate();
AsymmetricKeyParameter publicKey = keyPair.getPublic();
// storing the key as byte array
byte[] privateKeyByte = ((NTRUEncryptionPrivateKeyParameters) privateKey).getEncoded();
byte[] publicKeyByte = ((NTRUEncryptionPublicKeyParameters) publicKey).getEncoded();
System.out.println("\ngenerated private key length: " + privateKeyByte.length);
System.out.println("generated public key length: " + publicKeyByte.length);
privateKeyLength[i] = privateKeyByte.length;
publicKeyLength[i] = publicKeyByte.length;
// generate the keys from a byte array
NTRUEncryptionPrivateKeyParameters privateKeyLoad = getNtruPrivateKeyFromEncoded(privateKeyByte, ntruEncryptionKeyGenerationParameters);
NTRUEncryptionPublicKeyParameters publicKeyLoad = getNtruPublicKeyFromEncoded(publicKeyByte, ntruEncryptionKeyGenerationParameters);
// generate the encryption key and the encapsulated key
System.out.println("\nEncryption side: generate the encryption key");
byte[] encryptedKey = pqcNtruEncryptKey(publicKeyLoad, keyToEncrypt);
System.out.println("encrypted key length: " + encryptedKey.length
+ " key: " + bytesToHex(encryptedKey));
encryptedKeyLength[i] = encryptedKey.length;
System.out.println("\nDecryption side: receive the encrypted key and decrypt it to the decryption key");
byte[] decryptedKey = pqcNtruDecryptKey(privateKeyLoad, encryptedKey);
System.out.println("decryption key length: " + decryptedKey.length + " key: " + bytesToHex(decryptedKey));
boolean keysAreEqual = Arrays.areEqual(keyToEncrypt, decryptedKey);
System.out.println("decrypted key is equal to keyToEncrypt: " + keysAreEqual);
encryptionKeysEquals[i] = keysAreEqual;
}
System.out.println("\nTest results");
System.out.println("parameter spec name priKL pubKL encKL keyE");
for (int i = 0; i < nrOfSpecs; i++) {
System.out.format("%-20s%6d%8d%6d%6b%n", parameterSpecName[i], privateKeyLength[i], publicKeyLength[i], encryptedKeyLength[i], encryptionKeysEquals[i]);
}
System.out.println("Legend: priKL privateKey length, pubKL publicKey length, encKL encryption key length, keyE encryption keys are equal\n");
}
private static AsymmetricCipherKeyPair generateNtruKeyPair(NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters) {
NTRUEncryptionKeyPairGenerator ntruEncryptionKeyPairGenerator = new NTRUEncryptionKeyPairGenerator();
ntruEncryptionKeyPairGenerator.init(ntruEncryptionKeyGenerationParameters);
AsymmetricCipherKeyPair kp = ntruEncryptionKeyPairGenerator.generateKeyPair();
return kp;
}
private static byte[] pqcNtruEncryptKey(AsymmetricKeyParameter publicKey, byte[] keyToEncrypt) {
NTRUEngine ntru = new NTRUEngine();
ntru.init(true, publicKey);
try {
return ntru.processBlock(keyToEncrypt, 0, keyToEncrypt.length);
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return null;
}
}
private static byte[] pqcNtruDecryptKey(AsymmetricKeyParameter privateKey, byte[] encryptedKeyToDecrypt) {
NTRUEngine ntru = new NTRUEngine();
ntru.init(false, privateKey);
try {
return ntru.processBlock(encryptedKeyToDecrypt, 0, encryptedKeyToDecrypt.length);
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return null;
}
}
public static SecretKeyWithEncapsulation pqcGenerateNtruEncryptionKey(PublicKey publicKey) {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("Frodo", "BCPQC");
keyGen.init(new KEMGenerateSpec((PublicKey) publicKey, "AES"), new SecureRandom());
SecretKeyWithEncapsulation secEnc1 = (SecretKeyWithEncapsulation) keyGen.generateKey();
return secEnc1;
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
}
public static byte[] pqcGenerateFrodoDecryptionKey(PrivateKey privateKey, byte[] encapsulatedKey) {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("Frodo", "BCPQC");
keyGen.init(new KEMExtractSpec((PrivateKey) privateKey, encapsulatedKey, "AES"), new SecureRandom());
SecretKeyWithEncapsulation secEnc2 = (SecretKeyWithEncapsulation) keyGen.generateKey();
return secEnc2.getEncoded();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
}
private static NTRUEncryptionPrivateKeyParameters getNtruPrivateKeyFromEncoded(byte[] encodedKey, NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters) {
NTRUEncryptionParameters ntruEncryptionParameters = ntruEncryptionKeyGenerationParameters.getEncryptionParameters();
try {
return new NTRUEncryptionPrivateKeyParameters(encodedKey, ntruEncryptionParameters);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static NTRUEncryptionPublicKeyParameters getNtruPublicKeyFromEncoded(byte[] encodedKey, NTRUEncryptionKeyGenerationParameters ntruEncryptionKeyGenerationParameters) {
NTRUEncryptionParameters ntruEncryptionParameters = ntruEncryptionKeyGenerationParameters.getEncryptionParameters();
return new NTRUEncryptionPublicKeyParameters(encodedKey, ntruEncryptionParameters);
}
private static PublicKey getFrodoPublicKeyFromEncoded(byte[] encodedKey) {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("Frodo", "BCPQC");
return keyFactory.generatePublic(x509EncodedKeySpec);
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
When I encrypt plain text using Dart,and encrypted text is decrypted from Java code, I get this error:
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at AesUtil.doFinal(AesUtil.java:75)
at AesUtil.decrypt(AesUtil.java:60)
at Main.main(Main.java:18)
Same IV, salt and passphase value using Java side for key generation, but the generated key is different and also cipher test is different. I am using same method for key generation. I don't know what is missing in Dart code.
dependencies:
encrypt: ^5.0.1
hex: ^0.2.0
password_hash_plus: ^4.0.0
Dart code is:
var random = Random.secure();
var values = List<int>.generate(16, (i) => random.nextInt(255));
// final salt = aes.IV.fromSecureRandom(16);
final salt = hex.encode(values);
final generator = PBKDF2(hashAlgorithm: sha1);
final key = aes.Key.fromBase64(generator.generateBase64Key("1234567891234567", salt, 1000, 16));
final iv = aes.IV.fromSecureRandom(16);
final encrypter =
aes.Encrypter(aes.AES(key, mode: aes.AESMode.cbc, padding: 'PKCS7'));
final encrypted = encrypter.encrypt(st.password!, iv: iv);
var str = '${iv.base16}::${salt}::${encrypted.base64}';
var bytes = utf8.encode(str);
var base64Str = base64.encode(bytes);
//final decrypt = encrypter.decrypt64("/vvAYMc3rgCvPvuSVU/qQw==", iv: iv);
print(
'------------------------------,\n encrypt ${(encrypted.base64)}-----------'
//'--\ndecrypted ${decrypt}-----------base64--------$base64Str-----'
'\nkey = ${key.base64} array--\niv = ${iv.base16}--salt= {${salt}');
And Java code is:
class Main {
public static void main(String[] args) {
AesUtil aesUtil = new AesUtil();
String encrypt = aesUtil.encrypt("b9266c74df614967d9acaa2878bff87c", "6ab7c799d6411f9d0c8e048ad526eeee", "1234567891234567", "Jitu#123456");
String a = aesUtil.decrypt("01e6a073a4255c92e704bd94d76d75c5", "98a21e07ed34afc523c5f5938c9202db", "1234567891234567", "MumTfpnzZh9bk94yiTuA+g==");
System.out.println("encrypt = " + encrypt + " \ndecrpty valaue----" + a);
}
}
Encryption code in Java:
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Component;
public class AesUtil {
private final int keySize;
private final int iterationCount;
private final Cipher cipher;
public AesUtil() {
this.keySize = 128;
this.iterationCount = 1000;
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
throw fail(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public String encrypt(String salt, String iv, String passphrase, String plaintext) {
try {
SecretKey key = generateKey(salt, passphrase);
System.out.println("encryption key-------= " + base64(key.getEncoded()));
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("ISO-8859-1"));
return base64(encrypted);
} catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
System.out.println("decrypt key-------= " + base64(key.getEncoded()));
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return new String(decrypted, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
return null;
} catch (Exception e) {
return null;
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
IvParameterSpec IivParameterSpec = new IvParameterSpec(hex(iv));
System.out.println("----iv--= " + hex(IivParameterSpec.getIV()));
cipher.init(encryptMode, key, IivParameterSpec);
return cipher.doFinal(bytes);
} catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
e.printStackTrace();
return null;
}
}
private SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] s = hex(salt);
System.out.println("salt-= " + hex(s));
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), s, iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
return null;
}
}
public static String random(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return hex(salt);
}
public static String base64(byte[] bytes) {
return Base64.encodeBase64String(bytes);
}
public static byte[] base64(String str) {
return Base64.decodeBase64(str);
}
public static String hex(byte[] bytes) {
return Hex.encodeHexString(bytes);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
} catch (DecoderException e) {
throw new IllegalStateException(e);
}
}
private IllegalStateException fail(Exception e) {
return null;
}
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte) 0);
}
}
}
The decryption fails because in both codes different salts are used and therefore different keys are generated. Ultimately, this is due to an inappropriate design of the PBKDF2 implementation of the password_hash_plus Dart library.
In the Java code a random salt is applied, in this case 0xb9266c74df614967d9acaa2878bff87c. In main(), the salt is passed hex encoded to encrypt(), hex decoded in generateKey(), and the resulting byte sequence is used for key derivation.
The generateBase64Key() method of the password_hash_plus library, on the other hand, expects the salt as string and does a UTF-8 encoding internally, see here. Therefore, only salts that are UTF-8 decodable can be processed. This is generally not true for random salts, since these are corrupted by a UTF-8 decoding.
The hex encoding of the salt applied in the Dart code does not work either, of course, because generateBase64Key() does not perform a hex decoding internally but a UTF-8 encoding.
Since salts are generally random byte sequences, the design of the PBKDF2 implementation of the password_hash_plus library is unsuitable. Instead, an implementation is required where the salt is passed as byte sequence (Uint8List or List<int>), e.g. the PBKDF2 implementation of PointyCastle:
import 'package:pointycastle/export.dart';
import 'dart:typed_data';
...
final key = aes.Key(deriveKey("1234567891234567", Uint8List.fromList(values))); // Raw salt for key derivation!
final salt = hex.encode(values); // Hex encoded salt for output!
...
Uint8List deriveKey(String passphrase, Uint8List salt){
Uint8List passphraseBytes = Uint8List.fromList(utf8.encode(passphrase));
KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64)); // 64 byte block size
Pbkdf2Parameters params = Pbkdf2Parameters(salt, 1000, 16); // 16 byte key size
derivator.init(params);
return derivator.process(passphraseBytes);
}
...
The Dart code returns salt and IV hex encoded and the ciphertext Base64 encoded. If this data is passed with these encodings to the decrypt() method of the Java code, decryption is successful.
Be aware that an iteration count of 1000 is generally too small for PBKDF2.
I am developing a REST API in which the data to be passed to the server need to be encrypted with 'AES/CBC/PKCS5Padding'. The REST API client side is using Java to encrypt and decrypt the data, while on my side I am using PHP.
The secret key used here is hash with sha1. I have the same secret key value with the client side and also the IV value is the same, but when I try to encrypt and compare it to the client, it's different.
JAVA Code:
import java.util.Arrays;
import java.security.*;
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.lang.Object;
class Main {
public static String stringToHex(String base)
{
StringBuffer buffer = new StringBuffer();
int intValue;
for(int x = 0; x < base.length(); x++)
{
int cursor = 0;
intValue = base.charAt(x);
String binaryChar = new String(Integer.toBinaryString(base.charAt(x)));
for(int i = 0; i < binaryChar.length(); i++)
{
if(binaryChar.charAt(i) == '1')
{
cursor += 1;
}
}
if((cursor % 2) > 0)
{
intValue += 128;
}
buffer.append(Integer.toHexString(intValue) + " ");
}
return buffer.toString();
}
public static String bytesToHex(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for(byte b: a)
sb.append(String.format("%02x", b));
return sb.toString();
}
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException,
InvalidAlgorithmParameterException,
NoSuchProviderException,
NoSuchPaddingException,
InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException
{
MessageDigest sha = MessageDigest.getInstance("SHA-1");
String secretKey = "mysecretkey102018";
String message = "00155001C"
byte[] key = sha.digest(secretKey.getBytes("UTF-8"));
byte[] value = message.getBytes();
key = Arrays.copyOf(key, 16);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec iv = new IvParameterSpec("AAAAAAAAAAAAAAAA".getBytes("UTF-8"));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
System.out.println(bytesToHex(cipher.doFinal(value)));
}
PHP Code:
class IDebit
{
private $iv = "AAAAAAAAAAAAAAAA";
private $secret_key = "mysecretkey102018";
private $message = "00155001C";
public function generateEncrytedKeyId()
{
$hashPassword = substr(hash("SHA1", $secret_key), 0, 32);
$blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$str = $this->pkcs5_pad($message, $blocksize);
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashPassword, $str, MCRYPT_MODE_CBC, self::IV);
return bin2hex($encrypted);
}
protected function pkcs5_pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
}
The encryption result on Java is c1e7af770d9d0af59cc75d1c76aa78f6, while on PHP is d67a15a027be7e7ab68ea6ab88ea4f2f
I'm wondering what is wrong with my code. I have googled and check on this forum about the question dozens of times but I am unable to get the same result. I have rechecked my code with the answers posted by people on this forum and it's identical.
I even use the AES algorithm to encrypt and decrypt files, but according to my research, the performance of this algorithm is slower than the RC4 algorithm in Java.
I'm use this code for encrypt files in C#
public static class RC4
{
public static byte[] Encrypt(byte[] key, byte[] data)
{
return EncryptOutput(key, data).ToArray();
}
private static byte[] EncryptInitalize(byte[] key)
{
byte[] s = Enumerable.Range(0, 256)
.Select(i => (byte)i)
.ToArray();
for (int i = 0, j = 0; i < 256; i++)
{
j = (j + key[i % key.Length] + s[i]) & 255;
Swap(s, i, j);
}
return s;
}
private static IEnumerable<byte> EncryptOutput(byte[] key, IEnumerable<byte> data)
{
byte[] s = EncryptInitalize(key);
int i = 0;
int j = 0;
return data.Select((b) =>
{
i = (i + 1) & 255;
j = (j + s[i]) & 255;
Swap(s, i, j);
return (byte)(b ^ s[(s[i] + s[j]) & 255]);
});
}
private static void Swap(byte[] s, int i, int j)
{
byte c = s[i];
s[i] = s[j];
s[j] = c;
}
}
I need to encrypt a file in C # and decrypt this file with java, but found no implementation for both languages.
This solution implemented by Michael Remijan showed better performance to decrypt files using AES. Encrypt and Decrypt files for I implemented just a string conversion to byte array.
Java Code
package org.ferris.aes.crypto;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
*
* #author Michael Remijan mjremijan#yahoo.com #mjremijan
*/
public class AesBase64Wrapper {
private static String IV = "IV_VALUE_16_BYTE";
private static String PASSWORD = "PASSWORD_VALUE";
private static String SALT = "SALT_VALUE";
public String encryptAndEncode(String raw) {
try {
Cipher c = getCipher(Cipher.ENCRYPT_MODE);
byte[] encryptedVal = c.doFinal(getBytes(raw));
String s = getString(Base64.encodeBase64(encryptedVal));
return s;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
public String decodeAndDecrypt(String encrypted) throws Exception {
byte[] decodedValue = Base64.decodeBase64(getBytes(encrypted));
Cipher c = getCipher(Cipher.DECRYPT_MODE);
byte[] decValue = c.doFinal(decodedValue);
return new String(decValue);
}
private String getString(byte[] bytes) throws UnsupportedEncodingException {
return new String(bytes, "UTF-8");
}
private byte[] getBytes(String str) throws UnsupportedEncodingException {
return str.getBytes("UTF-8");
}
private Cipher getCipher(int mode) throws Exception {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = getBytes(IV);
c.init(mode, generateKey(), new IvParameterSpec(iv));
return c;
}
private Key generateKey() throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
char[] password = PASSWORD.toCharArray();
byte[] salt = getBytes(SALT);
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
byte[] encoded = tmp.getEncoded();
return new SecretKeySpec(encoded, "AES");
}
}
C# Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace EncryptDecryptTest
{
class Program
{
class AesBase64Wrapper
{
private static string IV = "IV_VALUE_16_BYTE";
private static string PASSWORD = "PASSWORD_VALUE";
private static string SALT = "SALT_VALUE";
public static string EncryptAndEncode(string raw)
{
using (var csp = new AesCryptoServiceProvider())
{
ICryptoTransform e = GetCryptoTransform(csp, true);
byte[] inputBuffer = Encoding.UTF8.GetBytes(raw);
byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
string encrypted = Convert.ToBase64String(output);
return encrypted;
}
}
public static string DecodeAndDecrypt(string encrypted)
{
using (var csp = new AesCryptoServiceProvider())
{
var d = GetCryptoTransform(csp, false);
byte[] output = Convert.FromBase64String(encrypted);
byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
string decypted = Encoding.UTF8.GetString(decryptedOutput);
return decypted;
}
}
private static ICryptoTransform GetCryptoTransform(AesCryptoServiceProvider csp, bool encrypting)
{
csp.Mode = CipherMode.CBC;
csp.Padding = PaddingMode.PKCS7;
var spec = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(PASSWORD), Encoding.UTF8.GetBytes(SALT), 65536);
byte[] key = spec.GetBytes(16);
csp.IV = Encoding.UTF8.GetBytes(IV);
csp.Key = key;
if (encrypting)
{
return csp.CreateEncryptor();
}
return csp.CreateDecryptor();
}
}
static void Main(string[] args)
{
string encryptMe;
string encrypted;
string decrypted;
encryptMe = "please encrypt me";
Console.WriteLine("encryptMe = " + encryptMe);
encrypted = AesBase64Wrapper.EncryptAndEncode(encryptMe);
Console.WriteLine("encypted: " + encrypted);
decrypted = AesBase64Wrapper.DecodeAndDecrypt(encrypted);
Console.WriteLine("decrypted: " + decrypted);
Console.WriteLine("press any key to exit....");
Console.ReadKey();
}
}
}
Based on your comments, I am assuming you want to know how to speed up your encryption / decryption process, and changing the main algorithm is not mandatory.
You could look at different modes for AES. For example, AES in counter (CTR) mode is significantly faster than cipher block chaining (CBC) which is often used.
Try creating your cipher like
Cipher myCipher = Cipher.getInstance("AES/CTR/NoPadding");
and you should see a performance increase. Additionally, using NoPadding will keep the size the same as the plaintext.
(Yes, I know that CTR mode turn AES into a stream cipher, never mind my comment)
UPDATE
I have used this in the past along these lines:
Key key = new SecretKeySpec(yourKeyValue, "AES");
Cipher enc = Cipher.getInstance("AES/CTR/NoPadding");
enc.init(Cipher.ENCRYPT_MODE, key);
// Get the IV that was generated
byte[] iv = enc.getIV();
// Encrypt your data
...
Cipher dec = Cipher.getInstance("AES/CTR/NoPadding");
dec.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
// Decrypt your data
...
I am encrypting a text using CryptoJS AES algorithm on the client side and I am decrypting It on Server side in java, I am getting the exception.
JS code :
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
console.info("encrypted " + encrypted);
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
var plainText = decrypted.toString(CryptoJS.enc.Utf8)
console.info("decrypted " + plainText);
js output :
encrypted U2FsdGVkX1/uYgVsNZmpbgKQJ8KD+8R8yyYn5+irhoI=
decrypted Message
Java Code :
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.regex.Pattern;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AESJavaScript {
private SecretKeySpec key;
private Cipher cipher;
private int size = 128;
private static final Charset CHARSET = Charset.forName("UTF-8");
public AESJavaScript() throws NoSuchAlgorithmException,
NoSuchPaddingException, NoSuchProviderException {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(size); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
key = new SecretKeySpec(raw, "AES/CTR/NoPadding");
cipher = Cipher.getInstance("AES/CTR/NoPadding");
}
public void setKey(String keyText) {
byte[] bText = new byte[size];
bText = keyText.getBytes(CHARSET);
key = new SecretKeySpec(bText, "AES/CTR/NoPadding");
}
public String encrypt(String message) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(message.getBytes());
return byteArrayToHexString(encrypted);
}
public String decrypt(String hexCiphertext)
throws IllegalBlockSizeException, BadPaddingException,
InvalidKeyException {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(hexStringToByteArray(hexCiphertext));
return byteArrayToHexString(decrypted);
}
private static String byteArrayToHexString(byte[] raw) {
String hex = "0x";
String s = new String(raw);
for (int x = 0; x < s.length(); x++) {
char[] t = s.substring(x, x + 1).toCharArray();
hex += Integer.toHexString((int) t[0]).toUpperCase();
}
return hex;
}
private static byte[] hexStringToByteArray(String hex) {
Pattern replace = Pattern.compile("^0x");
String s = replace.matcher(hex).replaceAll("");
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
public static void main(String[] args) {
try {
AESJavaScript ajs = new AESJavaScript();
ajs.setKey("Secret Passphrase");
String hexCiphertext = "U2FsdGVkX1/uYgVsNZmpbgKQJ8KD+8R8yyYn5+irhoI=";
String decrypted = ajs.decrypt(hexCiphertext);
System.out.println("decrypted > " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Exception is :
java.security.InvalidKeyException: Invalid AES key length: 17 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at com.test.jenkins.jenkinsRestart.AESJavaScript.decrypt(AESJavaScript.java:49)
at com.test.jenkins.jenkinsRestart.AESJavaScript.main(AESJavaScript.java:82)
Is there anything that I am doing wrong here or Is there anyother simple way to do these kind of encryption and decryption ?
Your Java code is riddled with errors. Here are a few of them:
Your initial exception is caused because your "Secret Passphrase" string contains 17 bytes. You need to truncate this to 16 bytes (or pad it to match 24 or 32 bytes). You need to match the behaviour of the CryptoJS library.
You are trying to hex-decode data that appears to be base64 encoded. Try using DatatypeConverter.parseBase64Binary(hex);.
You are creating a secret key with the algorithm "AES/CTR/NoPadding". This in invalid, just use "AES".
You must pass an IV/nonce value into your decryption:
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(...));
The value you use will depend upon what CryptoJS is doing. Perhaps it uses all zeroes? Perhaps it generates a random one and you need to store it with the ciphertext?
This should be enough to get you started.