我已经好几天没试成功了。
StackOverflow中有很多类似的问题,甚至其中两个与我的完全相同,但未得到答复和未解决: 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 ----”等等)?
也许我们可以在StackOverflow中第一次正确地得到这个答案: - )
答案 0 :(得分:13)
终于解决了!鼓,小号和迷人的交响声!!!
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再次为人)。
非常感谢你的帮助。 StackOverflow摇滚!
答案 1 :(得分:2)
您缺少一个关键点 - 公钥和私钥是分开的,您无法根据另一个计算一个。这是公钥加密的一点。使用原始RSA的问题除外,如果您使用公钥加密了某些内容,则需要使用相应的私有密钥对其进行解密。反之亦然。因此,如果您有公钥文件,则只能从中获取 public 键。仅当您的数据使用相应的私有密钥加密时,这才有用。
至于实际异常:删除开头和结尾的'---'行,使用Base64.decode()获取一个字节数组,并使用它来创建X509EncodedKeySpec
。一种方法 - 使用像BufferedReader
这样的东西逐行读取,忽略'---'行并将其余部分连成一个大的String
。
答案 2 :(得分:1)
公钥只能加密数据。公钥无法解密数据。您只能使用私钥解密数据。重点是您可以将公钥分发给任何人,他们可以向您发送只有私钥持有者才能看到的加密邮件。
使用加密技术确实需要非常小心。我担心您只是将私钥分发给您的所有设备,这会削弱您的安全性,因为每个人都拥有相同的私钥。因此,如果我想破解您的安全性,我只需去谷歌播放并下载您的应用程序并从您的应用程序中拔出私钥。 Viola我可以看到一切。
所以你有答案为什么它不起作用,但你现在需要有关设计的建议,我不知道为什么你要使用加密。你藏着什么?
<强>更新强>
听起来您正在尝试执行加密和签名验证,就像RSA的工作方式一样,但您对此实际工作原理感到困惑。为此,您需要两组私钥/公钥。客户端的一组密钥和服务器的一组密钥。
Web服务器会将其公钥发送给客户端。客户端可以使用服务器的公钥将经过身份验证和加密的消息发送到服务器,然后使用客户端的私钥对该消息进行签名。反之亦然,服务器。服务器将使用客户端的公钥来加密消息,并使用其私钥对其进行签名,以向客户端发送消息。然后,客户端可以使用客户端的私钥解密消息,并使用服务器的公钥验证签名。
您现在正在重新实施SSL吗?停下来。使用SSL。
以下是SSL如何实现安全通道。客户端从Web服务器接收PUBLIC密钥,并为对称加密算法接收1个或多个名称。它选择一个共同的算法,然后生成一个密钥,用于所有前进的消息。它使用Web服务器的公钥加密该密钥,并将其与所选的算法一起发送。 Web服务器DECRYPTS使用PRIVATE键获取共享密钥。之后,所有加密都是使用共享密钥的对称加密,这比非对称加密快得多。
答案 3 :(得分:1)
从您提供的PEM格式生成RSA公钥(openssl gen.rsa key)
-----BEGIN PUBLIC KEY-----
SOMEDES3UNREADABLETEXT+PADDING==
-----END PUBLIC KEY
并用它来阅读用它签名的一些内容?
- 在这里看一个类似问题的答案: https://stackoverflow.com/a/12101100/546054