如何在 Java 中加密字符串

新手上路,请多包涵

我需要的是加密将显示在 2D 条形码 (PDF-417) 中的字符串,因此当有人想到扫描它时,将无法读取任何内容。

其他需求:

  • 应该不复杂
  • 它不应包含 RSA、PKI 基础结构、密钥对等。

它必须足够简单才能摆脱四处窥探的人,并且对于其他有兴趣获取该数据的公司来说也很容易解密。他们打电话给我们,我们告诉他们标准或给他们一些简单的密钥,然后可以将其用于解密。

这些公司可能会使用不同的技术,因此最好坚持一些与某些特殊平台或技术无关的标准。

你有什么建议?是否有一些 Java 类正在做 encrypt() & decrypt() 在实现高安全标准方面没有太多复杂性?

原文由 ante.sabo 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 913
2 个回答

这是通过 Google 显示的第一个页面,所有实施中的安全漏洞让我感到畏缩,所以我发布此内容是为了为其他人添加有关加密的信息,因为它距离原始帖子已经 7 年 了。我拥有计算机工程 硕士学位,并花了很多时间研究和学习密码学,所以我花两分钱让互联网成为一个更安全的地方。

另外,请注意,对于给定的情况,很多实现可能是安全的,但为什么要使用这些实现并可能不小心犯错误呢?除非有特殊原因,否则请使用可用的最强大的工具。总的来说,我强烈建议使用图书馆,并尽可能远离细节。

2018 年 4 月 5 日更新: 我重写了一些部分以使它们更易于理解,并将推荐的库从 Jasypt 更改Google 的新库 Tink ,我建议从现有设置中完全删除 Jasypt

前言

我将在下面概述安全对称加密的基础知识,并指出人们在使用标准 Java 库自行实现加密时在网上看到的常见错误。如果您只想跳过所有详细信息,请转到 Google 的新库 Tink ,将其导入您的项目并使用 AES-GCM 模式进行所有加密,这样您就安全了。

现在,如果您想了解有关如何在 Java 中加密的细节,请继续阅读 :)

块密码

首先,您需要选择一个对称密钥分组密码。块密码是一种用于创建伪随机性的计算机函数/程序。伪随机性是假随机性,除了量子计算机之外,没有任何计算机能够区分它与真实随机性之间的区别。块密码就像密码学的基石,当与不同的模式或方案一起使用时,我们可以创建加密。

现在关于今天可用的分组密码算法,请确保 永远不要,我重复 永远不要 使用 DES ,我什至会说永远不要使用 3DES 。甚至斯诺登的 NSA 版本也能够验证真正尽可能接近伪随机的唯一块密码是 AES 256 。还有 AES 128;不同之处在于 AES 256 在 256 位块中工作,而 AES 128 在 128 位块中工作。总而言之,AES 128 被认为是安全的,尽管已经发现了一些弱点,但 256 是最可靠的。

有趣的是, DES 在最初成立时就被 NSA 破解了,实际上保密了几年。尽管仍然有人声称 3DES 是安全的,但已经有不少研究论文发现并分析了 3DES 的弱点。

加密模式

当您采用分组密码并使用特定方案时,就会创建加密,以便将随机性与密钥相结合,以创建只要您知道密钥就可逆的东西。这称为加密模式。

下面是一个加密模式的例子,最简单的模式称为 ECB,这样您就可以直观地了解正在发生的事情:

欧洲央行模式

您在网上最常见的加密模式如下:

ECB CTR、CBC、GCM

除了列出的模式之外,还存在其他模式,研究人员一直在努力寻找新的模式来改善现有问题。

现在让我们继续讨论实现以及什么是安全的。 永远不要 使用 ECB,因为它不擅长隐藏重复数据,如著名的 Linux 企鹅 所示。 Linux 企鹅示例

在Java中实现时,注意如果使用下面的代码,默认设置为ECB模式:

 Cipher cipher = Cipher.getInstance("AES");

…危险,这是一个漏洞! 不幸的是,这在整个 StackOverflow 和在线教程和示例中随处可见。

随机数和 IV

为了响应 ECB 模式中发现的问题,创建了也称为 IV 的公告。这个想法是我们生成一个新的随机变量并将其附加到每个加密,这样当您加密两条相同的消息时,它们会出现不同。这背后的美妙之处在于 IV 或随机数是公共知识。这意味着攻击者可以访问它,但只要他们没有您的密钥,他们就无法利用这些信息做任何事情。

我将看到的常见问题是人们会将 IV 设置为静态值,就像在他们的代码中使用相同的固定值一样。这是 IV 的陷阱,当你重复一个你实际上危及加密的整个安全性时。

生成随机 IV

 SecureRandom randomSecureRandom = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);

注意: SHA1 已损坏,但我找不到如何将 SHA256 正确地实施到这个用例中,所以如果有人想破解并更新它,那就太棒了!此外,SHA1 攻击仍然是非常规的,因为在一个巨大的集群上可能需要几年时间才能破解。 在这里查看详细信息。

点击率实施

CTR 模式不需要填充。

  Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

CBC实施

如果您选择实施 CBC 模式,请按如下方式使用 PKCS7Padding:

  Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

CBC 和 CTR 漏洞以及您应该使用 GCM 的原因

尽管 CBC 和 CTR 等其他一些模式是安全的,但它们会遇到攻击者可以翻转加密数据、在解密时更改其值的问题。因此,假设您加密了一个假想的银行消息“Sell 100”,您的加密消息看起来像这样“eu23ng”,攻击者将一位更改为“eu53ng”,并且在解密您的消息时突然间,它显示为“Sell 900”。

为了避免这种情况,大多数互联网都使用 GCM,并且每次您看到 HTTPS 时,它们都可能使用 GCM。 GCM 使用哈希对加密消息进行签名,并使用此签名检查消息是否未被更改。

由于其复杂性,我会避免实施 GCM。您最好使用 Google 的新库 Tink ,因为在这里,如果您不小心重复了 IV,那么您就是在使用 GCM 的情况下泄露密钥,这是最终的安全漏洞。新的研究人员正致力于 IV 抗重复加密模式,即使您重复 IV,密钥也没有危险,但这尚未成为主流。

现在,如果您确实想要实施 GCM,这里有 一个很好的 GCM 实施的链接。但是,我无法确保安全性,或者如果它得到正确实施,但它会降低基础。还要注意 GCM 没有填充。

 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

密钥与密码

另一个非常重要的注意事项是,在密码学方面,密钥和密码不是一回事。密码学中的密钥需要具有一定的熵和随机性才能被认为是安全的。这就是为什么您需要确保使用正确的加密库来为您生成密钥。

所以你真的有两个实现你可以在这里做,第一个是使用在 这个 StackOverflow 线程上找到的代码来生成随机密钥。该解决方案使用安全的随机数生成器从头开始创建您可以使用的密钥。

另一个不太安全的选项是使用用户输入,例如密码。我们讨论的问题是密码没有足够的熵,所以我们必须使用 PBKDF2 ,一种获取密码并加强它的算法。这是 我喜欢的 StackOverflow 实现。然而,Google Tink 库内置了所有这些,您应该利用它。

安卓开发者

这里要指出的重要一点是知道你的 android 代码是可逆向工程的,大多数情况下大多数 java 代码也是。这意味着如果您在代码中以纯文本形式存储密码。黑客可以轻松检索它。通常,对于这些类型的加密,您希望使用非对称密码术等。这超出了本文的范围,因此我将避免深入探讨。

2013 年的一个有趣读物:指出 Android 中 88% 的加密实现都没有正确完成。

最后的想法

我再次建议避免直接为加密实现 java 库并使用 Google Tink ,它会让你省去头痛,因为他们在正确实现所有算法方面确实做得很好。即便如此,也要确保你检查了 Tink github 上提出的问题,这里那里弹出的漏洞。

如果您有任何问题或反馈,请随时发表评论!安全性总是在变化,您需要尽最大努力跟上它:)

原文由 Konstantino Sparakis 发布,翻译遵循 CC BY-SA 4.0 许可协议

我建议使用一些广泛使用的标准对称密码,如 DES3DESAES 。虽然这不是最安全的算法,但有很多实现,您只需要将密钥提供给任何应该解密条形码信息的人即可。 javax.crypto.Cipher 是您要在此处使用的内容。

假设要加密的字节在

byte[] input;

接下来,您需要密钥和 初始化向量 字节

byte[] keyBytes;
byte[] ivBytes;

现在您可以为您选择的算法初始化密码:

 // wrap key data in Key/IV specs to pass to cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// create the cipher with the algorithm you choose
// see javadoc for Cipher class for more info, e.g.
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

加密会像这样:

 cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);

像这样解密:

 cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);

原文由 VoidPointer 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题