Java 密码加密

发布于 2024-07-10 14:49:50 字数 333 浏览 9 评论 0原文

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

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

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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

兰花执着 2024-07-17 14:49:50

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

话虽如此,我认为 Sun 的 Java 加密体系结构 ( JCA)参考指南是一个很好的起点。 查看随附的代码示例 说明基于密码的加密 (PBE)。

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

简单地说,PBE的加密方法可能包括以下步骤:

  1. 从用户处获取密码和明文,并将它们转换为字节数组。
  2. 生成安全的随机salt
  3. 将盐附加到密码并计算其加密哈希。 重复此操作多次。
  4. 使用生成的散列作为初始化向量和/或秘密密钥
  5. 保存盐和生成的密文。

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 Guide is a good starting point. Check out the accompanying code example illustrating Password-Based Encryption (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).

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

  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 vector and/or secret key.
  5. Save the salt and the resulting ciphertext.
酒绊 2024-07-17 14:49:50

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

Java 中有一些 LGPL 实现,但快速浏览一下 这个 看起来有点过于复杂了。 还有一个很好的 javascript 版本。 (我制作了 修改后的版本 由于缺乏适当许可证的

良好实现,我从 Mattias Gartner 打包了一些代码。 这是完整的代码。 简短、简单、容易理解。 它根据 MS 公共许可证 获得许可。

// 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 () {}

}

Use RFC2898 to 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).

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

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.

// 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 () {}

}
嗳卜坏 2024-07-17 14:49:50

在上面 Cheeso 非常有帮助的答案中,存在一个严重的性能错误。

该行

int l = Math.max( dkLen, hLen)

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

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

这将使 16 字节密钥的计算速度加快 20 倍。

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

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.

复古式 2024-07-17 14:49:50

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

Security.addProvider(new BouncyCastleProvider());

然后,使用 JCE 接口来完成您想要的任何操作:

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

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

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 here
The 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.

Security.addProvider(new BouncyCastleProvider());

And then only use the JCE interfaces to do whatever you want:

 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.

诗笺 2024-07-17 14:49:50

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

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

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

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

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

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

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.

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.

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

萌酱 2024-07-17 14:49:50

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文