Android文本加解密失败
Android encryption and decryption of text fails
我尝试加密一些文本(这里它被命名为代码)并再次解密。
为此,我使用了一个加盐的 4 位 Pin。
在此之后文本被加密,也再次进行一些 Base64 解码,所以我可以再次安全地输出字符串。
据我所知,我必须 base64.decode 然后再解密。
但是我没有正确解密我的加密文本。只是一些不同的文字。 (像这样[B@3ceB...)
这只是一个小项目,没什么大不了的。这也是我的第二个 android 应用程序,所以一些很好的例子会很棒。
String pinstr = new String();
pinstr = "5555";
try {
EncryptDecrypt encryptor = new EncryptDecrypt(pinstr);
//encryptor.encrypt(code);
String encrypted = new String();
encrypted = encryptor.encrypt(code);
String decrypted = new String();
decrypted = encryptor.decrypt(encrypted);
Toast.makeText(MainActivity.this, decrypted, Toast.LENGTH_SHORT).show();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public class EncryptDecrypt {
private SecretKeySpec skeySpec;
private Cipher cipher, cipher2;
EncryptDecrypt(String password) throws NoSuchAlgorithmException,
UnsupportedEncodingException, NoSuchPaddingException,
IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] key = Arrays.copyOf(sha.digest(("ThisisMySalt1234" + password).getBytes("UTF-8")),
16);
skeySpec = new SecretKeySpec(key, "AES");
cipher = Cipher.getInstance("AES");
cipher2 = Cipher.getInstance("AES");
}
String encrypt(String clear) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
String encrypted = new String();
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encryptedBytes = null;
encryptedBytes = cipher.doFinal(clear.getBytes());
encrypted = Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
return encrypted;
}
// fehlerhaft
String decrypt(String encryptedBase64) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String decrypted = new String();
cipher2.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decodedBytes = null;
decodedBytes = Base64.decode(encryptedBase64, Base64.DEFAULT);
decrypted = cipher2.doFinal(decodedBytes).toString();
return decrypted;
}
}
Cipher#doFinal(byte[])
的输出类型是 byte[]
,但数组没有默认的打印内容的方式。通过在数组上调用 byte[].toString()
,您只需打印其类型和哈希码。 (更多关于此 here)
你要的是
decrypted = new String(cipher2.doFinal(decodedBytes), "UTF-8");
告诉 String
构造函数给定的字节数组包含以 UTF-8 编码的字符。
执行此操作时,还需要以特定编码从字符串中获取字节数组:
clear.getBytes("UTF-8")
如果您省略编码,则会使用系统默认值,这可能会使您在具有不同系统编码默认值的系统之间发送密文时无法恢复明文。
顺便说一句,您不需要两个 Cipher
实例。只有一个就够了,因为你总是在加密和解密过程中启动它。
其他安全问题:
- 始终使用完全限定的密码字符串。所以将
Cipher.getInstance("AES")
更改为 Cipher.getInstance("AES/CBC/PKCS5Padding")
.
切勿使用 ECB 模式,这是使用 "AES"
密码字符串时的默认模式。它在语义上不安全。至少使用带有随机 IV 的 CBC 模式:
SecureRandom r = new SecureRandom();
byte[] iv = new byte[16];
r.nextBytes(iv);
...
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));
IV 不应该是秘密的,因此您可以简单地将其添加到密文中并在解密前将其拼接。
验证您的密文,这样您就不会容易受到填充 oracle 攻击,并且可以始终检查您的密文的完整性。这可以通过加密然后 MAC 方案和强大的 MAC 算法(如 HMAC-SHA256)轻松完成。您还可以使用经过身份验证的加密模式,例如 GCM 或 EAX。
您的代码有效,但您丢弃了结果。
换行
decrypted = cipher2.doFinal(decodedBytes).toString();
到
decrypted = new String(cipher2.doFinal(decodedBytes),"UTF-8");
然后你得到的是解码后的字符串而不是字节数组的 ID(调用 toString() 确实给你字节数组作为字符串)。
顺便说一句:您不需要在 Java 中初始化变量。 String encrypted = new String();
没用。直接写 String encrypted;
.
我尝试加密一些文本(这里它被命名为代码)并再次解密。 为此,我使用了一个加盐的 4 位 Pin。
在此之后文本被加密,也再次进行一些 Base64 解码,所以我可以再次安全地输出字符串。
据我所知,我必须 base64.decode 然后再解密。
但是我没有正确解密我的加密文本。只是一些不同的文字。 (像这样[B@3ceB...)
这只是一个小项目,没什么大不了的。这也是我的第二个 android 应用程序,所以一些很好的例子会很棒。
String pinstr = new String();
pinstr = "5555";
try {
EncryptDecrypt encryptor = new EncryptDecrypt(pinstr);
//encryptor.encrypt(code);
String encrypted = new String();
encrypted = encryptor.encrypt(code);
String decrypted = new String();
decrypted = encryptor.decrypt(encrypted);
Toast.makeText(MainActivity.this, decrypted, Toast.LENGTH_SHORT).show();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public class EncryptDecrypt {
private SecretKeySpec skeySpec;
private Cipher cipher, cipher2;
EncryptDecrypt(String password) throws NoSuchAlgorithmException,
UnsupportedEncodingException, NoSuchPaddingException,
IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] key = Arrays.copyOf(sha.digest(("ThisisMySalt1234" + password).getBytes("UTF-8")),
16);
skeySpec = new SecretKeySpec(key, "AES");
cipher = Cipher.getInstance("AES");
cipher2 = Cipher.getInstance("AES");
}
String encrypt(String clear) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
String encrypted = new String();
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encryptedBytes = null;
encryptedBytes = cipher.doFinal(clear.getBytes());
encrypted = Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
return encrypted;
}
// fehlerhaft
String decrypt(String encryptedBase64) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String decrypted = new String();
cipher2.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decodedBytes = null;
decodedBytes = Base64.decode(encryptedBase64, Base64.DEFAULT);
decrypted = cipher2.doFinal(decodedBytes).toString();
return decrypted;
}
}
Cipher#doFinal(byte[])
的输出类型是 byte[]
,但数组没有默认的打印内容的方式。通过在数组上调用 byte[].toString()
,您只需打印其类型和哈希码。 (更多关于此 here)
你要的是
decrypted = new String(cipher2.doFinal(decodedBytes), "UTF-8");
告诉 String
构造函数给定的字节数组包含以 UTF-8 编码的字符。
执行此操作时,还需要以特定编码从字符串中获取字节数组:
clear.getBytes("UTF-8")
如果您省略编码,则会使用系统默认值,这可能会使您在具有不同系统编码默认值的系统之间发送密文时无法恢复明文。
顺便说一句,您不需要两个 Cipher
实例。只有一个就够了,因为你总是在加密和解密过程中启动它。
其他安全问题:
- 始终使用完全限定的密码字符串。所以将
Cipher.getInstance("AES")
更改为Cipher.getInstance("AES/CBC/PKCS5Padding")
. 切勿使用 ECB 模式,这是使用
"AES"
密码字符串时的默认模式。它在语义上不安全。至少使用带有随机 IV 的 CBC 模式:SecureRandom r = new SecureRandom(); byte[] iv = new byte[16]; r.nextBytes(iv); ... cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));
IV 不应该是秘密的,因此您可以简单地将其添加到密文中并在解密前将其拼接。
验证您的密文,这样您就不会容易受到填充 oracle 攻击,并且可以始终检查您的密文的完整性。这可以通过加密然后 MAC 方案和强大的 MAC 算法(如 HMAC-SHA256)轻松完成。您还可以使用经过身份验证的加密模式,例如 GCM 或 EAX。
您的代码有效,但您丢弃了结果。
换行
decrypted = cipher2.doFinal(decodedBytes).toString();
到
decrypted = new String(cipher2.doFinal(decodedBytes),"UTF-8");
然后你得到的是解码后的字符串而不是字节数组的 ID(调用 toString() 确实给你字节数组作为字符串)。
顺便说一句:您不需要在 Java 中初始化变量。 String encrypted = new String();
没用。直接写 String encrypted;
.