Implementing AES encrypt and decrypt using hibernate - java

In my web application I am asking user to upload the files. I am reading that file and converting it into byte array like this
byte[] fileContent =new byte[(int)uploadedFile.length()];
FileInputStream fin=new FileInputStream(uploadedFile);
fin.read(fileContent);
Now I encrypt this byte array using AES encryption with the password supplies by the user. I am using this library to encrypt. After encryption I am saving this byte array into hibernate database. I have this filed in my Files entity class.
#Lob
#Basic(fetch=FetchType.LAZY)
#Column(name = "filedata", columnDefinition = "LONGBLOB")
private byte[] fileData;
I am saving this by save method
session=sessionFactory.openSession();
session.beginTransaction();
session.save(f);
session.getTransaction().commit();
session.close();
When I retrive the file from database I use get method
session=sessionFactory.openSession();
session.beginTransaction();
cr=session.createCriteria(Files.class);
Files f=(Files) cr.add(Restrictions.eq("fid", fid)).uniqueResult();
session.getTransaction().commit();
session.close();
After retrieving I am decrypting the byte array that is in the File object but I am getting Input file is corrupt error. I understand this error is because the byte array has changed from saving to getting.
What would be the solution to this issue. I want the user to supply password for encrypting and decrypting. I am using MySql database at the backend.
EDITS: AESCrypt class
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.NetworkInterface;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Enumeration;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.stereotype.Repository;
/**
* This class provides methods to encrypt and decrypt files using
* aescrypt file format,
* version 1 or 2.
* <p>
* Requires Java 6 and <a href="http://java.sun.com/javase/downloads/index.jsp">Java
* Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</a>.
* <p>
* Thread-safety and sharing: this class is not thread-safe.<br>
* <tt>AESCrypt</tt> objects can be used as Commands (create, use once and dispose),
* or reused to perform multiple operations (not concurrently though).
*
*/
#Repository
public final class AESCrypt {
private static final String JCE_EXCEPTION_MESSAGE = "Please make sure "
+ "\"Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files\" "
+ "(http://java.sun.com/javase/downloads/index.jsp) is installed on your JRE.";
private static final String RANDOM_ALG = "SHA1PRNG";
private static final String DIGEST_ALG = "SHA-256";
private static final String HMAC_ALG = "HmacSHA256";
private static final String CRYPT_ALG = "AES";
private static final String CRYPT_TRANS = "AES/CBC/NoPadding";
private static final byte[] DEFAULT_MAC =
{0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd, (byte) 0xef};
private static final int KEY_SIZE = 32;
private static final int BLOCK_SIZE = 16;
private static final int SHA_SIZE = 32;
private final boolean DEBUG;
private byte[] password;
private Cipher cipher;
private Mac hmac;
private SecureRandom random;
private MessageDigest digest;
private IvParameterSpec ivSpec1;
private SecretKeySpec aesKey1;
private IvParameterSpec ivSpec2;
private SecretKeySpec aesKey2;
/*******************
* PRIVATE METHODS *
*******************/
/**
* Prints a debug message on standard output if DEBUG mode is turned on.
* #param message
*/
protected void debug(String message) {
if (DEBUG) {
System.out.println("[DEBUG] " + message);
}
}
/**
* Prints a debug message on standard output if DEBUG mode is turned on.
* #param message
* #param bytes
*/
protected void debug(String message, byte[] bytes) {
if (DEBUG) {
StringBuilder buffer = new StringBuilder("[DEBUG] ");
buffer.append(message);
buffer.append("[");
for (int i = 0; i < bytes.length; i++) {
buffer.append(bytes[i]);
buffer.append(i < bytes.length - 1 ? ", " : "]");
}
System.out.println(buffer.toString());
}
}
/**
* Generates a pseudo-random byte array.
* #param len
* #return pseudo-random byte array of <tt>len</tt> bytes.
*/
protected byte[] generateRandomBytes(int len) {
byte[] bytes = new byte[len];
random.nextBytes(bytes);
return bytes;
}
/**
* SHA256 digest over given byte array and random bytes.<br>
* <tt>bytes.length</tt> * <tt>num</tt> random bytes are added to the digest.
* <p>
* The generated hash is saved back to the original byte array.<br>
* Maximum array size is {#link #SHA_SIZE} bytes.
* #param bytes
* #param num
*/
protected void digestRandomBytes(byte[] bytes, int num) {
assert bytes.length <= SHA_SIZE;
digest.reset();
digest.update(bytes);
for (int i = 0; i < num; i++) {
random.nextBytes(bytes);
digest.update(bytes);
}
System.arraycopy(digest.digest(), 0, bytes, 0, bytes.length);
}
/**
* Generates a pseudo-random IV based on time and this computer's MAC.
* <p>
* This IV is used to crypt IV 2 and AES key 2 in the file.
* #return IV.
*/
protected byte[] generateIv1() {
byte[] iv = new byte[BLOCK_SIZE];
long time = System.currentTimeMillis();
byte[] mac = null;
try {
Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
while (mac == null && ifaces.hasMoreElements()) {
mac = ifaces.nextElement().getHardwareAddress();
}
} catch (Exception e) {
// Ignore.
}
if (mac == null) {
mac = DEFAULT_MAC;
}
for (int i = 0; i < 8; i++) {
iv[i] = (byte) (time >> (i * 8));
}
System.arraycopy(mac, 0, iv, 8, mac.length);
digestRandomBytes(iv, 256);
return iv;
}
/**
* Generates an AES key starting with an IV and applying the supplied user password.
* <p>
* This AES key is used to crypt IV 2 and AES key 2.
* #param iv
* #param password
* #return AES key of {#link #KEY_SIZE} bytes.
*/
protected byte[] generateAESKey1(byte[] iv, byte[] password) {
byte[] aesKey = new byte[KEY_SIZE];
System.arraycopy(iv, 0, aesKey, 0, iv.length);
for (int i = 0; i < 8192; i++) {
digest.reset();
digest.update(aesKey);
digest.update(password);
aesKey = digest.digest();
}
return aesKey;
}
/**
* Generates the random IV used to crypt file contents.
* #return IV 2.
*/
protected byte[] generateIV2() {
byte[] iv = generateRandomBytes(BLOCK_SIZE);
digestRandomBytes(iv, 256);
return iv;
}
/**
* Generates the random AES key used to crypt file contents.
* #return AES key of {#link #KEY_SIZE} bytes.
*/
protected byte[] generateAESKey2() {
byte[] aesKey = generateRandomBytes(KEY_SIZE);
digestRandomBytes(aesKey, 32);
return aesKey;
}
/**
* Utility method to read bytes from a stream until the given array is fully filled.
* #param in
* #param bytes
* #throws IOException if the array can't be filled.
*/
protected void readBytes(InputStream in, byte[] bytes) throws IOException {
if (in.read(bytes) != bytes.length) {
throw new IOException("Unexpected end of file");
}
}
/**************
* PUBLIC METHODS *
**************/
/**
* Builds an object to encrypt or decrypt files with the given password.
* #param password
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
* #throws UnsupportedEncodingException if UTF-16 encoding is not supported.
*/
public AESCrypt(String password) throws GeneralSecurityException, UnsupportedEncodingException {
this(false, password);
}
/**
* Builds an object to encrypt or decrypt files with the given password.
* #param debug
* #param password
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
* #throws UnsupportedEncodingException if UTF-16 encoding is not supported.
*/
public AESCrypt(boolean debug, String password) throws GeneralSecurityException, UnsupportedEncodingException {
try {
DEBUG = debug;
setPassword(password);
random = SecureRandom.getInstance(RANDOM_ALG);
digest = MessageDigest.getInstance(DIGEST_ALG);
cipher = Cipher.getInstance(CRYPT_TRANS);
hmac = Mac.getInstance(HMAC_ALG);
} catch (GeneralSecurityException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
/**
* Changes the password this object uses to encrypt and decrypt.
* #param password
* #throws UnsupportedEncodingException if UTF-16 encoding is not supported.
*/
public void setPassword(String password) throws UnsupportedEncodingException {
this.password = password.getBytes("UTF-16LE");
debug("Using password: ", this.password);
}
/**
* The file at <tt>fromPath</tt> is encrypted and saved at <tt>toPath</tt> location.
* <p>
* <tt>version</tt> can be either 1 or 2.
* #param version
* #param fromPath
* #param toPath
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void encrypt(int version, String fromPath, String toPath)
throws IOException, GeneralSecurityException {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(fromPath));
debug("Opened for reading: " + fromPath);
out = new BufferedOutputStream(new FileOutputStream(toPath));
debug("Opened for writing: " + toPath);
encrypt(version, in, out);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
/**
* The input stream is encrypted and saved to the output stream.
* <p>
* <tt>version</tt> can be either 1 or 2.<br>
* None of the streams are closed.
* #param version
* #param in
* #param out
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void encrypt(int version, InputStream in, OutputStream out)
throws IOException, GeneralSecurityException {
try {
byte[] text = null;
ivSpec1 = new IvParameterSpec(generateIv1());
aesKey1 = new SecretKeySpec(generateAESKey1(ivSpec1.getIV(), password), CRYPT_ALG);
ivSpec2 = new IvParameterSpec(generateIV2());
aesKey2 = new SecretKeySpec(generateAESKey2(), CRYPT_ALG);
debug("IV1: ", ivSpec1.getIV());
debug("AES1: ", aesKey1.getEncoded());
debug("IV2: ", ivSpec2.getIV());
debug("AES2: ", aesKey2.getEncoded());
out.write("AES".getBytes("UTF-8")); // Heading.
out.write(version); // Version.
out.write(0); // Reserved.
if (version == 2) { // No extensions.
out.write(0);
out.write(0);
}
out.write(ivSpec1.getIV()); // Initialization Vector.
text = new byte[BLOCK_SIZE + KEY_SIZE];
cipher.init(Cipher.ENCRYPT_MODE, aesKey1, ivSpec1);
cipher.update(ivSpec2.getIV(), 0, BLOCK_SIZE, text);
cipher.doFinal(aesKey2.getEncoded(), 0, KEY_SIZE, text, BLOCK_SIZE);
out.write(text); // Crypted IV and key.
debug("IV2 + AES2 ciphertext: ", text);
hmac.init(new SecretKeySpec(aesKey1.getEncoded(), HMAC_ALG));
text = hmac.doFinal(text);
out.write(text); // HMAC from previous cyphertext.
debug("HMAC1: ", text);
cipher.init(Cipher.ENCRYPT_MODE, aesKey2, ivSpec2);
hmac.init(new SecretKeySpec(aesKey2.getEncoded(), HMAC_ALG));
text = new byte[BLOCK_SIZE];
int len, last = 0;
while ((len = in.read(text)) > 0) {
cipher.update(text, 0, BLOCK_SIZE, text);
hmac.update(text);
out.write(text); // Crypted file data block.
last = len;
}
last &= 0x0f;
out.write(last); // Last block size mod 16.
debug("Last block size mod 16: " + last);
text = hmac.doFinal();
out.write(text); // HMAC from previous cyphertext.
debug("HMAC2: ", text);
} catch (InvalidKeyException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
/**
* The file at <tt>fromPath</tt> is decrypted and saved at <tt>toPath</tt> location.
* <p>
* The input file can be encrypted using version 1 or 2 of aescrypt.<br>
* #param fromPath
* #param toPath
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void decrypt(String fromPath, String toPath)
throws IOException, GeneralSecurityException {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(fromPath));
debug("Opened for reading: " + fromPath);
out = new BufferedOutputStream(new FileOutputStream(toPath));
debug("Opened for writing: " + toPath);
decrypt(new File(fromPath).length(), in, out);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
/**
* The input stream is decrypted and saved to the output stream.
* <p>
* The input file size is needed in advance.<br>
* The input stream can be encrypted using version 1 or 2 of aescrypt.<br>
* None of the streams are closed.
* #param inSize
* #param in
* #param out
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void decrypt(long inSize, InputStream in, OutputStream out)
throws IOException, GeneralSecurityException {
try {
byte[] text = null, backup = null;
long total = 3 + 1 + 1 + BLOCK_SIZE + BLOCK_SIZE + KEY_SIZE + SHA_SIZE + 1 + SHA_SIZE;
int version;
text = new byte[3];
readBytes(in, text); // Heading.
if (!new String(text, "UTF-8").equals("AES")) {
throw new IOException("Invalid file header");
}
version = in.read(); // Version.
if (version < 1 || version > 2) {
throw new IOException("Unsupported version number: " + version);
}
debug("Version: " + version);
in.read(); // Reserved.
if (version == 2) { // Extensions.
text = new byte[2];
int len;
do {
readBytes(in, text);
len = ((0xff & (int) text[0]) << 8) | (0xff & (int) text[1]);
if (in.skip(len) != len) {
throw new IOException("Unexpected end of extension");
}
total += 2 + len;
debug("Skipped extension sized: " + len);
} while (len != 0);
}
text = new byte[BLOCK_SIZE];
readBytes(in, text); // Initialization Vector.
ivSpec1 = new IvParameterSpec(text);
aesKey1 = new SecretKeySpec(generateAESKey1(ivSpec1.getIV(), password), CRYPT_ALG);
debug("IV1: ", ivSpec1.getIV());
debug("AES1: ", aesKey1.getEncoded());
cipher.init(Cipher.DECRYPT_MODE, aesKey1, ivSpec1);
backup = new byte[BLOCK_SIZE + KEY_SIZE];
readBytes(in, backup); // IV and key to decrypt file contents.
debug("IV2 + AES2 ciphertext: ", backup);
text = cipher.doFinal(backup);
ivSpec2 = new IvParameterSpec(text, 0, BLOCK_SIZE);
aesKey2 = new SecretKeySpec(text, BLOCK_SIZE, KEY_SIZE, CRYPT_ALG);
debug("IV2: ", ivSpec2.getIV());
debug("AES2: ", aesKey2.getEncoded());
hmac.init(new SecretKeySpec(aesKey1.getEncoded(), HMAC_ALG));
backup = hmac.doFinal(backup);
text = new byte[SHA_SIZE];
readBytes(in, text); // HMAC and authenticity test.
if (!Arrays.equals(backup, text)) {
throw new IOException("Message has been altered or password incorrect");
}
debug("HMAC1: ", text);
total = inSize - total; // Payload size.
if (total % BLOCK_SIZE != 0) {
throw new IOException("Input file is corrupt");
}
if (total == 0) { // Hack: empty files won't enter block-processing for-loop below.
in.read(); // Skip last block size mod 16.
}
debug("Payload size: " + total);
cipher.init(Cipher.DECRYPT_MODE, aesKey2, ivSpec2);
hmac.init(new SecretKeySpec(aesKey2.getEncoded(), HMAC_ALG));
backup = new byte[BLOCK_SIZE];
text = new byte[BLOCK_SIZE];
for (int block = (int) (total / BLOCK_SIZE); block > 0; block--) {
int len = BLOCK_SIZE;
if (in.read(backup, 0, len) != len) { // Cyphertext block.
throw new IOException("Unexpected end of file contents");
}
cipher.update(backup, 0, len, text);
hmac.update(backup, 0, len);
if (block == 1) {
int last = in.read(); // Last block size mod 16.
debug("Last block size mod 16: " + last);
len = (last > 0 ? last : BLOCK_SIZE);
}
out.write(text, 0, len);
}
out.write(cipher.doFinal());
backup = hmac.doFinal();
text = new byte[SHA_SIZE];
readBytes(in, text); // HMAC and authenticity test.
if (!Arrays.equals(backup, text)) {
throw new IOException("Message has been altered or password incorrect");
}
debug("HMAC2: ", text);
} catch (InvalidKeyException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
}

Finally solved it. This code was triggering the exception.
total = inSize - total; // Payload size.
if (total % BLOCK_SIZE != 0) {
throw new IOException("Input file is corrupt");
}
Apparently I was passing the inSize parameter as file size but after encryption the file size increases by roughly about 134 bytes. So that modulus doesn't come out zero. I have checked the original, encrypted, the data that was passed to hibernate and the data that hibernate returns there is no single point of conflict. The hibernate returns the same byte data when I retrieve it using get or criteria that was passed at the time of saving into database.
Hope this helps others who are trying to use AES encryption and decryption along with hibernate.

Related

Ruby: Large data RSA Encryption

so I need to encrypt strings using RSA.
The example of the string (this is verified correct string):
"1658135277&{\"user_id\":\"f257bf74-8c14-4144-aca5-69e1d8819d9d\",\"user_id_type\":\"3\",\"user_name\":\"{\\\"first_name\\\":\\\"SEAN\\\",\\\"middle_name\\\":\\\"\\\",\\\"last_name\\\":\\\"YEO\\\",\\\"full_name\\\":\\\"SEAN YEO\\\"}\",\"user_dob\":\"1993-03-31\",\"nationality\":\"SG\",\"user_address\":\"{\\\"region\\\":\\\"SG\\\",\\\"address_line1\\\":\\\"200 Jalan Sultan, 03-02, Textile Centre\\\"}\"}"
The ruby code:
def get_base64_encryption(unix_timestamp, params)
str = "#{unix_timestamp}&#{params.to_json}"
public_key = OpenSSL::PKey::RSA.new(PUBLIC_KEY_PEM)
encrypted_str = public_key.public_encrypted(str, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
Base64.strict_encode64(encrypted_str)
end
But this way, it will raise error OpenSSL::PKey::RSAError (data too large for key size)
The thing is, I need to encrypt this for integration with 3rd party API. So there's no way I can use other encryption.
The partner actually gave example code, but in java:
public class RSAUtil {
private static final String KEY_ALGORITHM = "RSA";
private static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
// rsa secret key length 256 bytes(2048bit)
private static final int MAX_DECRYPT_BLOCK = 256;
// rsa encrypt data length
private static final int MAX_ENCRYPT_BLOCK = 200;
/**
* rsa encrypt data
* #param message
* #param publicKeyStr
* #return
*/
public static String rsaEncryptOAEP(String message, String publicKeyStr) {
try {
System.out.println("message_size:" + message.length());
Decoder decoder = Base64.getDecoder();
byte[] publicKeyStrByte = decoder.decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyStrByte);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] inputData = message.getBytes(StandardCharsets.UTF_8);
int inputLen = inputData.length;
byte[] cache = null;
byte[] out = null;
// split data by block size
for (int offSet = 0; offSet < inputLen; offSet += MAX_ENCRYPT_BLOCK) {
if (offSet + MAX_ENCRYPT_BLOCK < inputLen) {
cache = cipher.doFinal(inputData, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(inputData, offSet, inputLen - offSet);
}
out = mergeBytes(out, cache);
}
return Base64.getEncoder().encodeToString(out);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
System.out.println("current java env not support RSA v1.5/OAEP");
e.printStackTrace();
} catch (InvalidKeyException | InvalidKeySpecException e) {
System.out.println("invalid key");
e.printStackTrace();
} catch (IllegalBlockSizeException | BadPaddingException e) {
System.out.println("block size more than 214 ");
e.printStackTrace();
}
return "";
}
/**
* merge 2 bytes
*
* #param first
* #param last
* #return
*/
private static byte[] mergeBytes(byte[] first, byte[] last) {
if (first == null) {
return last;
}
byte[] result = new byte[first.length + last.length];
System.arraycopy(first, 0, result, 0, first.length);
System.arraycopy(last, 0, result, first.length, last.length);
return result;
}
}
But I still failed to translate the example code to ruby, especially because I couldn't find how to encrypt data in streaming fashion using RSA in ruby. Anyone can help? Thank you in advance.
For everyone who comes up with this problem, here is the answer:
def get_base64_encryption(unix_timestamp, params)
str = "#{unix_timestamp}&#{params.to_json}"
public_key = OpenSSL::PKey::RSA.new(PUBLIC_KEY_PEM)
encrypted_str = ''
io = StringIO.new(str)
until io.eof?
chunk = io.read(200)
encrypted_str << public_key.public_encrypt(chunk, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
end
Base64.strict_encode64(encrypted_str)
end
OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING is equal to RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING source

Working with AES Encryption Java to C#

We have the application done C# and passing parameter to Java application. To make it secure we Java application developer come up with AES encryption. The developer gave sample code in Java. Kindly somebody helps me. How can I encrypt same way in C#
// Cryptix imports
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import cryptix.provider.key.RawSecretKey;
import cryptix.util.core.Hex;
import xjava.security.Cipher;
public class AESEncryption {
private Cipher m_alg;
private RawSecretKey m_key;
private static final String RIJNDAEL = "Rijndael"; //mode of encryption
private static final String PROVIDER_CRYPTIX = "Cryptix"; // Cryptography algorithm providers
/**
* Must (once) be dynamically installed. - could alternatively be set
* statically in $JAVAHOME/lib/security by changing security provider.
*
*/
static {
java.security.Security.addProvider(new cryptix.provider.Cryptix());
System.out.println(" Java Security - Add security provider - Cryptix provider added");
}
public AESEncryption(String secretKey)
throws java.security.NoSuchProviderException, java.security.NoSuchAlgorithmException {
//System.out.println("Key used for encryption/decryption :- " + secretKey);
m_alg = Cipher.getInstance(RIJNDAEL, PROVIDER_CRYPTIX);
m_key = new RawSecretKey(RIJNDAEL, Hex.fromString(secretKey));
}
/**
* encrypt supplied string.
*
* #param str to encrypted
* #return encrypted String
*/
public String encrypt(String str) throws java.security.KeyException {
byte[] ect = null;
str = convert(str); // pad if necessary
//System.out.println("String to encrypt [ " + str + " ]");
m_alg.initEncrypt(m_key);
ect = m_alg.crypt(str.getBytes());
//System.out.println("Encrypted String [ " + Hex.toString(ect) + " ]");
return Hex.toString(ect);
}
public String decrypt(String str) throws java.security.KeyException {
byte[] dct = null;
String decryptedString;
//System.out.println("String to decrypt [ " + str + " ]");
m_alg.initDecrypt(m_key);
dct = m_alg.crypt(Hex.fromString(str));
decryptedString = new String(dct);
//System.out.println("Decrypted String [ " + decryptedString + " ]");
return decryptedString.trim();
}
/**
* Internal routine to convert a string to a byte array and to
* pad the byte array (with 0) in order ot fill out the final block.
*
* padding value based on Cipher's defined block size.
*
* #param str
* #return
*/
private String convert(String str) {
int padding = 0, sourceLength = 0, i, blockSize;
sourceLength = str.length();
blockSize = m_alg.blockSize();
padding = blockSize - (sourceLength % blockSize);
if (padding != blockSize) {
for (i = 0; i < padding; i++) {
str = str + " ";
}
}
return str;
}
public static void main(String[] args) {
try {
AESEncryption tcase = new AESEncryption("27323A0825226DDD316881852610DACB81210355C3117DAD83EF5EE9E8602915");
String params ="accentué";
System.out.println("Params Before:" + params);
String encrypted = tcase.encrypt(params);
System.out.println("Params Encrypted:" + encrypted);
String enc = "E669500CF68CCD88CBA54EDAA07E30A57E8C8518101D0D7C36323194222D6B4393AC2B22A914E902F47C89BC0CE1BCE67A14066219C3480875E1D75536AE0009899C23E644F0C701AE6EBB4DB0C1EDCF9A3A7FFA9BE117237083B5EC4B4EE7FBEBB94D7526C4C9E23EA38E2B2B526E8005265817FC505974DACAA55D3AA50FCE3440BFE79447135D071443DAE4013F2C32CDA00F4AFC2194B616FA50A1BF688F53336194B156E709818DD323F574CDA83A0716F7C22572278D9C162DC8DBC000AEF4C972E6FD203706CFFBE6143130CEB78D4F1CD77B64352766A9C2885AF66B1F81C11A4C71B2B1BF662AD854D429A8356B8FD91B1083ACDAD1DDE3BFF1353D108E2E133558324E63EE9DC13CB4B34BEE494C674BC7BB8C7E77B936AB16BC320E303A34B7510A438C9A9D7E212C3CA3F6771A7DD0CC1AB8A9D1B89FD850B343C7E8828A0155C1A30FA78B250EB073000C9D4F39EA8BF6AE8C19A0BA32EC222DF4BC59396F2396E4FBAB4599EE28E63F068E33614CF84D98D1FEF504E5DCC5940CF17294B6910845A9DDD8805833E1DB1C3CBF0658D1388A65546D58CE465E0E4FA941B6635E1A7042048C1B64456C3EAF9D331E3889E55E37CE71201C7B34526FF2C9297E52F7CBCC7603809566AE4AE3AE644BA826A130DA71DD201532C564F0B11E17306DF061DCF74AD6062931AD09D75345B11933D293945557755788E5C39D1485E87B8C20ED983EA977B9D9CDD7D8E3112D858061A6C376FB37A4E6292B0E4640A05A850175382E385F7F095CAFED9DFCBBEA6C10AA571EE8D01D6022CC240CCDD22EA374D619B4191F619DFB574EBFA80BAEC393F7FCD111789C96AA3DFA1E58E60EEB712F4C4A41506E38A7BF9CF24BFDEF2B7FE6E25C1D9ADAC11EA3CCF6F187A7446A9933D4C4BB25DE8467592D2457F7674E13D087B47221C0EBB8716F312FF52E46EA77566364346D2D899228F0C99737C4AD2A95C9CF892F89430CA1EEEFA68CE85321E5A4A44E71B6C4C62C8D3623E6103D9638A7DC0E66A249F130365773A3530F8F8F1FC4C57B4BEC296C1A0DD190646F2F3A427DE54155E6772FADF7A09488F45AE7CBA9C2F90BD3205D97E00C8CF62AB5FBEC774CA457A38E351FF110B8AA799918AE93E864862EE2F3B5D997D6C249613283337D83A60BDF3490BB73EB2A948A71D61E433F58A537693364D131DD7787D4D4A9C6EE891AB5B783A4F7B6009127D72A1F184ADA2BE20647EB6FE15FACDDF43A03BA9FE120E552A2BA14F568D65187C1F2E6108699C405018A3447A149C0A5196504201677E37CE789246D48A5270B59597D9F77F75E7CF23B18B51D5F25E37258BB231CE0E9FD3E5B21D14F1541A76F4875F231038751A36A79C84C4EBF9F2146506EC8DF6EBDD0CFECCBF388D9020CC5656322BF695D3ED716FAF0545040830815B550075F5D2301C6469F5DD99E5FD0093C4A";
//String decrypted = tcase.decrypt(encrypted);
String decrypted = tcase.decrypt(enc);
System.out.println("Params Decrypted:" + decrypted);
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (java.security.KeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
İf you used IV vector in c# while encrypted data with AES . You should use IV vector in Java
c#
using (var random = new System.Security.Cryptography.RNGCryptoServiceProvider())
{
var key = new byte[16];
random.GetBytes(key);
using (System.Security.Cryptography.AesCryptoServiceProvider aesAlg = new System.Security.Cryptography.AesCryptoServiceProvider())
{
aesAlg.BlockSize = 128;
aesAlg.KeySize = 128;
aesAlg.Key = key;
aesAlg.IV = key;
aesAlg.Mode = System.Security.Cryptography.CipherMode.CBC;
aesAlg.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
using (ICryptoTransform iCryptoper = aesAlg.CreateEncryptor())
{
byte[] encryptedData = iCryptoper.TransformFinalBlock(x509CertData, 0, x509CertData.Length);
string encodedCert = Convert.ToBase64String(encryptedData);
System.Security.Cryptography.X509Certificates.X509Certificate2 x509Cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(x509CertData);
System.Security.Cryptography.RSACryptoServiceProvider provider = (System.Security.Cryptography.RSACryptoServiceProvider)x509Cert.PublicKey.Key;
byte[] encrypted = provider.Encrypt(aesAlg.Key, false);
string test = Convert.ToBase64String(encrypted);
}
}
}
java
javax.crypto.SecretKey sc = new javax.crypto.spec.SecretKeySpec(secretKey, "AES");
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
dcipher.init(Cipher.DECRYPT_MODE, sc, ivParameterSpec);
byte[] decyrptedCert = dcipher.doFinal(java.util.Base64.getDecoder().decode(stringToDecrypt));

How do I generate a SALT in Java for Salted-Hash?

I've been looking around and the closest answer is : How to generate a random alpha-numeric string?
I want to follow this workflow according to this CrackStation tutorial:
To Store a Password
Generate a long random salt using a CSPRNG.
Prepend the salt to the password and hash it with a standard cryptographic hash function such as SHA256.
Save both the salt and the hash in the user's database record.
To Validate a Password
Retrieve the user's salt and hash from the database.
Prepend the salt to the given password and hash it using the same hash function.
Compare the hash of the given password with the hash from the database. If they match, the password is correct. Otherwise, the password is incorrect.
I don't know how to generate a SALT. I figured out how to generate a hash using the MessageDigest. I tried using SecureRandom but nextByte method produces garbled code.
Edit: I don't know which answer to choose, they're too complicated for me, I have decided to use jBCrypt; jBCript is easy to use, does all the complex stuff behind the scenes. so I'll let the community vote up for the best answer.
Inspired from this post and that post, I use this code to generate and verify hashed salted passwords. It only uses JDK provided classes, no external dependency.
The process is:
you create a salt with getNextSalt
you ask the user his password and use the hash method to generate a salted and hashed password. The method returns a byte[] which you can save as is in a database with the salt
to authenticate a user, you ask his password, retrieve the salt and hashed password from the database and use the isExpectedPassword method to check that the details match
/**
* A utility class to hash passwords and check passwords vs hashed values. It uses a combination of hashing and unique
* salt. The algorithm used is PBKDF2WithHmacSHA1 which, although not the best for hashing password (vs. bcrypt) is
* still considered robust and recommended by NIST .
* The hashed value has 256 bits.
*/
public class Passwords {
private static final Random RANDOM = new SecureRandom();
private static final int ITERATIONS = 10000;
private static final int KEY_LENGTH = 256;
/**
* static utility class
*/
private Passwords() { }
/**
* Returns a random salt to be used to hash a password.
*
* #return a 16 bytes random salt
*/
public static byte[] getNextSalt() {
byte[] salt = new byte[16];
RANDOM.nextBytes(salt);
return salt;
}
/**
* Returns a salted and hashed password using the provided hash.<br>
* Note - side effect: the password is destroyed (the char[] is filled with zeros)
*
* #param password the password to be hashed
* #param salt a 16 bytes salt, ideally obtained with the getNextSalt method
*
* #return the hashed password with a pinch of salt
*/
public static byte[] hash(char[] password, byte[] salt) {
PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
Arrays.fill(password, Character.MIN_VALUE);
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
} finally {
spec.clearPassword();
}
}
/**
* Returns true if the given password and salt match the hashed value, false otherwise.<br>
* Note - side effect: the password is destroyed (the char[] is filled with zeros)
*
* #param password the password to check
* #param salt the salt used to hash the password
* #param expectedHash the expected hashed value of the password
*
* #return true if the given password and salt match the hashed value, false otherwise
*/
public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) {
byte[] pwdHash = hash(password, salt);
Arrays.fill(password, Character.MIN_VALUE);
if (pwdHash.length != expectedHash.length) return false;
for (int i = 0; i < pwdHash.length; i++) {
if (pwdHash[i] != expectedHash[i]) return false;
}
return true;
}
/**
* Generates a random password of a given length, using letters and digits.
*
* #param length the length of the password
*
* #return a random password
*/
public static String generateRandomPassword(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int c = RANDOM.nextInt(62);
if (c <= 9) {
sb.append(String.valueOf(c));
} else if (c < 36) {
sb.append((char) ('a' + c - 10));
} else {
sb.append((char) ('A' + c - 36));
}
}
return sb.toString();
}
}
You were right regarding how you want to generate salt i.e. its nothing but a random number. For this particular case it would protect your system from possible Dictionary attacks. Now, for the second problem what you could do is instead of using UTF-8 encoding you may want to use Base64. Here, is a sample for generating a hash. I am using Apache Common Codecs for doing the base64 encoding you may select one of your own
public byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
return bytes;
}
public String bytetoString(byte[] input) {
return org.apache.commons.codec.binary.Base64.encodeBase64String(input);
}
public byte[] getHashWithSalt(String input, HashingTechqniue technique, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(technique.value);
digest.reset();
digest.update(salt);
byte[] hashedBytes = digest.digest(stringToByte(input));
return hashedBytes;
}
public byte[] stringToByte(String input) {
if (Base64.isBase64(input)) {
return Base64.decodeBase64(input);
} else {
return Base64.encodeBase64(input.getBytes());
}
}
Here is some additional reference of the standard practice in password hashing directly from OWASP
Another version using SHA-3, I am using bouncycastle:
The interface:
public interface IPasswords {
/**
* Generates a random salt.
*
* #return a byte array with a 64 byte length salt.
*/
byte[] getSalt64();
/**
* Generates a random salt
*
* #return a byte array with a 32 byte length salt.
*/
byte[] getSalt32();
/**
* Generates a new salt, minimum must be 32 bytes long, 64 bytes even better.
*
* #param size the size of the salt
* #return a random salt.
*/
byte[] getSalt(final int size);
/**
* Generates a new hashed password
*
* #param password to be hashed
* #param salt the randomly generated salt
* #return a hashed password
*/
byte[] hash(final String password, final byte[] salt);
/**
* Expected password
*
* #param password to be verified
* #param salt the generated salt (coming from database)
* #param hash the generated hash (coming from database)
* #return true if password matches, false otherwise
*/
boolean isExpectedPassword(final String password, final byte[] salt, final byte[] hash);
/**
* Generates a random password
*
* #param length desired password length
* #return a random password
*/
String generateRandomPassword(final int length);
}
The implementation:
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.apache.log4j.Logger;
import org.bouncycastle.jcajce.provider.digest.SHA3;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public final class Passwords implements IPasswords, Serializable {
/*serialVersionUID*/
private static final long serialVersionUID = 8036397974428641579L;
private static final Logger LOGGER = Logger.getLogger(Passwords.class);
private static final Random RANDOM = new SecureRandom();
private static final int DEFAULT_SIZE = 64;
private static final char[] symbols;
static {
final StringBuilder tmp = new StringBuilder();
for (char ch = '0'; ch <= '9'; ++ch) {
tmp.append(ch);
}
for (char ch = 'a'; ch <= 'z'; ++ch) {
tmp.append(ch);
}
symbols = tmp.toString().toCharArray();
}
#Override public byte[] getSalt64() {
return getSalt(DEFAULT_SIZE);
}
#Override public byte[] getSalt32() {
return getSalt(32);
}
#Override public byte[] getSalt(int size) {
final byte[] salt;
if (size < 32) {
final String message = String.format("Size < 32, using default of: %d", DEFAULT_SIZE);
LOGGER.warn(message);
salt = new byte[DEFAULT_SIZE];
} else {
salt = new byte[size];
}
RANDOM.nextBytes(salt);
return salt;
}
#Override public byte[] hash(String password, byte[] salt) {
Validate.notNull(password, "Password must not be null");
Validate.notNull(salt, "Salt must not be null");
try {
final byte[] passwordBytes = password.getBytes("UTF-8");
final byte[] all = ArrayUtils.addAll(passwordBytes, salt);
SHA3.DigestSHA3 md = new SHA3.Digest512();
md.update(all);
return md.digest();
} catch (UnsupportedEncodingException e) {
final String message = String
.format("Caught UnsupportedEncodingException e: <%s>", e.getMessage());
LOGGER.error(message);
}
return new byte[0];
}
#Override public boolean isExpectedPassword(final String password, final byte[] salt, final byte[] hash) {
Validate.notNull(password, "Password must not be null");
Validate.notNull(salt, "Salt must not be null");
Validate.notNull(hash, "Hash must not be null");
try {
final byte[] passwordBytes = password.getBytes("UTF-8");
final byte[] all = ArrayUtils.addAll(passwordBytes, salt);
SHA3.DigestSHA3 md = new SHA3.Digest512();
md.update(all);
final byte[] digest = md.digest();
return Arrays.equals(digest, hash);
}catch(UnsupportedEncodingException e){
final String message =
String.format("Caught UnsupportedEncodingException e: <%s>", e.getMessage());
LOGGER.error(message);
}
return false;
}
#Override public String generateRandomPassword(final int length) {
if (length < 1) {
throw new IllegalArgumentException("length must be greater than 0");
}
final char[] buf = new char[length];
for (int idx = 0; idx < buf.length; ++idx) {
buf[idx] = symbols[RANDOM.nextInt(symbols.length)];
}
return shuffle(new String(buf));
}
private String shuffle(final String input){
final List<Character> characters = new ArrayList<Character>();
for(char c:input.toCharArray()){
characters.add(c);
}
final StringBuilder output = new StringBuilder(input.length());
while(characters.size()!=0){
int randPicker = (int)(Math.random()*characters.size());
output.append(characters.remove(randPicker));
}
return output.toString();
}
}
The test cases:
public class PasswordsTest {
private static final Logger LOGGER = Logger.getLogger(PasswordsTest.class);
#Before
public void setup(){
BasicConfigurator.configure();
}
#Test
public void testGeSalt() throws Exception {
IPasswords passwords = new Passwords();
final byte[] bytes = passwords.getSalt(0);
int arrayLength = bytes.length;
assertThat("Expected length is", arrayLength, is(64));
}
#Test
public void testGeSalt32() throws Exception {
IPasswords passwords = new Passwords();
final byte[] bytes = passwords.getSalt32();
int arrayLength = bytes.length;
assertThat("Expected length is", arrayLength, is(32));
}
#Test
public void testGeSalt64() throws Exception {
IPasswords passwords = new Passwords();
final byte[] bytes = passwords.getSalt64();
int arrayLength = bytes.length;
assertThat("Expected length is", arrayLength, is(64));
}
#Test
public void testHash() throws Exception {
IPasswords passwords = new Passwords();
final byte[] hash = passwords.hash("holacomoestas", passwords.getSalt64());
assertThat("Array is not null", hash, Matchers.notNullValue());
}
#Test
public void testSHA3() throws UnsupportedEncodingException {
SHA3.DigestSHA3 md = new SHA3.Digest256();
md.update("holasa".getBytes("UTF-8"));
final byte[] digest = md.digest();
assertThat("expected digest is:",digest,Matchers.notNullValue());
}
#Test
public void testIsExpectedPasswordIncorrect() throws Exception {
String password = "givemebeer";
IPasswords passwords = new Passwords();
final byte[] salt64 = passwords.getSalt64();
final byte[] hash = passwords.hash(password, salt64);
//The salt and the hash go to database.
final boolean isPasswordCorrect = passwords.isExpectedPassword("jfjdsjfsd", salt64, hash);
assertThat("Password is not correct", isPasswordCorrect, is(false));
}
#Test
public void testIsExpectedPasswordCorrect() throws Exception {
String password = "givemebeer";
IPasswords passwords = new Passwords();
final byte[] salt64 = passwords.getSalt64();
final byte[] hash = passwords.hash(password, salt64);
//The salt and the hash go to database.
final boolean isPasswordCorrect = passwords.isExpectedPassword("givemebeer", salt64, hash);
assertThat("Password is correct", isPasswordCorrect, is(true));
}
#Test
public void testGenerateRandomPassword() throws Exception {
IPasswords passwords = new Passwords();
final String randomPassword = passwords.generateRandomPassword(10);
LOGGER.info(randomPassword);
assertThat("Random password is not null", randomPassword, Matchers.notNullValue());
}
}
pom.xml (only dependencies):
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.51</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
Here's my solution, i would love anyone's opinion on this, it's simple for beginners
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Scanner;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class Cryptography {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
System.out.print("Password: ");
String strPassword = new Scanner(System.in).nextLine();
byte[] bSalt = Salt();
String strSalt = encoder.encodeToString(bSalt); // Byte to String
System.out.println("Salt: " + strSalt);
System.out.println("String to be hashed: " + strPassword + strSalt);
String strHash = encoder.encodeToString(Hash(strPassword, bSalt)); // Byte to String
System.out.println("Hashed value (Password + Salt value): " + strHash);
}
private static byte[] Salt() {
SecureRandom random = new SecureRandom();
byte salt[] = new byte[6];
random.nextBytes(salt);
return salt;
}
private static byte[] Hash(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = factory.generateSecret(spec).getEncoded();
return hash;
}
}
You can validate by just decoding the strSalt and using the same hash method:
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
Decoder decoder = Base64.getUrlDecoder();
System.out.print("Password: ");
String strPassword = new Scanner(System.in).nextLine();
String strSalt = "Your Salt String Here";
byte[] bSalt = decoder.decode(strSalt); // String to Byte
System.out.println("Salt: " + strSalt);
System.out.println("String to be hashed: " + strPassword + strSalt);
String strHash = encoder.encodeToString(Hash(strPassword, bSalt)); // Byte to String
System.out.println("Hashed value (Password + Salt value): " + strHash);
}

Java: fileoutputstream (access denied )

I am using AESCrypt open source library to encrypt file. This is my code
String password="asdfgh";
String frompath=new String("C:/Users/sabertooth/Desktop/error/pnberror.png");
String topath=new String("C:/Users/sabertooth/desktop/error/");
AESCrypt aes=new AESCrypt(false,password);
aes.encrypt(2, frompath, topath);
I am geeting access denied exception. Here is the stacktrace
Exception in thread "main" java.io.FileNotFoundException: C:\Users\sabertooth\desktop\error (Access is denied)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:212)
at java.io.FileOutputStream.<init>(FileOutputStream.java:104)
at AESCrypt.encrypt(AESCrypt.java:284)
at NewClass.main(NewClass.java:28)
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)
Posting the code for aescrypt.java
/*
* Copyright 2008 Vócali Sistemas Inteligentes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.NetworkInterface;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Enumeration;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* This class provides methods to encrypt and decrypt files using
* aescrypt file format,
* version 1 or 2.
* <p>
* Requires Java 6 and <a href="http://java.sun.com/javase/downloads/index.jsp">Java
* Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</a>.
* <p>
* Thread-safety and sharing: this class is not thread-safe.<br>
* <tt>AESCrypt</tt> objects can be used as Commands (create, use once and dispose),
* or reused to perform multiple operations (not concurrently though).
*
* #author Vócali Sistemas Inteligentes
*/
public class AESCrypt {
private static final String JCE_EXCEPTION_MESSAGE = "Please make sure "
+ "\"Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files\" "
+ "(http://java.sun.com/javase/downloads/index.jsp) is installed on your JRE.";
private static final String RANDOM_ALG = "SHA1PRNG";
private static final String DIGEST_ALG = "SHA-256";
private static final String HMAC_ALG = "HmacSHA256";
private static final String CRYPT_ALG = "AES";
private static final String CRYPT_TRANS = "AES/CBC/NoPadding";
private static final byte[] DEFAULT_MAC =
{0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd, (byte) 0xef};
private static final int KEY_SIZE = 32;
private static final int BLOCK_SIZE = 16;
private static final int SHA_SIZE = 32;
private final boolean DEBUG;
private byte[] password;
private Cipher cipher;
private Mac hmac;
private SecureRandom random;
private MessageDigest digest;
private IvParameterSpec ivSpec1;
private SecretKeySpec aesKey1;
private IvParameterSpec ivSpec2;
private SecretKeySpec aesKey2;
/*******************
* PRIVATE METHODS *
*******************/
/**
* Prints a debug message on standard output if DEBUG mode is turned on.
*/
protected void debug(String message) {
if (DEBUG) {
System.out.println("[DEBUG] " + message);
}
}
/**
* Prints a debug message on standard output if DEBUG mode is turned on.
*/
protected void debug(String message, byte[] bytes) {
if (DEBUG) {
StringBuilder buffer = new StringBuilder("[DEBUG] ");
buffer.append(message);
buffer.append("[");
for (int i = 0; i < bytes.length; i++) {
buffer.append(bytes[i]);
buffer.append(i < bytes.length - 1 ? ", " : "]");
}
System.out.println(buffer.toString());
}
}
/**
* Generates a pseudo-random byte array.
* #return pseudo-random byte array of <tt>len</tt> bytes.
*/
protected byte[] generateRandomBytes(int len) {
byte[] bytes = new byte[len];
random.nextBytes(bytes);
return bytes;
}
/**
* SHA256 digest over given byte array and random bytes.<br>
* <tt>bytes.length</tt> * <tt>num</tt> random bytes are added to the digest.
* <p>
* The generated hash is saved back to the original byte array.<br>
* Maximum array size is {#link #SHA_SIZE} bytes.
*/
protected void digestRandomBytes(byte[] bytes, int num) {
assert bytes.length <= SHA_SIZE;
digest.reset();
digest.update(bytes);
for (int i = 0; i < num; i++) {
random.nextBytes(bytes);
digest.update(bytes);
}
System.arraycopy(digest.digest(), 0, bytes, 0, bytes.length);
}
/**
* Generates a pseudo-random IV based on time and this computer's MAC.
* <p>
* This IV is used to crypt IV 2 and AES key 2 in the file.
* #return IV.
*/
protected byte[] generateIv1() {
byte[] iv = new byte[BLOCK_SIZE];
long time = System.currentTimeMillis();
byte[] mac = null;
try {
Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
while (mac == null && ifaces.hasMoreElements()) {
mac = ifaces.nextElement().getHardwareAddress();
}
} catch (Exception e) {
// Ignore.
}
if (mac == null) {
mac = DEFAULT_MAC;
}
for (int i = 0; i < 8; i++) {
iv[i] = (byte) (time >> (i * 8));
}
System.arraycopy(mac, 0, iv, 8, mac.length);
digestRandomBytes(iv, 256);
return iv;
}
/**
* Generates an AES key starting with an IV and applying the supplied user password.
* <p>
* This AES key is used to crypt IV 2 and AES key 2.
* #return AES key of {#link #KEY_SIZE} bytes.
*/
protected byte[] generateAESKey1(byte[] iv, byte[] password) {
byte[] aesKey = new byte[KEY_SIZE];
System.arraycopy(iv, 0, aesKey, 0, iv.length);
for (int i = 0; i < 8192; i++) {
digest.reset();
digest.update(aesKey);
digest.update(password);
aesKey = digest.digest();
}
return aesKey;
}
/**
* Generates the random IV used to crypt file contents.
* #return IV 2.
*/
protected byte[] generateIV2() {
byte[] iv = generateRandomBytes(BLOCK_SIZE);
digestRandomBytes(iv, 256);
return iv;
}
/**
* Generates the random AES key used to crypt file contents.
* #return AES key of {#link #KEY_SIZE} bytes.
*/
protected byte[] generateAESKey2() {
byte[] aesKey = generateRandomBytes(KEY_SIZE);
digestRandomBytes(aesKey, 32);
return aesKey;
}
/**
* Utility method to read bytes from a stream until the given array is fully filled.
* #throws IOException if the array can't be filled.
*/
protected void readBytes(InputStream in, byte[] bytes) throws IOException {
if (in.read(bytes) != bytes.length) {
throw new IOException("Unexpected end of file");
}
}
/**************
* PUBLIC API *
**************/
/**
* Builds an object to encrypt or decrypt files with the given password.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
* #throws UnsupportedEncodingException if UTF-16 encoding is not supported.
*/
public AESCrypt(String password) throws GeneralSecurityException, UnsupportedEncodingException {
this(false, password);
}
/**
* Builds an object to encrypt or decrypt files with the given password.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
* #throws UnsupportedEncodingException if UTF-16 encoding is not supported.
*/
public AESCrypt(boolean debug, String password) throws GeneralSecurityException, UnsupportedEncodingException {
try {
DEBUG = debug;
setPassword(password);
random = SecureRandom.getInstance(RANDOM_ALG);
digest = MessageDigest.getInstance(DIGEST_ALG);
cipher = Cipher.getInstance(CRYPT_TRANS);
hmac = Mac.getInstance(HMAC_ALG);
} catch (GeneralSecurityException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
/**
* Changes the password this object uses to encrypt and decrypt.
* #throws UnsupportedEncodingException if UTF-16 encoding is not supported.
*/
public void setPassword(String password) throws UnsupportedEncodingException {
this.password = password.getBytes("UTF-16LE");
debug("Using password: ", this.password);
}
/**
* The file at <tt>fromPath</tt> is encrypted and saved at <tt>toPath</tt> location.
* <p>
* <tt>version</tt> can be either 1 or 2.
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void encrypt(int version, String fromPath, String toPath)
throws IOException, GeneralSecurityException {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(fromPath));
debug("Opened for reading: " + fromPath);
out = new BufferedOutputStream(new FileOutputStream(toPath));
debug("Opened for writing: " + toPath);
encrypt(version, in, out);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
/**
* The input stream is encrypted and saved to the output stream.
* <p>
* <tt>version</tt> can be either 1 or 2.<br>
* None of the streams are closed.
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void encrypt(int version, InputStream in, OutputStream out)
throws IOException, GeneralSecurityException {
try {
byte[] text = null;
ivSpec1 = new IvParameterSpec(generateIv1());
aesKey1 = new SecretKeySpec(generateAESKey1(ivSpec1.getIV(), password), CRYPT_ALG);
ivSpec2 = new IvParameterSpec(generateIV2());
aesKey2 = new SecretKeySpec(generateAESKey2(), CRYPT_ALG);
debug("IV1: ", ivSpec1.getIV());
debug("AES1: ", aesKey1.getEncoded());
debug("IV2: ", ivSpec2.getIV());
debug("AES2: ", aesKey2.getEncoded());
out.write("AES".getBytes("UTF-8")); // Heading.
out.write(version); // Version.
out.write(0); // Reserved.
if (version == 2) { // No extensions.
out.write(0);
out.write(0);
}
out.write(ivSpec1.getIV()); // Initialization Vector.
text = new byte[BLOCK_SIZE + KEY_SIZE];
cipher.init(Cipher.ENCRYPT_MODE, aesKey1, ivSpec1);
cipher.update(ivSpec2.getIV(), 0, BLOCK_SIZE, text);
cipher.doFinal(aesKey2.getEncoded(), 0, KEY_SIZE, text, BLOCK_SIZE);
out.write(text); // Crypted IV and key.
debug("IV2 + AES2 ciphertext: ", text);
hmac.init(new SecretKeySpec(aesKey1.getEncoded(), HMAC_ALG));
text = hmac.doFinal(text);
out.write(text); // HMAC from previous cyphertext.
debug("HMAC1: ", text);
cipher.init(Cipher.ENCRYPT_MODE, aesKey2, ivSpec2);
hmac.init(new SecretKeySpec(aesKey2.getEncoded(), HMAC_ALG));
text = new byte[BLOCK_SIZE];
int len, last = 0;
while ((len = in.read(text)) > 0) {
cipher.update(text, 0, BLOCK_SIZE, text);
hmac.update(text);
out.write(text); // Crypted file data block.
last = len;
}
last &= 0x0f;
out.write(last); // Last block size mod 16.
debug("Last block size mod 16: " + last);
text = hmac.doFinal();
out.write(text); // HMAC from previous cyphertext.
debug("HMAC2: ", text);
} catch (InvalidKeyException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
/**
* The file at <tt>fromPath</tt> is decrypted and saved at <tt>toPath</tt> location.
* <p>
* The input file can be encrypted using version 1 or 2 of aescrypt.<br>
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void decrypt(String fromPath, String toPath)
throws IOException, GeneralSecurityException {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(fromPath));
debug("Opened for reading: " + fromPath);
out = new BufferedOutputStream(new FileOutputStream(toPath));
debug("Opened for writing: " + toPath);
decrypt(new File(fromPath).length(), in, out);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
/**
* The input stream is decrypted and saved to the output stream.
* <p>
* The input file size is needed in advance.<br>
* The input stream can be encrypted using version 1 or 2 of aescrypt.<br>
* None of the streams are closed.
* #throws IOException when there are I/O errors.
* #throws GeneralSecurityException if the platform does not support the required cryptographic methods.
*/
public void decrypt(long inSize, InputStream in, OutputStream out)
throws IOException, GeneralSecurityException {
try {
byte[] text = null, backup = null;
long total = 3 + 1 + 1 + BLOCK_SIZE + BLOCK_SIZE + KEY_SIZE + SHA_SIZE + 1 + SHA_SIZE;
int version;
text = new byte[3];
readBytes(in, text); // Heading.
if (!new String(text, "UTF-8").equals("AES")) {
throw new IOException("Invalid file header");
}
version = in.read(); // Version.
if (version < 1 || version > 2) {
throw new IOException("Unsupported version number: " + version);
}
debug("Version: " + version);
in.read(); // Reserved.
if (version == 2) { // Extensions.
text = new byte[2];
int len;
do {
readBytes(in, text);
len = ((0xff & (int) text[0]) << 8) | (0xff & (int) text[1]);
if (in.skip(len) != len) {
throw new IOException("Unexpected end of extension");
}
total += 2 + len;
debug("Skipped extension sized: " + len);
} while (len != 0);
}
text = new byte[BLOCK_SIZE];
readBytes(in, text); // Initialization Vector.
ivSpec1 = new IvParameterSpec(text);
aesKey1 = new SecretKeySpec(generateAESKey1(ivSpec1.getIV(), password), CRYPT_ALG);
debug("IV1: ", ivSpec1.getIV());
debug("AES1: ", aesKey1.getEncoded());
cipher.init(Cipher.DECRYPT_MODE, aesKey1, ivSpec1);
backup = new byte[BLOCK_SIZE + KEY_SIZE];
readBytes(in, backup); // IV and key to decrypt file contents.
debug("IV2 + AES2 ciphertext: ", backup);
text = cipher.doFinal(backup);
ivSpec2 = new IvParameterSpec(text, 0, BLOCK_SIZE);
aesKey2 = new SecretKeySpec(text, BLOCK_SIZE, KEY_SIZE, CRYPT_ALG);
debug("IV2: ", ivSpec2.getIV());
debug("AES2: ", aesKey2.getEncoded());
hmac.init(new SecretKeySpec(aesKey1.getEncoded(), HMAC_ALG));
backup = hmac.doFinal(backup);
text = new byte[SHA_SIZE];
readBytes(in, text); // HMAC and authenticity test.
if (!Arrays.equals(backup, text)) {
throw new IOException("Message has been altered or password incorrect");
}
debug("HMAC1: ", text);
total = inSize - total; // Payload size.
if (total % BLOCK_SIZE != 0) {
throw new IOException("Input file is corrupt");
}
if (total == 0) { // Hack: empty files won't enter block-processing for-loop below.
in.read(); // Skip last block size mod 16.
}
debug("Payload size: " + total);
cipher.init(Cipher.DECRYPT_MODE, aesKey2, ivSpec2);
hmac.init(new SecretKeySpec(aesKey2.getEncoded(), HMAC_ALG));
backup = new byte[BLOCK_SIZE];
text = new byte[BLOCK_SIZE];
for (int block = (int) (total / BLOCK_SIZE); block > 0; block--) {
int len = BLOCK_SIZE;
if (in.read(backup, 0, len) != len) { // Cyphertext block.
throw new IOException("Unexpected end of file contents");
}
cipher.update(backup, 0, len, text);
hmac.update(backup, 0, len);
if (block == 1) {
int last = in.read(); // Last block size mod 16.
debug("Last block size mod 16: " + last);
len = (last > 0 ? last : BLOCK_SIZE);
}
out.write(text, 0, len);
}
out.write(cipher.doFinal());
backup = hmac.doFinal();
text = new byte[SHA_SIZE];
readBytes(in, text); // HMAC and authenticity test.
if (!Arrays.equals(backup, text)) {
throw new IOException("Message has been altered or password incorrect");
}
debug("HMAC2: ", text);
} catch (InvalidKeyException e) {
throw new GeneralSecurityException(JCE_EXCEPTION_MESSAGE, e);
}
}
/* public static void main(String[] args) {
try {
if (args.length < 4) {
System.out.println("AESCrypt e|d password fromPath toPath");
return;
}
AESCrypt aes = new AESCrypt(true, args[1]);
switch (args[0].charAt(0)) {
case 'e':
aes.encrypt(2, args[2], args[3]);
break;
case 'd':
aes.decrypt(args[2], args[3]);
break;
default:
System.out.println("Invalid operation: must be (e)ncrypt or (d)ecrypt.");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
*/
}
There is a method encrypt which takes 3 arguments version frompath topath
You can't write to a folder. And C:/Users/sabertooth/desktop/error/ is a folder path.
Also, new String("...") is unnecessary. Just use "...".

Using JavaScript to replace Java for signature encryption (DESede/CBC/PKCS5Padding)

I am working on a project that is using a Java service to encrypt a signature I receive from a webservice. I need to replace this code with JavaScript.
Here is where I am at with the JavaScript http://jsfiddle.net/cDeyv/4/ (Code now works correctly thanks to some help from Rasmus Faber)
Here is the Java
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.StringTokenizer;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
// TODO: Auto-generated Javadoc
/**
* The Class SymmetricEncryption.
*/
public class SymmetricEncryption {
public static void main(String args[]) {
String signatureKey = "185-188-32-81-185-2-188-103-248-127-38-173-109-200-56-32-81-47-234-4-191-157-26-247";
String serverTime = "2011-01-12 18:48:43.000";
String encryptedSignatureKey = "240-230-243-218-251-103-145-3-156-109-41-25-127-185-149-150-36-96-176-154-83-24-20-89";
SymmetricEncryption sE = new SymmetricEncryption();
String result1 = sE.genericencrypt(serverTime, signatureKey);
System.out.println(result1);
}
/**
* The d format.
*/
public final SimpleDateFormat dFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* The ivbytes.
*/
public byte[] ivbytes = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
/**
* The iv.
*/
public IvParameterSpec iv = new IvParameterSpec(ivbytes);
/**
* Genericencrypt.
*
* #param source the source
* #param keyString the key string
* #return the string
*/
public String genericencrypt(String source, String keyString) {
try {
// Generate key
SecretKey key = getKey(keyString);
// Create the cipher
Cipher desCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// Initialize the cipher for encryption
desCipher.init(Cipher.ENCRYPT_MODE, key, iv);
// Our cleartext as bytes
byte[] cleartext = source.getBytes();
System.out.println("Server Time ASCII " + new String(cleartext));
// Encrypt the cleartext
byte[] ciphertext = desCipher.doFinal(cleartext);
System.out.println("ciphertext ASCII " + new String(ciphertext));
// Return a String representation of the cipher text
return getString(ciphertext);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Gets the key.
*
* #param keyString the key string
* #return the key
*/
private SecretKey getKey(String keyString) {
try {
byte[] bytes = getBytes(keyString);
return new SecretKeySpec(bytes, "DESede");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Gets the string.
*
* #param bytes the bytes
* #return the string
*/
public String getString(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
sb.append((int) (0x00FF & b));
if (i + 1 < bytes.length) {
sb.append("-");
}
}
return sb.toString();
}
/**
* Gets the bytes.
*
* #param str the str
* #return the bytes
*/
public byte[] getBytes(String str) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StringTokenizer st = new StringTokenizer(str, "-", false);
while (st.hasMoreTokens()) {
int i = Integer.parseInt(st.nextToken());
bos.write((byte) i);
}
return bos.toByteArray();
}
}
This is returning "240-230-243-218-251-103-145-3-156-109-41-25-127-185-149-150-36-96-176-154-83-24-20-89"
I need to find an implementation of the DESede/CBC/PKCS5Padding encryption algorithm.
Any help with this would be appreciated.
Thanks
DESede is Java's name for Triple DES. That should help you google up an implementation. Here is one: http://www.tero.co.uk/des/code.php

Categories