- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我最近开始编写我用 Java 编写的在线游戏的 Android 版本。但是,我遇到了与加密不一致的问题。 Java 应用程序工作正常 - 它从文件中读取公钥,加密一些文本并将其传递到服务器,在服务器上使用私钥正确解密。在 android 上,一切似乎都正常(并且正在运行相同的代码),但是服务器有一个 BadPaddingException 试图解密消息。我在下面包含了所有相关代码和事件的逐步顺序:
连接到服务器后发生的第一件事是对称 key 的协议(protocol)。这是在客户端生成的,因此:
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) {}
}
}
}
当我查看两个平台上的 key 时(使用 toString()),我看到以下内容(我截断了模数):
桌面:
Sun RSA 公钥,1024 位模数:11920225567195913955197820411061866681846853580...公共(public)指数:65537
安卓:
OpenSSLRSAPublicKey{modulus=a9bfe8d8a199fc6a...,publicExponent=10001}
乍一看它们似乎完全不同,但我现在确信它们等同于一个以十进制表示的(桌面)和另一个以十六进制表示的(Android)。十六进制的 10001 相当于十进制的 65537,将十六进制模数放入在线转换器会生成一个至少以十进制模数的正确数字开头的数字。那么,为什么我会看到 BadPaddingException?
最后一件值得注意的事情,这似乎与大约一年前在这个问题中提出的问题相同:
RSA on Android is different from PC
但是,没有提出任何解决方案,我认为值得提出一个新问题,我可以在其中提供我可用的所有信息。
最佳答案
BadPaddingException
s 通常由以下原因之一引起:
传递给解密函数的密文不等于从加密函数接收到的密文(仅仅一个位的差异就会完全破坏这个过程)。
使用不同的 key (或不匹配的私钥)解密数据。
该程序尝试取消填充用方案 A
填充的消息使用方案 B
.
既然您已经验证了 1. 和 2. 是正确的,那么 3. 很可能是您问题的根源。
您在两端使用相同编程语言的相同代码,那么为什么会有任何不兼容?罪魁祸首是以下两行代码:
String algorithm = key.getAlgorithm();
Cipher cipher = Cipher.getInstance(algorithm);
由于 key 可以与任何填充方案结合使用,它只保存它所针对的算法,其效果是 key.getAlgorithm()
返回 "RSA"
.打电话Cipher.getInstance("RSA")
这通常不是一个好主意,因为 Java 会自动为 cipher-mode 和 padding 选择platform dependent 默认值。
这可以通过传递完整的密码字符串 ( "<algorithm>/<mode>/<padding>"
) 来避免,例如 "RSA/ECB/PKCS1Padding"
.为所有人执行此操作总是一个好主意 Cipher
使用 JCE 库时的实例。
关于java - 非对称加密差异 - Android 与 Java,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31359694/
我从NVIDIA手册Eg中复制了以下代码:__threadfence()。他们为什么有 在以下代码中使用了__threadfence()。我认为使用__syncthreads()而不是__thread
我在使用 SVN 更改列表和 svn diff 时遇到了一些麻烦.特别是我想获取特定修订范围的特定文件列表的更改历史记录。 SVN 变更列表似乎是完美的解决方案,所以我的方法是: svn change
我有两个 IP 地址列表。我需要将它们合并到三个文件中,交集,仅来自 list1 的文件和仅来自 list2 的文件。 我可以用 awk/diff 或任何其他简单的 unix 命令来做到这一点吗?如何
假设自上次更新(恢复)到我的 a.b 文件以来我做了一些更改。 此 a.b 文件也在存储库中更改。 现在我想将我所做的更改与 repos 更改进行比较。 如果我 svn revert 文件,我可以看到
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
我使用的是 openssl 1.0.1c , linux x86_64 我正在创建包含“hello”的文件(没有换行符) openssl dgst -sha256 hello_file i get :
假设我们有几个库。 有什么区别核心和 普通 图书馆?他们应该如何被认可,我们是否组织了两者的职责? +Common -Class1 +Core -Class2 +Lib1 has : Comm
如何在 SQLite 中计算以毫秒为单位的最小时间间隔? 好的,提供一些背景信息, 这是我的 table 的样子: link_budget table 所以有这个时间列,我想发出一个请求,以毫秒为单位
我想知道,乐观并发控制 (OCC) 和多版本并发控制 (MVCC) 之间的区别是什么? 到目前为止,我知道两者都是基于更新的版本检查。 在 OCC 中,我读到了没有获取读取访问锁的事务,仅适用于以后的
说到 SignalR,我有点菜鸟。刚刚开始四处探索和谷歌搜索它,我想知道是否有人可以向我解释完成的事情之间的一些差异。 在我见过的一些示例中,人们需要创建一个 Startup 类并定义 app.Map
我在 Ogre 工作,但这是一个一般的四元数问题。 我有一个对象,我最初对其应用旋转四元数 Q1。后来,我想让它看起来好像我最初通过不同的四元数 Q2 旋转了对象。 我如何计算四元数,该四元数将采用已
我了解 javascript 模块模式,但我使用两种类型的模块模式,并且想从架构 Angular 了解它们之间的区别。 // PATTERN ONE var module = (function()
我有两个具有完全相同键的 JSON。 val json1 = """{ 'name': 'Henry', 'age' : 26, 'activities' : {
我发现使用 VBA 在 Excel 中复制单个文件有两种不同的方法。一是文件复制: FileCopy (originalPath), (pathToCopyTo) 另一个是名称: Name (orig
我想知道查找两个 float 组之间差异的绝对值的最有效方法是什么? 是否是以下内容: private float absDifference(float[] vector1, float[] vec
我有一个关于 wicket getApplication 的问题。 getApplication() 和 getSession().getApplication 有什么区别? 部署 wicket 应用
我刚刚开始使用activemq,我有一个关于追溯消费者的问题,为了启用这个功能,你需要有一个持久的订阅。但是在主题上启用和不启用追溯的持久订阅有什么区别? activemq 文档说。 http://a
我有两个具有完全相同键的 JSON。 val json1 = """{ 'name': 'Henry', 'age' : 26, 'activities' : {
得到另一个 Erlang 二进制表示查询('因为这就是我最近正在阅读的内容,并且需要二进制协议(protocol)实现)。 如果我正确理解了类型说明符,那么对于“浮点”类型值,8 字节表示似乎很好(这
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 4 年前。 Improve this ques
我是一名优秀的程序员,十分优秀!