6

我正在用 Java 编写一个安全的文件共享应用程序。一般架构如下所示:

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

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

我想使用 AES 256 位加密来做到这一点。而且我想在不依赖 BouncyCastle 库或任何 3rd 方库的情况下完成整个工作。它需要使用标准的 Java 5 库,这就是为什么我选择使用 AES 256 加密和 RSA 而不是 PGP 之类的东西。

任何人都可以发现这种方法本质上不安全,或者想出一种更有效的方法来做到这一点吗?

编辑:

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

4

4 回答 4

5

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

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

编辑:至于我认为我会如何做到这一点:

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

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

这样一来,两条明显的攻击路线分别是针对随机数生成器和针对 RSA 本身。对于随机数生成器,我会使用 Java 的 SecureRandom ——这正是它的预期目的,如果记忆有用,它已经过非常仔细的检查,并且似乎不太可能出现重大中断。

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

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

编辑2:至于处理多个设备,我想我只是将每个设备视为一个单独的用户,拥有自己的公钥/私钥对。然后,我(可能)将这些实际用户分组在一起,这样我就可以轻松地选择“Joe Blow”来让他访问他的所有设备——但是通过分层显示,我也可以很容易地限制对子集的访问其中,所以如果我想在乔的办公室机器上分享它,但它足够敏感,我不希望它在他看着它时有人可能会从他的肩膀上看,我也可以很容易地做到这一点。

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

于 2012-07-17T17:15:35.390 回答
2

您概述的方案等效于 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 的 API 有点晦涩,但可能值得学习。

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

于 2012-07-17T18:18:14.580 回答
1

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

  1. Bouncy Castle PGP 库具有非常宽松的许可证,因此您甚至可以将它们复制到代码中的子包中;
  2. 除了 PGP 之外,还有其他标准容器格式,例如 CMS 或 XML 加密,尽管后者可能不是很好的通用格式;
  3. 我强烈建议不要使用 UUID,而是使用播种良好的 PRNG,例如 Java JCE“SHA1PRNG”来创建 AES 密钥——我看不出有什么强有力的理由说明你应该在你的方案中依赖像 UUID 这样的东西;
  4. AES 密钥应该由具有足够熵的随机位组成,将它们视为“密码”会导致陷阱:您不能将字符串用作安全的 AES 密钥;
  5. 用户必须信任您的应用程序和服务器,您受信任的第三方:您可以将用户密码发送到您的服务器,您可以向用户发送不正确的公钥等等等等。
  6. Your scheme is not protected against any man in the middle attacks (and many argue this cannot be done without using SSL)
  7. Instead of directly encrypting with a password, you should look into something like Password Based Encryption PBKDF2 to encrypt the RSA private key;
  8. Try and add integrity protection when encrypting, from Java 7 onwards you may use AES in GCM mode, it's well worth it.
于 2012-07-17T18:29:11.987 回答
-1

这完全取决于您希望加密的“安全性”程度。显然,RSA 是 PKI 的良好文档/公认标准。话虽如此,只要您提供明文和加密文本,黑客就可以更容易地在知道部分明文的情况下解密密文。在这里,您正是这样做的。尽管您只传输加密的 UUID,但通过使用多个密钥加密相同的明文,攻击者可以深入了解有效负载。此外,如果被黑者实际上是接收者之一,他能够解码 UUID,从而自动知道由其他用户的公钥加密的明文。

这对您来说可能不是一个关键问题,但只是想我会指出一个安全风险。

但是,我不完全确定为什么需要存储用户的私钥。此外,通过使用简单的密码来加密私钥,您基本上已经将整个系统的整体安全性降低到用户密码的强度。最后,如果用户丢失了密码,他就干杯了;没有办法恢复任何数据。

我过去做过类似的事情,但将结果存储在数据库中。但是,我当时使用了 BouncyCastle 库,所以我不确定没有它们如何实现这一点。

于 2012-07-17T17:19:58.210 回答