我想将明文加密和解密为AES算法。我得到了密钥,iv 来自 API。我尝试了很多东西但没有成功。怎么了?
https://github.com/simbiose/Encryption
https://github.com/scottyab/AESCrypt-Android
public class CryptoHandler {
private static CryptoHandler instance = null;
public static CryptoHandler getInstance() {
if (instance == null) {
instance = new CryptoHandler();
}
return instance;
}
public String encrypt(String message, String key, String IV) throws NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException,
UnsupportedEncodingException, InvalidAlgorithmParameterException {
byte[] srcBuff = message.getBytes("UTF8");
//here using substring because AES takes only 16 or 24 or 32 byte of key
SecretKeySpec skeySpec = new
SecretKeySpec(key.substring(0,32).getBytes(), "AES");
IvParameterSpec ivSpec = new
IvParameterSpec(IV.substring(0,16).getBytes());
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
ecipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
byte[] dstBuff = ecipher.doFinal(srcBuff);
String base64 = Base64.encodeToString(dstBuff, Base64.DEFAULT);
return base64;
}
public String decrypt(String encrypted, String key, String IV) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException {
SecretKeySpec skeySpec = new
SecretKeySpec(key.substring(0,32).getBytes(), "AES");
IvParameterSpec ivSpec = new
IvParameterSpec(IV.substring(0,16).getBytes());
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
ecipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] raw = Base64.decode(encrypted.getBytes(), 0, 16, Base64.DEFAULT);
byte[] originalBytes = ecipher.doFinal(raw);
String original = new String(originalBytes, "UTF8");
return original;
}
}
API 响应数据示例:
{
"key": "QaDtfPpeMW0VgMMd4XF88K6KkIPe5ZG0sitpyhuJf/E=",
"iv": "ccp2YePjewVL9X+vCms5BQ==",
"string": "5c2c82a6-66da-41f9-b20d-5d4ffd0c505a",
}
尤努斯,我遇到了同样的问题,虽然晚了,但如果其他人再次遇到,我正在分享我的解决方案。 Key和IVkey已经转换为base64并准备解码,因此不需要substr过程。当你制作 substr 时,你会丢失整个密钥的一部分。
对于下面的键,我从共享首选项中读取。
const val AES_KEY = "AES_KEY"
const val AES_IV = "AES_IV"
sharedprefs 返回与您的键相同的键(编码为 base64 并作为字符串 - 不是字节数组 -)
{
"key": "QaDtfPpeMW0VgMMd4XF88K6KkIPe5ZG0sitpyhuJf/E=",
"iv": "ccp2YePjewVL9X+vCms5BQ==",
"string": "5c2c82a6-66da-41f9-b20d-5d4ffd0c505a",
}
然后编码:
fun encrypt(message: String): String? {
try {
val srcBuff = message.toByteArray(charset("UTF8"))
val skeySpec = SecretKeySpec(Base64.decode(pref.getString(Constraints.AES_KEY, null)!!, Base64.DEFAULT), "AES")
val ivSpec = IvParameterSpec(Base64.decode(pref.getString(Constraints.AES_IV, null)!!, Base64.DEFAULT))
val ecipher: Cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
ecipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec)
val dstBuff: ByteArray = ecipher.doFinal(srcBuff)
return Base64.encodeToString(dstBuff, Base64.DEFAULT)
} catch (ex: Exception) {
context.longToast(ex.localizedMessage)
}
return null
}
或解码:
fun decrypt(encrypted: String): String? {
try {
val skeySpec = SecretKeySpec(Base64.decode(pref.getString(Constraints.AES_KEY, null)!!, Base64.DEFAULT), "AES")
val ivSpec = IvParameterSpec(Base64.decode(pref.getString(Constraints.AES_IV, null)!!, Base64.DEFAULT))
val ecipher: Cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
ecipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec)
val raw: ByteArray = Base64.decode(encrypted, Base64.DEFAULT)
val originalBytes: ByteArray = ecipher.doFinal(raw)
return String(originalBytes, Charset.forName("UTF-8"))
} catch (ex: Exception) {
context.longToast(ex.localizedMessage)
}
return null
}
除了字符串化不正确(键和 IV 应该包含随机字节值,包括那些不属于可打印 ASCII 字符集的部分)之外,我没有看到太多错误。
但是,最有可能的罪魁祸首是这一行:
byte[] raw = Base64.decode(encrypted.getBytes(), 0, 16, Base64.DEFAULT);
由于 PKCS#7 总是取消填充,因此使用单个块可能意味着如果它出现在以下块之一中,则取消填充会失败。只需解码整个 64 位基数,而不仅仅是 16 个字符。
绝对没有理由认为
CryptoHandler
应该只有一个实例。您当前的 getInstace
方法也不是线程安全的,因此您可能会出现多个处理程序。只需使用普通的类,并考虑其中应该包含哪些内容。
这主要是一个所谓的“包装类”。它没有做任何有用的事情。我建议编写特定于特定用例的密码学相关类。相信我,如果你不这样做,你可能会在以后重写所有内容 - 我也经历过。
不需要任何第三方库。
private fun get16CharIv(): String {
return UUID.randomUUID().toString().replace("-", "").substring(0, 16)
}
fun encryptTextWith16CharIv(textToEncrypt: String, key: String): String? {
return try {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
val secretKeyByte = key.toByteArray(Charsets.UTF_8)
val keySpec = SecretKeySpec(secretKeyByte, "AES")
val ivSpec = IvParameterSpec(get16CharIv().toByteArray(Charsets.UTF_8))
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encoder = BASE64Encoder()
encoder.encode(cipher.doFinal(textToEncrypt.toByteArray(Charsets.UTF_8)))
} catch (e: Exception) {
null
}
}
fun decryptTextWith16CharIv(encodedTextToDecrypt: String, key: String): String? {
return try {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
val secretKeyByte = key.toByteArray(Charsets.UTF_8)
val keySpec = SecretKeySpec(secretKeyByte, "AES")
val ivSpec = IvParameterSpec(get16CharIv().toByteArray(Charsets.UTF_8))
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
val decodedText = Base64.decode(
encodedTextToDecrypt.toByteArray(Charsets.UTF_8), Base64.DEFAULT
)
String(cipher.doFinal(decodedText))
} catch (e: Exception) {
null
}
}
//Another way to get IV
fun getIv(): ByteArray {
if (IV == null) {
val IV = ByteArray(32)
val random: SecureRandom = SecureRandom()
random.nextBytes(IV)
}
return IV!!
}