文字列をSharedPreferences
に保存する前に暗号化したいハードコードされたキーがあります。これは私がこれまでに持っているコードです:
public class TokenEncryptor {
private final static String TOKEN_KEY = "91a29fa7w46d8x41";
public static String encrypt(String plain) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(plain.getBytes()));
} catch (Exception e) {
Ln.e(e);
return null;
}
}
public static String decrypt(String encoded) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(encoded.getBytes()));
} catch (Exception e) {
Ln.e(e);
return null;
}
}
}
decrypt
メソッドの最後で例外をキャッチしているようです:
javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
誰かが私を正しい方向に向けることができますか? IvParameterSpec
のインスタンス化に何か問題があるようです。
AESで文字列を暗号化すると、バイトの配列が返されます。それらのバイトを文字列(new String(cipher.doFinal(plaintextBytes))
)に直接変換しようとすると、あらゆる種類の問題が発生します。暗号化メソッドからの出力を文字列にする必要がある場合は、直接変換するのではなく、_Base64
_を使用します。復号化メソッドで、バイト配列を復号化する前に、_Base64
_文字列をバイト配列に変換します。
また、出力はシステムのデフォルトに依存するため、getBytes()
を使用しないでください。 getBytes("utf-8")
などを使用します。これにより、あいまいさがなくなります。
だれかが興味を持っている(または調査を行うのが面倒すぎる)場合に備えて、受け入れた回答とコメントの助けを借りて、私がまとめたAES-256
暗号化と復号化の結果コードを次に示します。
public class TokenEncryptor {
private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw";
public static String encrypt(String plain) {
try {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8"));
byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
} catch (Exception e) {
Ln.e(e);
return null;
}
}
public static String decrypt(String encoded) {
try {
byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), "utf-8");
} catch (Exception e) {
Ln.e(e);
return null;
}
}
private static byte[] getCombinedArray(byte[] one, byte[] two) {
byte[] combined = new byte[one.length + two.length];
for (int i = 0; i < combined.length; ++i) {
combined[i] = i < one.length ? one[i] : two[i - one.length];
}
return combined;
}
}
これはArtjom B answerの延長であり、私のために働いています。
public String encryptMsg(String message, SecretKey secret)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return Base64.encodeToString(cipherText, Base64.NO_WRAP);
}
public String decryptMsg(String cipherText, SecretKey secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP);
String decryptString = new String(cipher.doFinal(decode), "UTF-8");
return decryptString;
}
@Oleksiyの回答のKotlinバージョン。
<script src="https://Gist.github.com/kasim1011/a5a9644a60c33a4df3c29f4b34cf93a4.js"></script>
import Android.util.Base64
import Java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
private const val algorithm = "AES"
private const val tokenKey = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw"
private const val padding = "AES/CBC/PKCS5Padding"
private const val ivSize = 16
fun String.encryptAES(): String {
val tokenBytes = tokenKey.toByteArray(Charsets.UTF_8)
val secretKey = SecretKeySpec(tokenBytes, algorithm)
val ivByteArray = ByteArray(ivSize)
val iv = IvParameterSpec(ivByteArray)
val cipher = Cipher.getInstance(padding)
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv)
val cipherText = cipher.doFinal(toByteArray(Charsets.UTF_8))
val ivAndCipherText = getCombinedArray(ivByteArray, cipherText)
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP)
}
fun String.decryptAES(): String {
val tokenBytes = tokenKey.toByteArray(Charsets.UTF_8)
val secretKey = SecretKeySpec(tokenBytes, algorithm)
val ivAndCipherText = Base64.decode(this, Base64.NO_WRAP)
val cipherText = Arrays.copyOfRange(ivAndCipherText, ivSize, ivAndCipherText.size)
val ivByteArray = Arrays.copyOfRange(ivAndCipherText, 0, ivSize)
val iv = IvParameterSpec(ivByteArray)
val cipher = Cipher.getInstance(padding)
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv)
return cipher.doFinal(cipherText).toString(Charsets.UTF_8)
}
private fun getCombinedArray(one: ByteArray, two: ByteArray): ByteArray {
val combined = ByteArray(one.size + two.size)
for (i in combined.indices) {
combined[i] = if (i < one.size) one[i] else two[i - one.size]
}
return combined
}