如何在Java中加密String

问题

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

其他需求:

  • 不应该复杂
  • 它不应包含RSA,PKI基础设施,密钥对等。

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

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

你有什么建议?是否有一些Java类在执行高安全性标准时没有太多的复杂性来执行encrypt()decrypt()?


#1 热门回答(110 赞)

我建议使用一些广泛可用的标准对称密码,如DES,3DESorAES。虽然这不是最安全的算法,但是有很多实现,你只需要将密钥交给任何应该解密条形码信息的人.javax.crypto.Cipher这就是你想要在这里工作的东西。
我们假设要加密的字节在

byte[] input;

下一步,你需要密钥和initialization vectorbytes

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);

#2 热门回答(55 赞)

这是第一个通过Google显示的页面,所有实施中的安全漏洞让我感到畏缩,因此我发布此信息是为了添加有关其他人的加密信息,因为距离原始帖子已有7年了。我拥有计算机工程硕士学位,并花了很多时间学习和学习密码学,所以我投入了两分钱让互联网变得更加安全。另外,请注意,对于给定的情况,许多实现可能是安全的,但为什么要使用这些实现并且可能意外地犯了错误?除非你有特殊原因,否则请使用你可用的最强大的工具。总的来说,我强烈建议你使用图书馆,如果可以,请远离细节。更新4/5/18:我重写了一些部分以使它们更容易理解并将推荐的库从Jasypt更改为Google的新库Tink,我建议从现有设置中完全删除Jasypt。
前言
我将在下面概述安全对称加密的基础知识,并指出当人们使用标准Java库自己实现加密时我在网上看到的常见错误。如果你只想跳过运行到项目中的所有详细信息到Google's new library Tinkimport,并使用AES-GCM模式进行所有加密,那么你应该是安全的。

现在,如果你想了解如何在java中加密的细节,请阅读:)
分组密码
首先,你需要选择对称密钥块密码。分组密码是用于创建伪随机性的计算机功能/程序。伪随机性是伪随机性,除了量子计算机之外,没有任何计算机能够区分它与真实随机性之间的区别。分组密码就像加密的构建块一样,当与不同的模式或方案一起使用时,我们可以创建加密。

现在关于今天可用的分组密码算法,请确保335514032 NEVER**,I repeat从不useDES,我甚至会说不使用3DES。即使Snowden的NSA版本能够验证真正尽可能接近伪随机的唯一Block Cipher是AES 256。还存在AES 128,区别在于AES 256在256位块中工作,而AES 128在128块中工作。总而言之,虽然已经发现了一些弱点,但AES 128被认为是安全的,但256就像它一样坚固。

有趣的事实DES在最初成立时被美国国家安全局打破,实际上保密了几年,虽然有些人仍然声称有保障,但有很多研究论文发现并分析了3DES的弱点。
加密模式
当你使用分组密码并使用特定方案时,会创建加密,以便随机性与密钥组合,以便在你知道密钥时创建可逆的内容。这被称为加密模式。

以下是加密模式和称为ECB的最简单模式的示例,以便你可以直观地了解发生的情况:

ECB Mode

你最常见的在线加密模式如下:
ECB CTR,CBC,GCM
除了列出的模式之外还存在其他模式,研究人员总是致力于新模式以改善现有问题。

现在让我们继续讨论实现和什么是安全.从来没有使用ECB这对于隐藏重复数据是不好的,如着名的Linux penguin.
Linux Penguin Example
所示

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

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

**...危险这是一个脆弱性!**并且遗憾的是,这在整个StackOverflow和在线教程和示例中都可以看到。
Nonce和IVs
针对ECB模式中发现的问题,已经创建了nounce,也称为IV。我们的想法是,我们生成一个新的随机变量并将其附加到每个加密,这样当你加密两个相同的消息时,它们就会变得不同。这背后的美丽是IV或nonce是公共知识。这意味着攻击者可以访问此权限,但只要他们没有你的密钥,他们就无法利用这些知识做任何事情。

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

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

**注意:**SHA1坏了,但我找不到如何正确地将SHA256实现到这个用例中,所以如果有人想对此进行破解并更新它将是非常棒的!此外,SHA1攻击仍然是非传统的,因为在一个巨大的集群上可能需要几年才能破解.Check out details here.
CTR实施
CTR模式不需要填充。

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

CBC Implementation
如果你选择实施CBC模式,请执行此操作PKCS7Padding如下:

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

CBC和点击率漏洞以及为何使用GCM
虽然其他一些模式(如CBC和CTR)是安全的,但它们遇到的问题是攻击者可以翻转加密数据,在解密时更改其值。因此,假设你加密了一个假想的银行消息"卖100",你的加密消息看起来像这样"eu23ng",攻击者将一位改为"eu53ng",并且在解密你的消息时突然显示为"卖出900"。

为了避免这种情况,大多数互联网使用GCM,每次看到HTTPS时他们都可能使用GCM。 GCM使用散列对加密消息进行签名,并检查以验证消息是否未使用此签名进行更改。

由于其复杂性,我会避免实施GCM。你最好还是使用Googles new library Tink,因为如果你不小心重复一次静脉注射,你会在GCM的情况下妥协你的密钥,这是最终的安全漏洞。新的研究人员正致力于IV重复抗性加密模式,即使你重复IV,关键也没有危险,但这尚未成为主流。

现在,如果你想要实现GCM,这里是alink to a nice GCM implementation。但是,我无法确保安全性,或者它是否正确实施但是它得到了基础。另请注意,GCM没有填充。

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

密钥vs密码
另一个非常重要的注意事项是,当涉及到加密时,密钥和密码不是一回事。密码学中的密钥需要具有一定量的熵和随机性才能被认为是安全的。这就是为什么你需要确保使用适当的加密库为你生成密钥的原因。

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

另一个不太安全的选项是使用用户输入,例如密码。我们讨论的问题是密码没有足够的熵,所以我们必须使用PBKDF2,这是一种获取密码并加强密码的算法。这是aStackOverflow implementation I liked。但是谷歌Tink库已经内置了所有这些,你应该利用它。
Android开发者
需要指出的一点是要知道你的android代码是反向可操作的,大多数情况下大多数java代码也是如此。这意味着如果你在代码中以纯文本格式存储密码。黑客可以轻松检索它。通常,对于这些类型的加密,你希望使用非对称加密等。这超出了本文的范围,所以我将避免潜入它。

Aninteresting reading from 2013:指出Android中88%的Crypto实现都是不正确的。
最后的想法
再一次,我建议避免直接实现加密的java库和使用Google Tink,它会让你头疼,因为他们已经真正做好了正确实现所有算法的工作。即使这样,也要确保你查看Tink github上提出的问题,这里和那里都会出现漏洞。

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


#3 热门回答(22 赞)

警告请勿将其用作某种安全措施。此帖子中的加密机制是一次性密码,这意味着攻击者可以使用2个加密消息轻松恢复密钥。 XOR 2加密消息,你得到了密钥。那很简单!穆萨指出

我正在使用Sun的Base64Encoder / Decoder,它可以在Sun的JRE中找到,以避免在lib中使用另一个JAR。从使用OpenJDK或其他一些JRE开始,这是危险的。除此之外,还有另一个原因我应该考虑使用带有编码器/解码器的Apache commons lib吗?

public class EncryptUtils {
    public static final String DEFAULT_ENCODING = "UTF-8"; 
    static BASE64Encoder enc = new BASE64Encoder();
    static BASE64Decoder dec = new BASE64Decoder();

    public static String base64encode(String text) {
        try {
            return enc.encode(text.getBytes(DEFAULT_ENCODING));
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }//base64encode

    public static String base64decode(String text) {
        try {
            return new String(dec.decodeBuffer(text), DEFAULT_ENCODING);
        } catch (IOException e) {
            return null;
        }
    }//base64decode

    public static void main(String[] args) {
        String txt = "some text to be encrypted";
        String key = "key phrase used for XOR-ing";
        System.out.println(txt + " XOR-ed to: " + (txt = xorMessage(txt, key)));

        String encoded = base64encode(txt);       
        System.out.println(" is encoded to: " + encoded + " and that is decoding to: " + (txt = base64decode(encoded)));
        System.out.print("XOR-ing back to original: " + xorMessage(txt, key));
    }

    public static String xorMessage(String message, String key) {
        try {
            if (message == null || key == null) return null;

            char[] keys = key.toCharArray();
            char[] mesg = message.toCharArray();

            int ml = mesg.length;
            int kl = keys.length;
            char[] newmsg = new char[ml];

            for (int i = 0; i < ml; i++) {
                newmsg[i] = (char)(mesg[i] ^ keys[i % kl]);
            }//for i

            return new String(newmsg);
        } catch (Exception e) {
            return null;
        }
    }//xorMessage
}//class