Android:使用存储在文件中的公钥解密 RSA 文本
Posted
技术标签:
【中文标题】Android:使用存储在文件中的公钥解密 RSA 文本【英文标题】:Android: decrypt RSA text using a Public key stored in a file 【发布时间】:2012-07-16 23:50:52 【问题描述】:我已经尝试了好几天没有成功。
在 *** 中有很多类似的问题,甚至其中两个与我的完全相同,但没有得到解答和解决: 1)Convert php RSA PublicKey into android PublicKey 2)Android: how to decrypt an openssl encrypted file with RSA key?
我的场景: 我有一些使用 RSA 加密的文本(我没有加密)。我的 res/raw 文件夹中有一个“public.key”文件,其中包含解密它所需的公钥(与用于加密消息的私钥相关的公钥),其格式类似于以下示例:
我看到很多关于如何解密 RSA 文本的示例,如下所示:
public static byte[] decryptRSA( PublicKey key, byte[] text) throws Exception
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
但我的问题是,如何从文件中获取正确的 PublicKey 实例?没有这样的例子。
如果我只是尝试:
InputStream is = getResources().openRawResource(R.raw.public);
DataInputStream dis = new DataInputStream(is);
byte [] keyBytes = new byte [(int) is.available()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
我在返回语句中得到一个 InvalidKeyException。 我需要解码 Hex 或 Base64 吗?公钥文件的第一行和最后一行不是问题吗(带有“----BEGIN PUBLIC KEY----”的那些)?
也许我们可以在 *** 中第一次正确地得到这个答案:-)
【问题讨论】:
如果你使用更精确的术语,你可能会取得更大的进步。 RSA 有两个用途。 1) 加密,其中少量字节(通常是对称密钥)用公钥加密,然后用私钥解密。您上面的decryptRSA
方法适用于这种情况。 2) 签名,其中消息的哈希用私钥签名,然后用公钥验证。这听起来像是您想要支持的案例。
嗨@GregS,tnx 回答!我已经编辑了我的问题。如您所见,我不想加密,也不想验证。我想像应用程序的 ios 版本一样解密,只需使用用于加密消息的私钥的公钥。理论上这是可能的,甚至一点都不奇怪。
如果有人能解密,那又有什么意义呢?
重点是:获取原始消息。我无法控制网络服务器,但我需要读取它发送到我的应用程序的消息,所以我需要这个系统来读取它们。
正如詹姆斯也提到的,使用RSA
,您需要使用公钥加密并使用私钥解密。目的是,您加密的内容只能由私钥的所有者打开,其他任何人都不能打开。我不确定理论上如何用私钥加密并用公钥解密,毕竟这甚至很奇怪
【参考方案1】:
终于解决了!!!鼓、喇叭和迷人的交响乐!!!
public static byte[] decryptRSA(Context mContext, byte[] message) throws Exception
// reads the public key stored in a file
InputStream is = mContext.getResources().openRawResource(R.raw.sm_public);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
List<String> lines = new ArrayList<String>();
String line = null;
while ((line = br.readLine()) != null)
lines.add(line);
// removes the first and last lines of the file (comments)
if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size()-1).startsWith("-----"))
lines.remove(0);
lines.remove(lines.size()-1);
// concats the remaining lines to a single String
StringBuilder sb = new StringBuilder();
for (String aLine: lines)
sb.append(aLine);
String keyString = sb.toString();
Log.d("log", "keyString:"+keyString);
// converts the String to a PublicKey instance
byte[] keyBytes = Base64.decodeBase64(keyString.getBytes("utf-8"));
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(spec);
// decrypts the message
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(Base64.decodeBase64(message));
return dectyptedText;
解决方案是对从文件中读取的公钥以及加密消息本身进行 Base64 解码!
顺便说一句,我按照@Nikolay 建议的方式从文件中读取了公钥(tnx again man)。
非常感谢大家的帮助。 *** 摇滚!
【讨论】:
这会创建任何人都可以阅读的消息。加密它没有什么意义,因为任何人都可以访问公钥,因此任何人都可以解密消息。实际上,您所做的所有事情都是在消息上签名。您实际上并没有通过使用加密来使您的消息保密。它仅验证此消息来自私钥的持有者。 好吧,您确实说过您不想验证具有误导性的消息,因为这正是您想要做的。这更适合其他人在未来阅读您的问题。我认为继续使用加密和解密一词意味着保密,而您对验证说“不”会更加混乱。您应该刚刚说我要验证来自服务器的签名,并使用了众多签名验证算法之一。签名类已经为您完成了这项工作,因此您不必像以前那样自己扮演它的角色。 如果您知道任何更好的方法来从我无法控制的服务器获取原始消息并仅向我的应用程序发送两件事:1)加密消息 2)和公钥,请告诉我如何,我会试一试。事实上,我真的很感兴趣。 字节[] 消息 = ...;字节 [] 签名 = ...; verifier = Signature.getInstance ("SHA1WithRSA") verifier.initVerify( publicKey ); verifier.update (message, 0, message.length); if(verifier.verify(signature)) System.out.println("已验证!") else System.out.println("未验证!"); 谢谢你,正是我需要的。完全同意你混淆了一些术语,但我不明白为什么验证不能返回你解密的数据。【参考方案2】:您遗漏了一个关键点——公钥和私钥是分开的,您不能根据另一个来计算一个。这就是公钥加密的意义所在。除了使用原始 RSA 的问题之外,如果您使用公钥加密了某些内容,则需要使用相应的 private 密钥对其进行解密。反之亦然。因此,如果您有公钥文件,则只能从中获取 public 密钥。这仅在您的数据使用相应的 private 密钥加密时才有用。
至于实际的例外:删除开头和结尾的“---”行,使用 Base64.decode() 获取字节数组,并使用它来创建您的X509EncodedKeySpec
。一种方法 - 使用类似 BufferedReader
的东西逐行读取,忽略“---”行并将其余部分合并成一个大的 String
。
【讨论】:
嗨@Nikolay,谢谢回答!别担心,我理解这个理论,我的文件中有公钥文件需要来解密消息。 Web 服务将加密消息发送到我的应用程序,我已经存储了他们的公钥,与他们用于加密消息的私钥相对应。我会尝试你的建议并在这里发布结果。【参考方案3】:公钥只能加密数据。公钥不能解密数据。您只能使用私钥解密数据。关键是您可以将公钥分发给任何人和所有人,他们可以向您发送只有私钥持有者才能看到的加密消息。
您确实需要非常小心地使用加密技术。我担心您只是将私钥分发给您的所有设备,这会削弱您的安全性,因为每个人都将拥有相同的私钥。因此,如果我想破解您的安全性,我只需去 google play 并下载您的应用程序,然后将私钥从您的应用程序中提取出来。维奥拉我什么都看得见。
所以你知道为什么它不起作用,但你现在需要关于设计的建议,我无法告诉你为什么要使用加密。你在隐藏什么?
更新:
听起来您正在尝试执行加密和签名验证,就像 RSA 的工作原理一样,但您对它的实际工作原理感到困惑。为此,您需要两组私钥/公钥。一组用于客户端的密钥和一组用于服务器的密钥。
Web 服务器会将其公钥发送给客户端。客户端可以使用服务器的公钥向服务器发送经过身份验证和加密的消息,然后使用客户端的私钥对该消息进行签名。服务器反之亦然。服务器将使用客户端的公钥加密消息并使用他的私钥对其进行签名以向客户端发送消息。然后客户端可以使用客户端的私钥解密消息并使用服务器的公钥验证签名。
现在您要重新实施 SSL 吗?停下来。使用 SSL。
以下是 SSL 实现安全通道的方式。客户端从 Web 服务器接收 PUBLIC 密钥,以及对称加密算法的 1 个或多个名称。它选择一个他们共享的算法,然后生成一个用于所有消息的密钥。它使用 Web 服务器的公钥加密该密钥,并将其与它选择的算法一起发送。 Web 服务器使用 PRIVATE 密钥解密以获取共享密钥。之后所有的加密都是使用共享密钥的对称加密,比非对称加密快得多。
【讨论】:
Re: '公钥不能解密数据' 真的吗?从何时起?那么 RSA 签名验证是如何工作的呢? RSA 没有这样的限制。你用一把钥匙“加密”的东西总是可以用另一把钥匙“解密”。 @Nikolay 是的,公钥不能解密数据(参见***)。签名验证不涉及加密/解密,但使用公钥/私钥。签名算法在给定消息和私钥的情况下生成签名。该算法不执行加密。将其视为哈希更容易。然后签名验证算法验证消息是否使用给定消息、签名和公钥的相应私钥签名。签名验证验证此消息来自持有私钥的人。 好的,那么您究竟如何“在给定消息和 RSA 私钥的情况下生成签名”?您执行哪些加密操作以及按什么顺序执行? 术语“私有”和“公共”只是一个约定。 Nikolay 说的是正确的,我们可以使用任一密钥“加密”,另一个密钥“解密”。只需检查算法,就很明显了。数学在这里很容易解释:en.wikipedia.org/wiki/RSA_%28algorithm%29#A_working_example 只是加入 - 因为我可以 - radhoo 是对的,如果你真的按照这个理论,那么你就会知道公钥和私钥本质上是一样的 - 你可以用一个加密并用另一个解密.您也可以使用任一密钥进行签名和验证。关键是,用一个完成的,只能用另一个撤消。【参考方案4】:从您提供的 PEM 格式生成 RSA 公钥(openssl gen.rsa 密钥)
-----BEGIN PUBLIC KEY-----
SOMEDES3UNREADABLETEXT+PADDING==
-----END PUBLIC KEY
并用它来阅读一些用它签名的内容?
-在此处查看我在类似问题中的回答: https://***.com/a/12101100/546054
【讨论】:
感谢您的回答!但是,我已经解决了我的问题(请参阅此线程的标记答案)。另一方面,看看我的程序,它在删除第一行和最后一行时比你的效率高得多,因为你有 2 个“while 循环”和 2 个“if 检查”全部嵌套(如果文件会更大)。只是想一起改进,当然把它当作一个建议!以上是关于Android:使用存储在文件中的公钥解密 RSA 文本的主要内容,如果未能解决你的问题,请参考以下文章