package xyz.yhsj.yhutils.secarity; import android.util.Base64; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * Desction: * Author:pengjianbo * Date:15/11/7 下午7:44 */ public final class AESUtils { //AESCoder-ObjC uses CBC and PKCS7Padding private static final String AES_MODE = "AES/CBC/PKCS7Padding"; private static final String CHARSET = "UTF-8"; //AESCoder-ObjC uses SHA-256 (and so a 256-bit key) private static final String HASH_ALGORITHM = "SHA-256"; //AESCoder-ObjC uses blank IV (not the best security, but the aim here is compatibility) private static final byte[] ivBytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /** * Generates SHA256 hash of the password which is used as key * * @param password used to generated key * @return SHA256 of the password */ private static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException { final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM); byte[] bytes = password.getBytes("UTF-8"); digest.update(bytes, 0, bytes.length); byte[] key = digest.digest(); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); return secretKeySpec; } /** * Encrypt and encode message using 256-bit AES with key generated from password. * * * @param password used to generated key * @param message the thing you want to encrypt assumed String UTF-8 * @return Base64 encoded CipherText * @throws GeneralSecurityException if problems occur during encryption */ public static String encrypt(final String password, String message) throws GeneralSecurityException { try { final SecretKeySpec key = generateKey(password); byte[] cipherText = encrypt(key, ivBytes, message.getBytes(CHARSET)); //NO_WRAP is important as was getting \n at the end String encoded = Base64.encodeToString(cipherText, Base64.NO_WRAP); return encoded; } catch (UnsupportedEncodingException e) { throw new GeneralSecurityException(e); } } /** * More flexible AES encrypt that doesn't encode * @param key AES key typically 128, 192 or 256 bit * @param iv Initiation Vector * @param message in bytes (assumed it's already been decoded) * @return Encrypted cipher text (not encoded) * @throws GeneralSecurityException if something goes wrong during encryption */ public static byte[] encrypt(final SecretKeySpec key, final byte[] iv, final byte[] message) throws GeneralSecurityException { final Cipher cipher = Cipher.getInstance(AES_MODE); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); byte[] cipherText = cipher.doFinal(message); return cipherText; } /** * Decrypt and decode ciphertext using 256-bit AES with key generated from password * * @param password used to generated key * @param base64EncodedCipherText the encrpyted message encoded with base64 * @return message in Plain text (String UTF-8) * @throws GeneralSecurityException if there's an issue decrypting */ public static String decrypt(final String password, String base64EncodedCipherText) throws GeneralSecurityException { try { final SecretKeySpec key = generateKey(password); byte[] decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP); byte[] decryptedBytes = decrypt(key, ivBytes, decodedCipherText); String message = new String(decryptedBytes, CHARSET); return message; } catch (UnsupportedEncodingException e) { throw new GeneralSecurityException(e); } } /** * More flexible AES decrypt that doesn't encode * * @param key AES key typically 128, 192 or 256 bit * @param iv Initiation Vector * @param decodedCipherText in bytes (assumed it's already been decoded) * @return Decrypted message cipher text (not encoded) * @throws GeneralSecurityException if something goes wrong during encryption */ public static byte[] decrypt(final SecretKeySpec key, final byte[] iv, final byte[] decodedCipherText) throws GeneralSecurityException { final Cipher cipher = Cipher.getInstance(AES_MODE); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); byte[] decryptedBytes = cipher.doFinal(decodedCipherText); return decryptedBytes; } private AESUtils() { } }