一个问题 如果创建密码器的时候,
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
TRANSFORMATION = "AES"如果和生成秘钥的算法一样设置为 “AES/CBC/PKCS5PADDING”,就会报下面的错,
java.security.InvalidKeyException: no IV set when one expected
没有理解为什么啊。请路过的大佬指点。。
/**
* 对称加密数据
*
* 使用SecretKeySpec生成秘钥,用AES/DES方式生成秘钥
* 每次加解密都生成同样的秘钥
*/
public class SymmetricEncryUtils {
//AES ,DES
// 算法/模式/补码方式
//与给定的密钥内容相关联的 密钥算法的名称
// private static String ALGRITHM = "AES/CBC/PKCS5PADDING";
private static String ALGRITHM = "DES/CBC/PKCS5Padding";
// private static String TRANSFORMATION = "AES";
private static String TRANSFORMATION = "DES";
public static byte[] generateRandomString() {
Random random = new Random();
//AES秘钥规定是16位秘钥
//DES 8bytes
byte[] rand = new byte[8];
random.nextBytes(rand);
return rand;
}
private static final byte[] ALIAS = generateRandomString();
public static String encrypt(String content) {
try {
// 创建秘钥
SecretKeySpec keySpec = new SecretKeySpec(ALIAS, ALGRITHM);
// 创建密码器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// 初始化加密器
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 加密
return Base64.encodeToString(cipher.doFinal(content.getBytes("UTF-8")), Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}
public static String decrypt(String content) {
try {
// 创建秘钥
SecretKeySpec keySpec = new SecretKeySpec(ALIAS, ALGRITHM);
// 创建密码器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// 初始化解密器
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 解密
return new String(cipher.doFinal(Base64.decode(content, Base64.NO_WRAP)), "UTF-8");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return "";
}
}
但是这里面解密要用到加密时候cipher对象获取的iv
encryptIv = cipher.getIV();
难道每个加密数据还要对应保存其加密时候的iv吗?
请大佬指点。
还有KeyGenerator初始化时的Spec对象,Android6.0以上可以使用 KeyGenParameterSpec来生成,但是低版本用哪个对象生成呢?搜了半天没解决。
/**
* 通过AES加密方式,用KeyGenerator生成秘钥,保存在Android Keystore中
* 对数据进行加解密
*
* 1、创建秘钥,保存在AndroidKeystore里面,秘钥别名为alias
* 2、创建并初始化cipher对象,获取秘钥,对数据进行加解密
*/
public class AESKeystoreUtils {
private static final String ALIAS = "123";
// 算法/模式/补码方式
private static String TRANSFORMATION = "AES/GCM/NoPadding";
private static byte[] encryptIv;
/**
* 创建秘钥
*/
private static void createKey() {
//获取Android KeyGenerator的实例
//设置使用KeyGenerator的生成的密钥加密算法是AES,在 AndroidKeyStore 中保存密钥/数据
final KeyGenerator keyGenerator;
AlgorithmParameterSpec spec = null;
try {
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
//使用KeyGenParameterSpec.Builder 创建KeyGenParameterSpec ,传递给KeyGenerators的init方法
//KeyGenParameterSpec 是生成的密钥的参数
//setBlockMode保证了只有指定的block模式下可以加密,解密数据,如果使用其它的block模式,将会被拒绝。
//使用了“AES/GCM/NoPadding”变换算法,还需要设置KeyGenParameterSpec的padding类型
//创建一个开始和结束时间,有效范围内的密钥对才会生成。
Calendar start = new GregorianCalendar();
Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 10);//往后加十年
//todo 高于6.0才可以使用KeyGenParameterSpec 来生成秘钥,低版本呢?
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
spec = new KeyGenParameterSpec.Builder(ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setCertificateNotBefore(start.getTime())
.setCertificateNotAfter(end.getTime())
.build();
} else {
// spec = new ;
}
keyGenerator.init(spec);
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
public static String encryptData(String needEncrypt) {
if (!isHaveKeyStore()) {
createKey();
}
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null);
SecretKey secretKey = secretKeyEntry.getSecretKey();
//KeyGenParameterSpecs中设置的block模式是KeyProperties.BLOCK_MODE_GCM,所以这里只能使用这个模式解密数据。
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
//ciphers initialization vector (IV)的引用,用于解密
encryptIv = cipher.getIV();
return Base64.encodeToString(cipher.doFinal(needEncrypt.getBytes()), Base64.NO_WRAP);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
return "空指针异常";
}
return "";
}
public static String decryptData(String needDecrypt) {
if (!isHaveKeyStore()) {
createKey();
}
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null);
SecretKey secretKey = secretKeyEntry.getSecretKey();
//KeyGenParameterSpecs中设置的block模式是KeyProperties.BLOCK_MODE_GCM,所以这里只能使用这个模式解密数据。
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
//需要为GCMParameterSpec 指定一个认证标签长度(可以是128、120、112、104、96这个例子中我们能使用最大的128),
// 并且用到之前的加密过程中用到的IV。
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, encryptIv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
return new String(cipher.doFinal(Base64.decode(needDecrypt, Base64.NO_WRAP)));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return "";
}
/**
* 是否创建过秘钥
*
* @return
*/
private static boolean isHaveKeyStore() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry keyentry = keyStore.getEntry(ALIAS, null);
if (null != keyentry) {
return true;
}
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
return false;
}
}
通过keypairGenerator生成秘钥对,公钥用于加密数据,私钥用于解密数据。
但是有个问题是:
keyPairGenerator.generateKeyPair();
生成秘钥这一步,涉及到大量的运算?view明显的停顿了一下,log显示:
Skipped 129 frames! The application may be doing too much work on its main thread.
待解决的问题。大佬路过请指示。
public class RSAKeystoreUtils {
private static final String AndroidKeyStore = "AndroidKeyStore";
//RSA/ECB/PKCS1Padding
//RSA/ECB/OAEPWithSHA-256AndMGF1Padding
private static final String RSA_MODE_OAEP = "RSA/ECB/PKCS1Padding";
private static final String KEY_ALIAS = "123";
/**
* 创建秘钥
*/
public static void createKey() {
KeyPairGenerator keyPairGenerator = null;
try {
keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
AlgorithmParameterSpec spec;
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 30);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
spec = new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT|KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setCertificateNotBefore(start.getTime())
.setCertificateNotAfter(end.getTime())
.build();
} else {
spec = new KeyPairGeneratorSpec.Builder(App.getInstance())
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=" + KEY_ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
}
try {
keyPairGenerator.initialize(spec);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
keyPairGenerator.generateKeyPair();
}
public static String encryptData(String data) {
if (!isHaveKeyStore()) {
createKey();
}
try {
Cipher cipher = Cipher.getInstance(RSA_MODE_OAEP);
KeyStore keyStore = KeyStore.getInstance(AndroidKeyStore);
keyStore.load(null);
PublicKey publicKey = keyStore.getCertificate(KEY_ALIAS).getPublicKey();
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeToString(cipher.doFinal(data.getBytes("UTF-8")), Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
return "空指针异常";
}
return "加密数据失败";
}
public static String decryptData(String data) {
try {
Cipher cipher = Cipher.getInstance(RSA_MODE_OAEP);
KeyStore keyStore = KeyStore.getInstance(AndroidKeyStore);
keyStore.load(null);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decode(data, Base64.NO_WRAP)), "UTF-8");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
return "空指针异常";
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
return "解密数据失败";
}
/**
* 是否创建过秘钥
*
* @return
*/
private static boolean isHaveKeyStore() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (keyStore.containsAlias(KEY_ALIAS)) {
return true;
}
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
}
return false;
}
}