我在Android应用程序中实现了加密/解密。
我添加了一个已经成为Singleton类的加密类。
部分代码如下:
public class Encryption {
private SecretKeySpec mKey = null;
private Cipher mCipher = null;
private byte[] mKeyBytes = null;
private AlgorithmParameterSpec mParamSpec = null;
private static Encryption sInstance;
public Encryption() {
byte[] iv = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
mParamSpec = new IvParameterSpec(iv);
mKeyBytes = getMD5(MD5_KEY.getBytes();
mKey = new SecretKeySpec(mKeyBytes, AES_TAG);
try {
mCipher = Cipher.getInstance(TRANSFORMATION_STR);
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
}
}
public static synchronized Encryption getInstance() {
if (sInstance == null) {
sInstance = new Encryption();
}
return sInstance;
}
public String encryptString(String strPwd) {
String strToEncripted = null;
strToEncripted = strPwd;
String result = null;
byte[] input = null;
byte[] cipherText = null;
int ctLength = 0;
try {
input = strToEncripted.getBytes(UTF8_STR);
mCipher.init(Cipher.ENCRYPT_MODE, mKey, mParamSpec);
cipherText = new byte[mCipher.getOutputSize(input.length)];
ctLength = mCipher.update(input, 0, input.length, cipherText, 0);
ctLength += mCipher.doFinal(cipherText, ctLength);
result = Base64.encodeToString(cipherText, Base64.DEFAULT)
.replace(NEWLINE_CHAR, EMPTY_CHAR).trim();
} catch (InvalidKeyException e) {
} catch (UnsupportedEncodingException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (ShortBufferException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} catch (IllegalStateException e) {
}
return result;
}
public String decryptstring(byte[] encripted) {
String textDecrypt = "";
byte[] encriptedByteDecode64 = Base64.decode(encripted, Base64.DEFAULT);
byte[] plainText = new byte[mCipher.getOutputSize(encriptedByteDecode64.length)];
int ptLength = 0;
try {
mCipher.init(Cipher.DECRYPT_MODE, mKey, mParamSpec);
ptLength = mCipher.update(encriptedByteDecode64, 0, encriptedByteDecode64.length, plainText, 0);
ptLength += mCipher.doFinal(plainText, ptLength);
textDecrypt = (new String(plainText)).trim();
} catch (InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (ShortBufferException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
}
return textDecrypt;
}
private String getMD5(String strKey) {
String key = strKey;
String result = null;
try {
MessageDigest algorithm = MessageDigest.getInstance(MD5_TAG);
algorithm.reset();
algorithm.update(key.getBytes(UTF8_STR));
byte messageDigest[] = algorithm.digest();
StringBuilder hexString = new StringBuilder();
for (int count = 0; count < messageDigest.length; count++) {
String hexaDecimal = Integer.toHexString(0xFF & messageDigest[count]);
while (hexaDecimal.length() < 2)
hexaDecimal = new StringBuilder(ZERO_STR).append(hexaDecimal).toString();
hexString.append(hexaDecimal);
}
result = hexString.toString();
} catch (NoSuchAlgorithmException e) {
} catch (UnsupportedEncodingException e) {
}
return result;
}
}
使用单例实例,加密&amp;实现字符串的解密&amp;他们大多都在工作。
有时,虽然密码已经初始化,但仍然会抛出异常: java.lang.IllegalStateException: Cipher not initialized
该场景主要是在经过一段时间间隔(30分钟)后,执行字符串解密。
是否可能是由于Singleton实例的使用不正确?
我尝试使用new运算符加密字符串创建Encryption类的实例,而不是Singleton类,但问题是我需要相同的对象进行解密,否则 java.lang.IllegalStateException: Cipher not initialized
被抛出。
欢迎任何建议/提示。
答案 0 :(得分:6)
这个问题肯定会发生在多线程环境中,就像我遇到的那样。问题是mCipher.init()和mCipher.doFinal()方法之间的冲突。
以下是Cipher类中的相关方法:
public final void init(int opmode, Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException
{
init(opmode, key, params, JceSecurity.RANDOM);
}
public final void init(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
initialized = false;
checkOpmode(opmode);
if (spi != null) {
checkCryptoPerm(spi, key, params);
spi.engineInit(opmode, key, params, random);
} else {
chooseProvider(I_PARAMSPEC, opmode, key, params, null, random);
}
initialized = true;
this.opmode = opmode;
}
public final int doFinal(byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
checkCipherState();
// Input sanity check
if ((output == null) || (outputOffset < 0)) {
throw new IllegalArgumentException("Bad arguments");
}
chooseFirstProvider();
return spi.engineDoFinal(null, 0, 0, output, outputOffset);
}
private void checkCipherState() {
if (!(this instanceof NullCipher)) {
if (!initialized) {
throw new IllegalStateException("Cipher not initialized");
}
if ((opmode != Cipher.ENCRYPT_MODE) &&
(opmode != Cipher.DECRYPT_MODE)) {
throw new IllegalStateException("Cipher not initialized " +
"for encryption/decryption");
}
}
}
在多线程环境中查看initialized
变量的行为,其中两个线程执行init()和doFinal()。返回的异常与未实际初始化的对象无关,但initialized
变量设置为false
。
我通过同步我的encryptString()和decryptString()方法解决了我的问题。希望通过密码可以获得一些见解。
答案 1 :(得分:1)
在decryptstring
方法中,您可以致电
byte[] plainText = new byte[mCipher.getOutputSize(encriptedByteDecode64.length)];
在你打电话之前几行
mCipher.init(Cipher.DECRYPT_MODE, mKey, mParamSpec);
由于在您调用getOutputSize时尚未初始化密码,因此您将获得异常。重新排序这些行应该修复它。 (它确实适合我。)
答案 2 :(得分:0)
我有同样的问题(Cipher not initialized
)。我使用的是高加密技术,对我而言,解决方案是使用无限强度版本替换jre/lib/security
中的常规策略jar。
答案 3 :(得分:0)
确保在加密和解密方法中调用Cipher.getInstance()
。
cipher = Cipher.getInstance(TRANSFORMATION_STR);
cipher.init(Cipher.ENCRYPT_MODE, mKey, mParamSpec);
// encrypt code here
getInstance()
方法为当前线程获取一个密码实例,以避免出现竞争条件,例如刚发布的异常。