Here is some Java code that hashes a salted password and prints the first byte to the console. The printed value is 62.
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
class AuthTest
{
public static void main (String[] args) throws java.lang.Exception
{
byte[] result;
byte[] salt = new byte[] { (byte)0xe3, (byte)0x2c, (byte)0xf8, (byte)0x9e, (byte)0x6f, (byte)0xe4, (byte)0xf8, (byte)0x90 };
byte[] password = "password".getBytes("UTF-8");
result = getHash(1105, password, salt);
System.out.println(result[0]);
}
public static byte[] getHash(int iterations, byte[] password, byte[] salt) throws NoSuchAlgorithmException,
UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.reset();
md.update(salt);
byte[] result = md.digest(password);
for (int i = 0; i < iterations; i++) {
md.reset();
result = md.digest(result);
}
return result;
}
}
The following C# code, which I thought was identical to the above, yields the value 40.
void Main()
{
byte[] salt = new byte[] { (byte)0xe3, (byte)0x2c, (byte)0xf8, (byte)0x9e, (byte)0x6f, (byte)0xe4, (byte)0xf8, (byte)0x90 };
byte[] password = Encoding.UTF8.GetBytes("password");
var result = GetHash(1105, password, salt);
Console.WriteLine(result[0]);
}
public byte[] GetHash(int iterations, byte[] password, byte[] salt)
{
var saltedPassword = password.Concat(salt).ToArray();
using (var sha1 = new SHA1CryptoServiceProvider())
{
var result = sha1.ComputeHash(saltedPassword);
for (int i = 0; i < iterations; ++i)
{
result = sha1.ComputeHash(result);
}
return result;
}
}
Could someone please help me spot the difference?
One of the major difference is that in Java, byte is signed while in C#, byte is unsigned. The equivalent for Java byte in C# is sbyte, not byte.
If you want to get the same result as Java in C#, use sbyte for every byte you use in Java.
In the Java version you have the salt followed by the password. In the C# version this is reversed.
Related
I am looking for any sample java code that will decrypt the messages encrypted using "openssl enc -aes-256-cbc) -a -salt" command provided the key is known.
https://pastebin.com/YiwbCAW8
So far i was able to get the following java code that encrypts and also decrypts the message. But i am not able to decrypt the encrypted message using openssl command. Getting "Bad Magic Number" error. Any idea ?
Encrypt the message using the code >
Encrypt("sample text", "test$password") = "i+5zkPPgnDdV7fr/w8uHkw=="
Decrypt("i+5zkPPgnDdV7fr/w8uHkw==", "test$password") = "sample text"
Decrypt the message using openssl >
F:\cipher>echo i+5zkPPgnDdV7fr/w8uHkw== | openssl aes-256-cbc -a -salt -d
enter aes-256-cbc decryption password:
bad magic number
import java.security.spec.KeySpec;
import java.util.Base64;
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;
public class AES {
private static final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 256;
private Cipher ecipher;
private Cipher dcipher;
AES(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, secret);
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return Base64.getEncoder().encodeToString(encrypted);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
byte[] bytes = Base64.getDecoder().decode(encrypt);
byte[] decrypted = decrypt(bytes);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
public static void main(String[] args) throws Exception {
String message = "sample text";
String password = "test$password";
AES encrypter = new AES(password);
String encrypted = encrypter.encrypt(message);
String decrypted = encrypter.decrypt(encrypted);
System.out.println("Encrypt(\"" + message + "\", \"" + password + "\") = \"" + encrypted + "\"");
System.out.println("Decrypt(\"" + encrypted + "\", \"" + password + "\") = \"" + decrypted + "\"");
}
}
You may search stackoverflow for many similar questions.
you have multiple issues in your code:
You use different keys:
In Java you use PBKDF2 to generate an encryption key from the provided password. Openssl uses its EVP_BytesToKey. Search internet for Java implementation. Please note the hash used in the EVP_BytesToKey changed with some openssl version (from MD5 to SHA-1 SHA-256), if someone is having more details, please comment
And you use random IV. you don't pass the IV along the ciphertext, so you may be able to decrypt the ciphertext with the same cipher instance (kkeping the same iv), but lets try your Java code to decrypt your ciphertext other time or with other instance, it won't work. You need to pass IV along the ciphertext (usually it's prepended)
Openssl expect following format:
Salted_<8 byte salt>ciphertext
Salted__<8 byte salt>ciphertext
8 byte salt is a random byte array used to generate the encryption key and IV from the provided password. Try encrypt with openssl with -pparameter, it will print the salt, IV and Key generated so you can check and compare
Using CBC without any integrity check (hmac, ..) may be unsafe in many implementations
Suggestions:
you can find an openssl java library implementing the same required (EVP_BytesToKey)
you can implement EVP_BytesToKey yourself
you can use openssl directly with -K/-ivparameters providing the encryption key and IV (in hex format) instead of password, then openssl expects pure ciphertext (no Salted_ or salt inside the input)
Thanks a lot for the clues. As mentioned, did some search and modified the code from one of the post. I have seen similar code with EVP_BytesToKeys in many places, but took some time to figure out the usage. I am able to decrypt the msg encrypted by openssl.
Trying to search the code for encryption as well. Meanwhile any help of encryption is appreciated as well.
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AES5 {
private static final Charset ASCII = Charset.forName("ASCII");
private static final int INDEX_KEY = 0;
private static final int INDEX_IV = 1;
private static final int ITERATIONS = 1;
private static final int SALT_OFFSET = 8;
private static final int SALT_SIZE = 8;
private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;
private static final int KEY_SIZE_BITS = 256;
/**
* Thanks go to Ola Bini for releasing this source on his blog. The source was
* obtained from here
*
*/
public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data,
int count) {
byte[][] both = new byte[2][];
byte[] key = new byte[key_len];
int key_ix = 0;
byte[] iv = new byte[iv_len];
int iv_ix = 0;
both[0] = key;
both[1] = iv;
byte[] md_buf = null;
int nkey = key_len;
int niv = iv_len;
int i = 0;
if (data == null) {
return both;
}
int addmd = 0;
for (;;) {
md.reset();
if (addmd++ > 0) {
md.update(md_buf);
}
md.update(data);
if (null != salt) {
md.update(salt, 0, 8);
}
md_buf = md.digest();
for (i = 1; i < count; i++) {
md.reset();
md.update(md_buf);
md_buf = md.digest();
}
i = 0;
if (nkey > 0) {
for (;;) {
if (nkey == 0)
break;
if (i == md_buf.length)
break;
key[key_ix++] = md_buf[i];
nkey--;
i++;
}
}
if (niv > 0 && i != md_buf.length) {
for (;;) {
if (niv == 0)
break;
if (i == md_buf.length)
break;
iv[iv_ix++] = md_buf[i];
niv--;
i++;
}
}
if (nkey == 0 && niv == 0) {
break;
}
}
for (i = 0; i < md_buf.length; i++) {
md_buf[i] = 0;
}
return both;
}
public static byte[][] getKeyIV(byte[] headerSaltAndCipherText, Cipher aesCBC, String password) {
byte[] salt = Arrays.copyOfRange(headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[][] keyAndIV=null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
keyAndIV = EVP_BytesToKey(KEY_SIZE_BITS / Byte.SIZE, aesCBC.getBlockSize(), md5, salt,
password.getBytes(ASCII), ITERATIONS);
} catch (Exception e) {e.printStackTrace();}
return keyAndIV;
}
// https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
public static String decrypt(String encryptedMsg, String password) {
String decryptedMsg =null;
byte[] headerSaltAndCipherText = Base64.decodeBase64(encryptedMsg);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
try {
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
decryptedMsg = new String(decrypted, ASCII);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
//TODO - Encrypt the msg in same manner as "openssl enc -aes-256-cbc -a -salt"
public static String encrypt(String msg, String password) {
String decryptedMsg =null;
byte[] headerSaltAndCipherText = Base64.decodeBase64(msg);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
try {
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
aesCBC.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
decryptedMsg = new String(decrypted, ASCII);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
public static void main(String[] args) {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(encrypt(msg, password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
System.out.println(decrypt(encryptedMsg, password));
}
}
Also got an improved solution from the following site. Got the code for both encryption and decryption for now...
http://qaru.site/questions/19874/java-equivalent-of-an-openssl-aes-cbc-encryption
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
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;
import org.apache.commons.codec.binary.Base64;
import static java.nio.charset.StandardCharsets.*;
/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a
* shared key (aka password) with symetric ciphers.
*/
public class OpenSslAesQu {
/** OpenSSL magic initial bytes. */
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
public static String encryptAndURLEncode(String password, String clearText) {
String encrypted = null;
try {
encrypted = URLEncoder.encode(encrypt(password, clearText),UTF_8.name());
} catch (Exception e) {e.printStackTrace();}
return encrypted;
}
/**
*
* #param password The password / key to encrypt with.
* #param data The data to encrypt
* #return A base64 encoded string containing the encrypted data.
*/
public static String encrypt(String password, String clearText) {
String encryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] inBytes = clearText.getBytes(UTF_8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] data = cipher.doFinal(inBytes);
data = array_concat(array_concat(SALTED_MAGIC, salt), data);
//return Base64.getEncoder().encodeToString( data );
encryptedMsg = org.apache.commons.codec.binary.Base64.encodeBase64String(data);
} catch(Exception e) {e.printStackTrace();}
return encryptedMsg;
}
/**
* #see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption for what looks like a useful answer. The not-yet-commons-ssl also has an implementation
* #param password
* #param source The encrypted data
*/
public static String decrypt(String password, String source) {
String decryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
//final byte[] inBytes = Base64.getDecoder().decode(source);
final byte[] inBytes = Base64.decodeBase64(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
decryptedMsg = new String(clear, UTF_8);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
private static byte[] array_concat(final byte[] a, final byte[] b) {
final byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(">> "+encrypt(password,msg));
//System.out.println("<< "+decrypt(encrypt(msg, password), password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
String encryptedMsg2 = "U2FsdGVkX1/B6oOznz5+nd7W/qXwXI7G7rhj5o9pjx8MS0TXp9SNxO3AhM9HBJ/z";
System.out.println(decrypt(password,encryptedMsg));
System.out.println(decrypt(password,encryptedMsg2));
System.out.println(decrypt(password,encrypt(password,msg)));
}
}
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 would like to know the code to do this in java please?
This is what i have so far but it does not work?
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static jdk.nashorn.tools.ShellFunctions.input;
public class Sha256hash
{
public static String main(String[] args) throws NoSuchAlgorithmException
{
MessageDigest md = MessageDigest.getInstance("SHA1");
md.reset();
byte[] buffer = input.getBytes("UTF-8");
md.update(buffer);
byte[] digest = md.digest();
String hexStr = "";
for (int i = 0; i < digest.length; i++) {
hexStr += Integer.toString( ( digest[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return hexStr;
}
}
I'm still unclear whether you want SHA-1 or SHA-256, so let's abstract the problem; firstly, an encode method to take a byte[] and return the hex (don't worry, you already wrote it; but I would prefer a StringBuilder over String concatenation. Java String is immutable, so you're creating garbage for later garbage collection with +) -
private static String encodeHex(byte[] digest) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
Next, we can create a method that takes the algorithm name and the String to digest and returns that digest. Like
public static String digest(String alg, String input) {
try {
MessageDigest md = MessageDigest.getInstance(alg);
byte[] buffer = input.getBytes("UTF-8");
md.update(buffer);
byte[] digest = md.digest();
return encodeHex(digest);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
return e.getMessage();
}
}
Then we can get a SHA-1 or a SHA-256 hash like
public static void main(String[] args) {
System.out.println(digest("SHA1", ""));
System.out.println(digest("SHA-256", ""));
}
Which outputs (as expected)
da39a3ee5e6b4b0d3255bfef95601890afd80709
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
The main entry point can not return String. Furthermore, input is not declared. You maybe want to change the name of your function to generate with input as a parameter.
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static jdk.nashorn.tools.ShellFunctions.input;
public class Sha256hash
{
public static String generate(String input) throws NoSuchAlgorithmException
{
MessageDigest md = MessageDigest.getInstance("SHA1");
md.reset();
byte[] buffer = input.getBytes("UTF-8");
md.update(buffer);
byte[] digest = md.digest();
String hexStr = "";
for (int i = 0; i < digest.length; i++) {
hexStr += Integer.toString( ( digest[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return hexStr;
}
}
This example works for me returning c3499c2729730a7f807efb8676a92dcb6f8a3f8f as result of processing the string example:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Sha256hash
{
public static String generate(String input) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
MessageDigest md = MessageDigest.getInstance("SHA1");
md.reset();
byte[] buffer = input.getBytes("UTF-8");
md.update(buffer);
byte[] digest = md.digest();
String hexStr = "";
for (int i = 0; i < digest.length; i++) {
hexStr += Integer.toString( ( digest[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return hexStr;
}
}
Main:
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
public class Tester {
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String someText = "example";
System.out.println(Sha256hash.generate(someText));
}
}
Finally, as Elliott has pointed out If you want to use SHA-256 you should change MessageDigest.getInstance("SHA1"); to MessageDigest.getInstance("SHA256"); Right now you are using SHA-1. Also pointed by Elliot you should use StringBuilder in the loop for improved efficiency.
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 wrote this simple Java program which encrypt a string and output the hex value of the iv, salt, derived key and cipher text.
public class tmp{
static Cipher encryptionCipher;
static String RANDOM_ALGORITHM = "SHA1PRNG";
static String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
static String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
static String SECRET_KEY_ALGORITHM = "AES";
static int PBE_ITERATION_COUNT = 2048;
static String PROVIDER = "BC";
public static byte[] generateIv() {
try{
SecureRandom random;
random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] iv = new byte[16];
random.nextBytes(iv);
return iv;
} catch(Exception e){
return null; // Always must return something
}
}
public static byte[] generateSalt() {
try {SecureRandom random;
random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] salt = new byte[32];
random.nextBytes(salt);
return salt;
} catch(Exception e){
return null; // Always must return something
}
}
public static SecretKey getSecretKey(String password, byte[] salt){
try {
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM, PROVIDER);
SecretKey tmp = factory.generateSecret(pbeKeySpec);
return new SecretKeySpec(tmp.getEncoded(), SECRET_KEY_ALGORITHM);
} catch(Exception e){
System.out.println(e); // Always must return something
return null;
}
}
public static String encrypt(String plaintext, Key key, byte[] iv) {
try {
AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(iv);
encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
encryptionCipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec);
byte[] ciphertext = encryptionCipher.doFinal(plaintext.getBytes("UTF-8"));
String cipherHexString = DatatypeConverter.printHexBinary(ciphertext);
return cipherHexString;
}
catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main (String[] Args){
SecretKey key;
//sha512(ciao)
String encami = "This is a test pharse. Thanks!!";
String password = "a0c299b71a9e59d5ebb07917e70601a3570aa103e99a7bb65a58e780ec9077b1902d1dedb31b1457beda595fe4d71d779b6ca9cad476266cc07590e31d84b206";
byte[] iv = new byte[16];
byte[] salt = new byte[32];
iv = generateIv();
salt = generateSalt();
String ll = DatatypeConverter.printHexBinary(iv);
String lp = DatatypeConverter.printHexBinary(salt);
System.out.println(ll);
System.out.println(lp);
key = getSecretKey(password, salt);
byte tt[] = new byte[32];
tt = key.getEncoded();
String lo = DatatypeConverter.printHexBinary(tt);
System.out.println(lo);
String outenc = encrypt(encami, key, iv);
System.out.println(outenc);
}
}
In the following C program iv and salt are initialized with the values given by the above Java program. No padding needed since the length of the text is 32 bytes.
#include <stdio.h>
#include <gcrypt.h>
#include <stdlib.h>
#include <string.h>
int
main (void)
{
int i;
char *encami = "This is a test pharse. Thanks!!";
char *pwd = "a0c299b71a9e59d5ebb07917e70601a3570aa103e99a7bb65a58e780ec9077b1902d1dedb31b1457beda595fe4d71d779b6ca9cad476266cc07590e31d84b206";
unsigned char iv[] = {};
unsigned char salt[] = {};
int algo = gcry_cipher_map_name("aes256");
unsigned char *devkey = NULL;
unsigned char *enc_buf = NULL;
enc_buf = gcry_malloc(32);
devkey = gcry_malloc_secure (32);
gcry_cipher_hd_t hd;
gcry_cipher_open(&hd, algo, GCRY_CIPHER_MODE_CBC, 0);
gcry_kdf_derive (pwd, strlen(pwd)+1, GCRY_KDF_PBKDF2, GCRY_MD_SHA256, salt, 32, 2048, 32, devkey);
for (i=0; i<32; i++)
printf ("%02x", devkey[i]);
printf("\n");
gcry_cipher_setkey(hd, devkey, 32);
gcry_cipher_setiv(hd, iv, 16);
gcry_cipher_encrypt(hd, enc_buf, strlen(encami)+1, encami, strlen(encami)+1);
for (i=0; i<32; i++)
printf("%02x", enc_buf[i]);
printf("\n");
gcry_cipher_close(hd);
gcry_free(enc_buf);
gcry_free (devkey);
return 0;
}
My problem is that the derived key is not the same in those two programs. Why?
Is the bouncy castle deriving function not working in the same way as gcry_kdf_derive?
Thanks!
I've now looked into the PBEWithSHA256And256BitAES-CBC-BC algorithm in the BC provider, and found that it is not compatible with GCRY_KDF_PBKDF2. The gcrypt algorithm is PKCS#5 2.0 Scheme 2, whereas the BC one is actually implementing PKCS#12.
Actually, I've so far not found a named algorithm in the provider that matches the gcrypt one, however I was able to use the BC API directly to get matching results b/w them, as follows.
Bouncy Castle:
byte[] salt = new byte[8];
Arrays.fill(salt, (byte)1);
PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest());
pGen.init(Strings.toByteArray("password"), salt, 2048);
KeyParameter key = (KeyParameter)pGen.generateDerivedParameters(256);
System.out.println(Hex.toHexString(key.getKey()));
gcrypt:
unsigned char salt[8];
memset(salt, 1, 8);
unsigned char key[32];
gcry_kdf_derive("password", 8, GCRY_KDF_PBKDF2, GCRY_MD_SHA256, salt, 8, 2048, 32, key);
for (int i = 0; i < 32; ++i)
printf("%02x", key[i]);
printf("\n");
which both output:
4182537a153b1f0da1ccb57971787a42537e38dbf2b4aa3692baebb106fc02e8
You appear to be including a terminating NULL character in your count of 32 bytes (encami), which explains the differing outputs. The java version sees a 31-character input and provides a single PKCS#7-padded output block (PKCS#7 will pad the input with a single '1' byte). The C version is passed 32 bytes, including the final '0' byte. So the inputs are different.
I recommend you stop treating the NULL terminator as part of the input; instead apply PKCS#7 padding, as the Java version is doing. I'm not familiar with gcrypt, so I don't know what the typical method is for doing this, but PKCS#7 padding is a quite simple concept in any case.