我正在用 Java 编写一个客户端应用程序,它将加密数据发送回服务器。所有客户都有我的 RSA 公钥。是否有一种标准方法可以用它加密给定文件以将其发送回服务器?
从诸如此类的网站,我了解到,通常会生成一个对称密钥来加密实际文件,然后使用 RSA 公钥对该密钥进行加密并随文件一起发送。Java中是否有标准方法或标准文件格式(没有额外的库)来打包加密的对称密钥和对称加密的文件?
当然,我可以设计自己的文件格式,但如果可能的话,我宁愿坚持标准。
我正在用 Java 编写一个客户端应用程序,它将加密数据发送回服务器。所有客户都有我的 RSA 公钥。是否有一种标准方法可以用它加密给定文件以将其发送回服务器?
从诸如此类的网站,我了解到,通常会生成一个对称密钥来加密实际文件,然后使用 RSA 公钥对该密钥进行加密并随文件一起发送。Java中是否有标准方法或标准文件格式(没有额外的库)来打包加密的对称密钥和对称加密的文件?
当然,我可以设计自己的文件格式,但如果可能的话,我宁愿坚持标准。
一种常见的标准加密格式是加密消息语法(CMS,从 PKCS#7 演变而来),例如 BouncyCastle 支持它。它还允许签名,并且是例如 S/MIME 邮件消息安全性的基础。这可能是矫枉过正,但得到广泛支持。
示例代码由 Bouncy Castle 提供。对于 CMS,请参阅org.bouncycastle.cms.test,可能是 Enveloped Data 示例。
另一种选择是由 W3C 标准化的 XML 加密格式。对于 Java 示例,请参阅Exploring XML Encryption, Part 1或使用 XSS4J 进行操作:在 Java 中实现 XML 加密
文件加密的最简单和最可靠的方法是调用 gpg 或 pgp 对其进行加密。我知道这听起来不优雅,但根据我审查大量加密代码的经验,这是确保您的解决方案安全的最简单方法,而无需了解很多关于密码学的知识。如果你想自己做,你需要更多地了解密码学是如何工作的,并且安全问题的风险会增加。
确保加密数据具有真实性保护(例如,加密然后签名/MAC):您将需要机密性和真实性保护。一个非常常见的错误是加密但无法签名;此错误可能会引入微妙的安全问题。(如果你不是密码学家,我知道这对你来说可能没有任何意义。我知道这绝不是显而易见的,但我是密码学家。如果你熟悉 IND-CCA2 和 INT-CTXT,你就会明白为什么我推荐这个。如果你不熟悉这些概念,你可以阅读它们,或者你可以相信我的话。嘿,加密是棘手的东西;我能说什么。)例如,您链接到的网站犯了这个错误;我不应该依赖该网站来获取有关安全加密的建议。
如果客户端和服务器实时连接,另一种方法是非常好的方法,即在客户端和服务器之间打开一个 TLS 加密的通道,并通过该通道发送数据。确保客户端验证服务器的公钥/证书。如果要对客户端进行身份验证,请为客户端提供客户端证书,并确保服务器要求客户端提供客户端证书并验证该证书。
@JPP,你的这个评论让我觉得你问错了问题:
客户端应用程序保存用户活动日志,其内容应原封不动地发布到服务器,即使用户对保存的文件具有读/写访问权限。此外,用户不应该能够检查内容(文件是来自测验应用程序的练习、进度和错误报告)。
如果客户端应用程序写入日志文件并对其进行加密 - 这意味着客户端应用程序可以访问加密密钥,无论它可能是什么。
但是,您正试图阻止客户端应用程序的用户读取文件!
那是行不通的,因为:
对我来说,您的问题听起来更像是访问控制之一 - 如何在用户无法访问内容的情况下从客户端应用程序写入日志文件?
一种方法是构建在不同用户上下文中运行的本地服务(例如 Windows 上的 Windows 服务);客户端应用程序将与服务对话,然后服务会将内容写入文件中的普通用户无法访问的受保护位置。
另一种选择是,正如@DW 建议的那样,立即将其直接发送到服务器,而不是将任何内容写入文件。
正如我之前提到的,这两种解决方案的问题是:用户可以控制其进程中的任何内容,并且可以阻止或修改发送到服务器或本地 Windows 服务的任何请求。
显然,例如,如果不对 Bouncy Castle 中已有的东西进行大量重新编码,就无法使用标准库来做到这一点。
这就是我想出的。在客户端,我加密文件plainfile
如下:
// install security provider
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
// load public certificate for signing
KeyStore publicKs = KeyStore.getInstance(KeyStore.getDefaultType());
publicKs.load(new FileInputStream("/path/to/publicKeystore.ks"), null);
TrustedCertificateEntry entry = (TrustedCertificateEntry) publicKs.getEntry("public", null);
X509Certificate cert = (X509Certificate) entry.getTrustedCertificate();
// create CMS envelope data;
// check http://www.ietf.org/rfc/rfc3852.txt pages 15-16 for details
CMSEnvelopedDataGenerator envelopedDataGen = new CMSEnvelopedDataGenerator();
// specify that generated symmetric key will be encrypted by with this public key
envelopedDataGen.addKeyTransRecipient(cert);
// automatically generate the AES key, encrypt the data, and encrypt the key
CMSEnvelopedData data = envelopedDataGen.generate(new CMSProcessableFile(plainfile),
CMSEnvelopedDataGenerator.AES256_CBC, 256, BouncyCastleProvider.PROVIDER_NAME);
byte[] encryptedData = data.getEncoded();
然后,在服务器上,我可以解密这些数据,如下所示:
// load private key
KeyStore privateKs = KeyStore.getInstance(KeyStore.getDefaultType());
privateKs.load(new FileInputStream("/path/to/privateKeystore.ks"), null);
PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) privateKs.getEntry("privatekey",
new KeyStore.PasswordProtection("password".toCharArray()));
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
byte[] encryptedData = ...
// parse CMS envelope data
CMSEnvelopedDataParser envelopedDataParser = new CMSEnvelopedDataParser(new ByteArrayInputStream(encryptedData));
// expect exactly one recipient
Collection<?> recipients = envelopedDataParser.getRecipientInfos().getRecipients();
if (recipients.size() != 1)
throw new IllegalArgumentException();
// retrieve recipient and decode it
RecipientInformation recipient = (RecipientInformation) recipients.iterator().next();
byte[] decryptedData = recipient.getContent(privateKey, BouncyCastleProvider.PROVIDER_NAME);
如果我遗漏了重要的东西或在这里犯了错误,请留下评论......谢谢!