当前位置: 首页 > 知识库问答 >
问题:

在Java中使用AES 256加密RSA私钥

潘凯
2023-03-14

我在Java写一个安全的文件共享应用程序。一般的架构是这样的:

  1. 用户希望加密文件以在多个用户之间安全共享
  2. 应用程序在客户端上生成一个随机UUID,并将其用作AES 256密码,然后使用UUID对数据进行加密
  3. 然后使用每个人的公钥对UUID进行RSA加密。每个共享用户一次
  4. 每个加密的UUID数据包作为文件的一部分存储在自定义文件头中
  5. 然后将文件上载到其他人可以访问的服务器
  6. 用户可以使用各自的私钥读取AES加密密钥并解密文件

这是陷阱。用户的私钥必须加密并存储在数据库中的服务器上,以便可以从多个位置访问文件。在将私钥上载到服务器之前,将在客户端上使用用户选择的密码对其进行加密。

我想使用AES 256位加密来实现这一点。我希望在不依赖BouncyCastle库或任何第三方库的情况下完成整个工作。它需要使用标准的Java5库,这就是为什么我选择使用AES256加密和RSA,而不是PGP之类的东西。

任何人都能发现这种方法有什么内在的不安全之处,或者想出一种更有效的方法来做到这一点吗?

编辑:

好的,我正在更新这个问题,因为我得到的所有答案都建议我不要将私钥传输到服务器。我需要服务器上的私钥的原因是因为用户需要能够从多个客户端和多个位置(即:他们的iphone,他们的ipad,他们的工作笔记本电脑,他们的家用电脑)访问他们的数据。他们不想要管理和复制他们的密钥从一个设备到另一个设备,这甚至比在我们的服务器上存储他们的密钥更不安全,因为他们最终会通过电子邮件发送给自己。

共有3个答案

江宏深
2023-03-14

好的,这个问题要求进行协议讨论,所以它不完全符合stackoverflow的标准。也就是说,让我们看看是否可以发表一些评论:):

  1. Bouncy Castle PGP库具有非常宽松的许可证,因此您甚至可以将它们复制到代码中的子包中;
  2. 除了PGP,还有其他标准容器格式,如CMS或XML加密,尽管后者可能不是一个好的通用格式;
  3. 而不是UUID的,我强烈建议使用一个种子良好的PRNG,如JavaJCE“SHA1PRNG”来创建AES密钥——我看不出有任何强有力的理由为什么你应该依赖这样的东西UUID在你的方案中;
  4. AES密钥应该由随机位组成,以具有足够的熵,将它们视为密码会导致陷阱:您不能使用字符串作为安全的AES密钥;
  5. 用户必须信任你的应用程序和服务器,你是作为一个受信任的第三方:你可以发送用户的密码到你的服务器,你可以发送不正确的公钥给用户等。
  6. 你的方案无法抵御任何中间人的攻击(许多人认为不使用SSL就无法做到这一点)
  7. 与其直接用密码加密,您应该研究类似于基于密码的加密PBKDF2来加密RSA私钥;
  8. 在加密时尝试添加完整性保护,从Java7开始,您可以在GCM模式下使用AES,这是非常值得的。
邵城
2023-03-14

您概述的方案相当于CMS(标准底层S/MIME)和PGP;从根本上说,它是安全的。在CMS中,这种模式称为“密钥传输”。您还可以使用多方“密钥协议”,使用DH或ECDH等算法。

唯一的问题是,您对AES使用的密钥选择不当。

我想不出任何理由使用随机UUID,它包含非随机位。只需使用Java密码体系结构的普通密钥生成机制。键、明文和密文都应该表示为字节序列,除非您需要容纳一些仅容纳文本的外部存储或传输。

Iterable<Certificate> recipients = null;
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(256);
SecretKey contentEncryptionKey = gen.generateKey();

初始化AES密码并让提供者选择IV。

Cipher contentCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
contentCipher.init(Cipher.ENCRYPT_MODE, contentEncryptionKey);
AlgorithmParameters params = contentCipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();

对于每个收件人,初始化RSA密码并加密AES密钥。

Cipher keyEncryptionCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
for (Certificate recipient : recipients) {
  keyEncryptionCipher.init(Cipher.WRAP_MODE, recipient);
  byte[] encryptedKey = keyEncryptionCipher.wrap(contentEncryptionKey);
  /* Store the encryptedKey with an identifier for the recipient... */
}
/* Store the IV... */ 
/* Encrypt the file... */

让用户选择并记住具有256位有效强度的密码是不合理的。为了获得这种优势,你必须随机选择密码,将它们编码为文本,并让用户将它们写在卡片上。如果你真的需要这么大的力量,你可以看看一个基于智能卡的存储用户RSA密钥的解决方案。

我强烈建议使用CMS库来存储您的文件。它将增加您使用的协议是安全的,您使用的代码有更多的审查,以及其他工具、库和系统可以与加密消息互操作的机会。BouncyCastle的应用编程接口有点模糊,但可能值得学习。

(我不记得Java 5是否支持“RSA/ECB/OAEPWITHHA-512和MGF1PADDING”;如果支持,您应该使用它而不是PKCS1Padding。)

越勇锐
2023-03-14

最大的问题是使用UUID。尽管UUID(某种程度上)保证是唯一的,但它们包含的相当多内容是可以预测的;在一台机器上生成的所有UUID中,大量内容保持不变。因此,如果一个人可以访问(例如)他们自己的密钥,他们可能可以相当容易地猜出许多其他人的密钥。

另一个问题是在服务器上存储用户的私钥。这使得整个方案的其余部分相对脆弱,因为访问这些密钥显然会访问所有其余的数据。它还(显然)意味着您通常会在服务器上解密数据,因此当用户通过网络访问该数据时,它要么需要重新加密以便传输,要么需要在用户的机器上解密,否则您将以明文形式传输数据(从而使大多数加密无效)。

编辑:至于我认为我应该怎么做:

我在服务器上有一个公钥列表。当客户机希望与其他客户机共享文件时,它会从服务器获取这些客户机的公钥。然后它生成一个安全的随机密钥,并用该密钥加密数据。然后,它用所有其他客户端的公钥加密随机密钥,这些客户端应该能够访问数据。将它们放在一个流中,并将它们传输到服务器。然后,其他客户端可以下载流,用它们的私钥解密密钥,并使用该密钥解密数据本身。

这意味着每个客户机的私钥都是真正私有的——它永远不必以任何形式离开他们的机器。他们必须与世界其他地方共享的只是他们的公钥(根据定义,公钥不应该引起安全问题)。

因此,两条明显的攻击线分别针对随机数生成器和RSA本身。对于随机数生成器,我会使用Java的SecureRandom——这正是它想要达到的目的,如果内存可用,那么它已经被非常仔细地检查过了,与之发生重大冲突的可能性似乎非常小。

我不会试图评论RSA本身的安全性。现在,我认为您主要关心的是协议,而不是加密算法本身。只需说一句,如果RSA被严重破坏,您显然需要更改代码,但您会有很多公司。

这样,客户机就可以安全地存储他们的私钥。我喜欢用智能卡做那份工作,但有很多选择。从服务器和协议的角度来看,它不再是一个真正的因素。

编辑2:对于处理多个设备,我认为我应该简单地将每个设备视为一个单独的用户,拥有自己的公钥/私钥对。然后,我(可能)根据实际用户将它们分组,这样我就可以很容易地选择“Joe Blow”让他访问所有设备——但通过分层显示,我也可以很容易地限制对其中一个子集的访问,因此如果我想在Joe的办公机器上与他共享,但它足够敏感,我不想让它去有人可能会回头看,而他看着它,我也可以很容易地做到这一点。

这使得用户的生活变得简单,但保留了相同的基本安全模型(即私钥保持私有)。

 类似资料:
  • 问题内容: 我正在尝试使用RSA私钥加密某些内容。 我遵循以下示例:http : //www.junkheap.net/content/public_key_encryption_java, 但将其转换为使用私钥而不是公共密钥。遵循该示例,我认为我需要做的是: 读取DER格式的私钥 生成PCKS8EncodedKeySpec 从KeyFactory调用generatePrivate()获得一个私钥

  • 我想使用带有RSA算法的OpenSSL使用私钥加密文件: 现在,如果我执行解密操作: 此操作需要私钥 我知道我应该使用公钥进行加密,如果我使用私钥,我会得到一个签名。 然而,我想这样做是为了学习。

  • 我试图通过RSA加密字符串。我也有java样本,但我不能正确地将其转换为c#。 RSA示例: 这是我在c#中生成的代码,但我的c#代码结果与javaCode结果不同。我的代码怎么了?

  • 我正在研究一个客户机-服务器安全协议,其中我需要使用Java中的RSA来加密HMAC摘要的SecretKey,因为密钥必须发送到服务器。加密分为两个阶段;首先,我需要用一个公共非对称密钥加密对称密钥,然后,该加密消息用一个私有非对称密钥加密。 为此,我将SecretKey生成为: 如何将SecretKey转换为字节数组,以便使用RSA和公钥对其进行加密? 由于公钥加密产生128字节的字节数组,如果

  • 是否可以使用pycryptodome或任何其他库在python中使用私钥加密消息?我知道你不应该用私钥加密而用公钥解密,但我的目的是用私钥加密,这样接收者就可以确定消息是由真正的作者发送的。除了安全加密之外,我还在寻找某种模糊处理。我想做一个应用程序,其中的消息是公开的,但它只能看到,如果你有公钥。我尝试过这样做: 但它会引发下一个错误:TypeError:这不是私钥。

  • 问题内容: 我需要用openssl生成的和密钥替换从Unix到Java代码的加密和解密步骤 我生成密钥 我在Unix中使用键(我需要在Java中执行) 这是我的尝试 但它不起作用,PKCS8EncodedKeySpec / X509EncodedKeySpec不正确…但是我不知道该放什么 问题答案: 我认为您在读取PEM文件时遇到问题。JPA不直接支持PEM格式。您有两种选择,要么将它们转换为DE