java Java密码加密

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/372268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 12:07:37  来源:igfitidea点击:

Java passphrase encryption

javaencryptionpassphrase

提问by skiphoppy

I'm trying to learn how to do passphrase-based encryption with Java. I'm finding several examples online, but none (yet) on Stack Overflow. The examples are a little light on explanation for me, particularly regarding algorithm selection. There seems to be a lot of passing strings around to say what algorithms to use, but little documentation as to where the strings came from and what they mean. And it also seems like the different algorithms may require different implementations of the KeySpec class, so I'm not sure what algorithms can use the PBEKeySpec class I'm looking at. Furthermore, the examples all seem a little out of date, many requiring you to get an older cryptography package that used to not be part of the JDK, or even a third-party implementation.

我正在尝试学习如何使用 Java 进行基于密码的加密。我在网上找到了几个例子,但在 Stack Overflow 上还没有找到。这些例子对我来说有点解释,特别是关于算法选择。似乎有很多传递字符串来说明要使用的算法,但很少有关于字符串来自何处及其含义的文档。而且似乎不同的算法可能需要 KeySpec 类的不同实现,所以我不确定哪些算法可以使用我正在查看的 PBEKeySpec 类。此外,这些示例似乎都有点过时,许多示例要求您获得一个旧的加密包,而该包曾经不是 JDK 的一部分,甚至不是第三方实现的一部分。

Can someone provide a straightforward introduction to what I need to do to implement encrypt(String data, String passphrase) and decrypt(byte[] data, String passphrase)?

有人可以直接介绍我需要做什么来实现加密(字符串数据,字符串密码)和解密(字节[] 数据,字符串密码)?

回答by Zach Scrivena

I'll be cautious about giving or taking security-related advice from a forum... the specifics are quite intricate, and often become outdated quickly.

我会谨慎地从论坛上提供或接受与安全相关的建议……具体细节非常复杂,而且通常很快就会过时。

Having said that, I think Sun's Java Cryptography Architecture (JCA) Reference Guideis a good starting point. Check out the accompanying code exampleillustrating Password-Based Encryption (PBE).

话虽如此,我认为 Sun 的Java Cryptography Architecture (JCA) Reference Guide是一个很好的起点。查看说明基于密码的加密 (PBE)的随附代码示例

Btw, the standard JRE provides only a few options out-of-the-box for PBE ("PBEWithMD5AndDES" is one of them). For more choices, you'll need the "strong encryption pack" or some third-party provider like Bouncy Castle. Another alternative would be to implement your own PBE using the hash and cipher algorithms provided in the JRE. You can implement PBE with SHA-256 and AES-128 this way (sample encrypt/decrypt methods).

顺便说一句,标准 JRE 只为 PBE 提供了几个开箱即用的选项(“PBEWithMD5AndDES”就是其中之一)。要获得更多选择,您需要“强加密包”或一些第三方提供商,例如Bouncy Castle。另一种选择是使用 JRE 中提供的散列和密码算法实现您自己的 PBE。您可以通过这种方式使用 SHA-256 和 AES-128 实现 PBE(示例加密/解密方法)。

Briefly, the encrypt method for PBE may involve the following steps:

简而言之,PBE 的加密方法可能包括以下步骤:

  1. Get password and cleartext from the user, and convert them to byte arrays.
  2. Generate a secure random salt.
  3. Append the salt to the password and compute its cryptographic hash. Repeat this many times.
  4. Encrypt the cleartext using the resulting hash as the initialization vectorand/or secret key.
  5. Save the salt and the resulting ciphertext.
  1. 从用户那里获取密码和明文,并将它们转换为字节数组。
  2. 生成一个安全的随机
  3. 将盐附加到密码并计算其加密哈希。多次重复此操作。
  4. 使用生成的散列作为初始化向量和/或密钥加密明文。
  5. 保存盐和生成的密文。

回答by Cheeso

Use RFC2898to generate keys from passwords. This isn't included in the JRE or JCE, as far as I know, but it is included in J2EE Servers like JBoss, Oracle, and WebSphere. It is also included in the .NET Base Class Library (Rfc2898DeriveBytes).

使用RFC2898从密码生成密钥。据我所知,这不包含在 JRE 或 JCE 中,但它包含在 J2EE 服务器中,如JBoss、Oracle 和WebSphere。它也包含在 .NET 基类库 ( Rfc2898DeriveBytes) 中。

There are some LGPL implementations in Java out there, but on a quick look this onelooks a little over complicated. There is also a good javascript version. (I produced a modified version of that oneand packaged it as a Windows Script Component)

有一些 Java 中的 LGPL 实现,但快速浏览一下,这个实现看起来有点过于复杂。还有一个很好的javascript 版本。(我制作了该版本的修改版本并将其打包为 Windows 脚本组件)

Lacking a good implementation with an appropriate license, I packaged some code up from Mattias Gartner. This is the code in its entirety. Short, simple, easy to understand. It's licensed under the MS Public License.

由于缺乏具有适当许可的良好实现,我从 Mattias Gartner 打包了一些代码。这是完整的代码。简短,简单,易于理解。它是根据MS Public License获得许可的

// PBKDF2.java
// ------------------------------------------------------------------
//
// RFC2898 PBKDF2 in Java.  The RFC2898 defines a standard algorithm for
// deriving key bytes from a text password.  This is sometimes
// abbreviated "PBKDF2", for Password-based key derivation function #2.
//
// There's no RFC2898-compliant PBKDF2 function in the JRE, as far as I
// know, but it is available in many J2EE runtimes, including those from
// JBoss, IBM, and Oracle.
//
// It's fairly simple to implement, so here it is. 
// 
// Created Sun Aug 09 01:06:57 2009
//
// last saved: 
// Time-stamp: <2009-August-09 02:19:50>
// ------------------------------------------------------------------
//
// code thanks to Matthias Gartner
//
// ------------------------------------------------------------------

package cheeso.examples;


import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class PBKDF2
{
    public static byte[] deriveKey( byte[] password, byte[] salt, int iterationCount, int dkLen )
        throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException
    {
        SecretKeySpec keyspec = new SecretKeySpec( password, "HmacSHA1" );
        Mac prf = Mac.getInstance( "HmacSHA1" );
        prf.init( keyspec );

        // Note: hLen, dkLen, l, r, T, F, etc. are horrible names for
        //       variables and functions in this day and age, but they
        //       reflect the terse symbols used in RFC 2898 to describe
        //       the PBKDF2 algorithm, which improves validation of the
        //       code vs. the RFC.
        //
        // dklen is expressed in bytes. (16 for a 128-bit key)

        int hLen = prf.getMacLength();   // 20 for SHA1
        int l = Math.max( dkLen, hLen); //  1 for 128bit (16-byte) keys
        int r = dkLen - (l-1)*hLen;      // 16 for 128bit (16-byte) keys
        byte T[] = new byte[l * hLen];
        int ti_offset = 0;
        for (int i = 1; i <= l; i++) {
            F( T, ti_offset, prf, salt, iterationCount, i );
            ti_offset += hLen;
        }

        if (r < hLen) {
            // Incomplete last block
            byte DK[] = new byte[dkLen];
            System.arraycopy(T, 0, DK, 0, dkLen);
            return DK;
        }
        return T;
    } 


    private static void F( byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex ) {
        final int hLen = prf.getMacLength();
        byte U_r[] = new byte[ hLen ];
        // U0 = S || INT (i);
        byte U_i[] = new byte[S.length + 4];
        System.arraycopy( S, 0, U_i, 0, S.length );
        INT( U_i, S.length, blockIndex );
        for( int i = 0; i < c; i++ ) {
            U_i = prf.doFinal( U_i );
            xor( U_r, U_i );
        }

        System.arraycopy( U_r, 0, dest, offset, hLen );
    }

    private static void xor( byte[] dest, byte[] src ) {
        for( int i = 0; i < dest.length; i++ ) {
            dest[i] ^= src[i];
        }
    }

    private static void INT( byte[] dest, int offset, int i ) {
        dest[offset + 0] = (byte) (i / (256 * 256 * 256));
        dest[offset + 1] = (byte) (i / (256 * 256));
        dest[offset + 2] = (byte) (i / (256));
        dest[offset + 3] = (byte) (i);
    } 

    // ctor
    private PBKDF2 () {}

}

回答by johsin18

In Cheeso's very helpful answer above, there is a bad performance bug.

在上面 Cheeso 非常有用的答案中,有一个糟糕的性能错误。

The line

线

int l = Math.max( dkLen, hLen)

should not caculate the max, but the ceiling of the division, so

不应该计算最大值,而是计算除法的上限,所以

int l = ((dkLen - 1) / hLen) + 1; // >= ceil(dkLen / hLen), == for dkLen =>1

This will speed up computation by a factor of 20 for 16 byte keys.

对于 16 字节密钥,这将使计算速度提高 20 倍。

回答by Jim Barrows

You need an encryption library, which will tell you how to set it up.
I happen to like the stuff from bouncycastle.org. You can find their how to hereThe DES the refer to in the 5.1 example, is one of the encryptions they offer. What the actual string means, will depend on the provider. Essentially you load the library.

您需要一个加密库,它会告诉您如何设置它。
我碰巧喜欢 bouncycastle.org 上的东西。您可以在此处找到他们的操作方法 5.1 示例中所指的 DES 是他们提供的加密之一。实际字符串的含义取决于提供者。基本上你加载库。

Security.addProvider(new BouncyCastleProvider());

And then onlyuse the JCE interfaces to do whatever you want:

然后使用 JCE 接口做任何你想做的事:

 keyGen = KeyGenerator.getInstance("DES", "BC");

Java handles the binding of the library and the interfaces for you, you don't have to do that. I'd be more then happy to explain more, if you have any questions. Unfortunately at the moment I'm suffering from "I can't remember how I learned it" disease, so please feel free to ask.

Java 为您处理库和接口的绑定,您不必这样做。如果您有任何问题,我会更乐意解释更多。不幸的是,目前我患有“我不记得我是如何学会它的”疾病,所以请随时提问。

回答by frankodwyer

You could use a hash algorithm (multiple times if necessary) to get from the passphrase to some raw data you can use as a key (+ an initialisation vector if the algorithm calls for one).

您可以使用散列算法(必要时多次)从密码短语获取一些可以用作密钥的原始数据(如果算法需要一个初始化向量,则为一个初始化向量)。

Then you can use that key with any symmetric algorithm - such as 3DES-CBC or AES-CBC (DES is considered obsolete these days).

然后,您可以将该密钥与任何对称算法一起使用,例如 3DES-CBC 或 AES-CBC(如今 DES 已过时)。

Depending on the JCE you have available you may have different algorithms at your disposal, but AES is probably what you want. Choice of algorithm and exactly how to use it is somewhat a religious issue, however, and you would be ill advised to try and roll your own, or even to try and build some encryption scheme of your own using standard algorithms. You will almost certainly get it wrong if you have not studied it, and maybe even if you have.

根据您可用的 JCE,您可能有不同的算法可供您使用,但 AES 可能是您想要的。然而,算法的选择以及如何使用它在某种程度上是一个宗教问题,并且不建议您尝试使用自己的算法,甚至尝试使用标准算法构建一些您自己的加密方案。如果你没有研究过它,你几乎肯定会弄错,即使你有。

If the security is that important to you that you are considering encryption, then you should consider also looking at a security engineering book like Applied Cryptography by Bruce Schneier or Security Engineering by Ross Anderson - there are a lot of implementation pitfalls. For example, using a passphrase as a key is not that great an idea in the first place, as it essentially reduces the size of your key.

如果安全性对您来说如此重要以至于您正在考虑加密,那么您还应该考虑查看安全工程书籍,例如 Bruce Schneier 的 Applied Cryptography 或 Ross Anderson 的 Security Engineering - 有很多实施陷阱。例如,首先使用密码短语作为密钥并不是一个好主意,因为它本质上减少了密钥的大小。

You could also look at designs that other people have done, there are lots at the IETF, e.g.: http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha1-00

你也可以看看其他人做过的设计,IETF 有很多,例如:http: //tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha1-00

回答by Chochos

If you don't need to decrypt the passphrase, but just generate an encryption key based on a password/passphrase, you can implement the PKCS#5 standard, using the JCE Cipher and MessageDigest classes.

如果您不需要解密密码,而只是根据密码/密码生成加密密钥,则可以使用 JCE Cipher 和 MessageDigest 类实现PKCS#5 标准