我最近开始编写我使用Java编写的在线游戏的Android版本。但是,我遇到了与加密不一致的问题。 Java应用程序工作正常 - 它从文件中读取公钥,加密一些文本并将其传递到使用私钥正确解密的服务器。在android上,一切都似乎工作(并且正在运行相同的代码),但服务器有一个BadPaddingException尝试解密消息。我已经包含了以下所有相关代码和一步一步的事件:非对称加密差异 - Android vs Java
连接到服务器时发生的第一件事是对称密钥的协议。这是在客户端上产生的,从而:
SecretKey symmetricKey = null;
try
{
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
symmetricKey = keyGen.generateKey();
}
catch (Throwable t)
{
Debug.stackTrace(t, "Failed to generate symmetric key.");
}
return symmetricKey;
它然后被转换到Base64字符串:
byte[] keyBytes = secretKey.getEncoded();
return base64Interface.encode(keyBytes);
并使用所述公共密钥加密:
public static String encrypt(String messageString, Key key)
{
String encryptedString = null;
try
{
byte[] messageBytes = messageString.getBytes();
String algorithm = key.getAlgorithm()
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherData = cipher.doFinal(messageBytes);
encryptedString = base64Interface.encode(cipherData);
//Strip out any newline characters
encryptedString = encryptedString.replaceAll("\n", "");
encryptedString = encryptedString.replaceAll("\r", "");
}
catch (Throwable t)
{
Debug.append("Caught " + t + " trying to encrypt message: " + messageString);
}
return encryptedString;
}
在这种形式中,它被传递给使用私钥解密消息并恢复SecretKey对象的服务器:
public static String decrypt(String encryptedMessage, Key key)
{
String messageString = null;
try
{
byte[] cipherData = base64Interface.decode(encryptedMessage);
String algorithm = key.getAlgorithm();
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] messageBytes = cipher.doFinal(cipherData);
messageString = new String(messageBytes);
}
catch (Throwable t)
{
Debug.append("Caught " + t + " trying to decrypt message: " + encryptedMessage, failedDecryptionLogging);
}
return messageString;
}
但是,每当我做到这一点从Android应用中国率先起来的消息,则doFinal线产生以下异常:
11/07 12:55:55.975 javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:354)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:380)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
at util.EncryptionUtil.decrypt(EncryptionUtil.java:85)
at server.MessageHandlerRunnable.handleUnencryptedMessage(MessageHandlerRunnable.java:226)
at server.MessageHandlerRunnable.getResponse(MessageHandlerRunnable.java:188)
at server.MessageHandlerRunnable.run(MessageHandlerRunnable.java:85)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
我首先想到的是,这个问题必须要在Base64编码/因为这是Android和桌面版本之间唯一不同的代码。不过,我已经做了一些测试,并验证了这些是一致的,并且我的服务器代码可以使用任一编码方法恢复原始文本。
我的下一个想法是,Android版本必须以某种方式使用错误的公钥。
public static void generatePublicKey()
{
InputStream in = null;
ObjectInputStream oin = null;
try
{
in = KeyGeneratorUtil.class.getResourceAsStream("/assets/public.key");
oin = new ObjectInputStream(new BufferedInputStream(in));
BigInteger m = (BigInteger) oin.readObject();
BigInteger e = (BigInteger) oin.readObject();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
MessageUtil.publicKey = fact.generatePublic(keySpec);
}
catch (Throwable e)
{
Debug.stackTrace(e, "Unable to read public key - won't be able to communicate with Server.");
}
finally
{
if (in != null)
{
try {in.close();} catch (Throwable t) {}
}
if (oin != null)
{
try {oin.close();} catch (Throwable t) {}
}
}
}
当我看到这两个平台上的键(使用的toString()),我看到以下(:这是从文件中启动它是常见的使用下面的代码在这两个平台产生我已经被截断模量):
桌面:
孙RSA公共密钥,1024位模数:11920225567195913955197820411061866681846853580 ...公用指数:65537
安卓
OpenSSLRSAPublicKey {模= a9bfe8d8a199fc6a ...,publicExponent = 10001}
乍一看这似乎是完全不同的,但是我现在相信,它们是等价的一个十进制(桌面)表示,另以十六进制(Android)。 10001十六进制等于65537十进制,并将十六进制模数放入在线转换器中产生一个数字,该数字至少以小数模的正确数字开始。那么,为什么我看到BadPaddingException?
最后一点值得注意的是,这似乎是在这个问题提出了大约一年前,同样的问题:
RSA on Android is different from PC
然而,没有解决方案提出,我认为这值得我们作出一个新的问题,我可以提供我可以获得的所有信息。
你传递给'Cipher.getInstance()'什么字符串?我曾经有一个非常类似的问题,因为只传递了“RSA”,导致应用程序和服务器使用平台相关的默认值,这些默认值恰好不同。指定完整的密码字符串,即“RSA/ECB/PKCS1Padding”,为我解决了这个问题。 –
它在Android和桌面上都以“RSA”出现。我已经快速调整了它,它看起来很有前途 - 一旦我(希望)有它的工作,我会发布我的全部发现! –
指定'RSA/ECB/PKCS1Padding'可以在两个平台上运行,并解决了我的问题 - 将其作为答案发布,以便我可以接受它作为解决方案。谢谢! –