Java 256位AES密码加密

Nip*_*rus 381 java encryption passwords cryptography aes

我需要实现256位AES加密,但我在网上找到的所有示例都使用"KeyGenerator"生成256位密钥,但我想使用自己的密码.如何创建自己的密钥?我已经尝试将其填充为256位,但后来我得到一个错误,说密钥太长了.我确实安装了无限管辖区补丁,所以那不是问题:)

IE浏览器.KeyGenerator看起来像这样......

// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available

// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
Run Code Online (Sandbox Code Playgroud)

代码来自这里

编辑

我实际上是将密码填充到256个字节,而不是位,这太长了.以下是我现在使用的一些代码,我对此有更多的经验.

byte[] key = null; // TODO
byte[] input = null; // TODO
byte[] output = null;
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
output = cipher.doFinal(input)
Run Code Online (Sandbox Code Playgroud)

您需要自己做的"TODO"位:-)

eri*_*son 460

与收件人带外共享password(a char[])和salt(byte[]由a选择的-8字节SecureRandom构成一个好的盐 - 不需要保密).然后从这些信息中获得一个好的密钥:

/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Run Code Online (Sandbox Code Playgroud)

幻数(可以在某处定义为常量)65536和256分别是密钥导出迭代计数和密钥大小.

密钥派生函数被迭代以需要大量的计算工作,并且可以防止攻击者快速尝试许多不同的密码.可以根据可用的计算资源来改变迭代计数.

密钥大小可以减少到128位,这仍然被认为是"强"加密,但如果发现削弱AES的攻击,它不会给予很大的安全余量.

与适当的块链接模式一起使用时,可以使用相同的派生密钥来加密许多消息.在密码块链接(CBC)中,为每个消息生成随机初始化向量(IV),即使纯文本相同,也产生不同的密文.CBC可能不是您可以使用的最安全模式(参见下面的AEAD); 还有许多其他模式具有不同的安全属性,但它们都使用类似的随机输入.在任何情况下,每个加密操作的输出都是密文初始化向量:

/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
Run Code Online (Sandbox Code Playgroud)

存储ciphertextiv.在解密时,SecretKey使用具有相同salt和迭代参数的密码以完全相同的方式重新生成.使用此密钥初始化密码,使用消息存储初始化向量:

/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
Run Code Online (Sandbox Code Playgroud)

Java 7包含对AEAD密码模式的 API 支持,OpenJDK和Oracle发行版中包含的"SunJCE"提供程序从Java 8开始实现这些.强烈建议使用其中一种模式代替CBC; 它将保护数据的完整性和隐私.


java.security.InvalidKeyException与消息"非法密钥大小或默认参数"指的是加密强度有限的; 无限强度管辖区域策略文件不在正确的位置.在JDK中,它们应放在下面${jdk}/jre/lib/security

根据问题描述,听起来好像没有正确安装策略文件.系统可以轻松拥有多个Java运行时; 仔细检查以确保使用正确的位置.

  • @Nick:阅读PKCS#5.PBKDF2需要盐,这就是基于密码的加密API需要它们作为密钥派生的输入的原因.在没有盐的情况下,可以使用字典攻击,从而实现最可能的对称加密密钥的预先计算列表.密码IV和密钥衍生盐用于不同的目的.IV允许一个人为多个消息重用相同的密钥.Salts防止对密钥进行字典攻击. (28认同)
  • 我已经将@ erickson的答案实现为一个类:https://github.com/mrclay/jSecureEdit/tree/master/src/org/mrclay/crypto(PBE完成工作,PBEStorage是一个用于存储IV /的值对象密文在一起.) (6认同)
  • 要运行此代码,请确保您的JRE中具有正确的无限强度管辖权政策文件,如http://www.ngs.ac.uk/tools/jcepolicyfiles中所述 (6认同)
  • 在这种情况下,我会将密文存储在一个字段中,将 IV 存储在另一个字段中,并将“盐”和“迭代”一起存储在第三个字段中。我会在客户端应用程序中提示用户输入密码,并使用存储的盐和迭代派生密钥。然后用派生的密钥和存储的 IV 初始化密码,并解密内容。 (2认同)
  • 首先,那将是DES加密,而不是AES.大多数提供商对`PBEwith <prf>和<encryption>`算法没有很好的支持; 例如,SunJCE不提供AES和PBE.其次,启用jasypt是一个非目标.声称提供安全性而不需要理解基本原则的软件包似乎是危险的. (2认同)
  • @AndyNuss此示例用于可逆加密,通常不应将其用于密码。您可以*使用PBKDF2密钥派生来安全地“哈希”密码。这意味着在上面的示例中,您将把“ tmp.getEncoded()”的结果存储为哈希。您还应该存储“盐”和迭代(在此示例中为65536),以便在有人尝试进行身份验证时可以重新计算哈希。在这种情况下,每次更改密码时,使用密码随机数生成器生成盐。 (2认同)
  • +1,但是如果您按照答案中的暗示通过网络发送加密的数据,则必须确保确保消息的完整性。换句话说,您需要生成另一个密钥才能使用MAC,或者可以使用经过身份验证的加密模式,例如GCM。 (2认同)
  • 还要注意,如果每次对明文进行加密时,盐都是完全随机的,则可以跳过随机IV的创建(以及发送/通信)。仅在每次加密的密钥不变的情况下才需要唯一/随机IV,但是使用随机盐将导致每次加密的密钥不同。 (2认同)
  • @voltar 是的,您可以调用`getIV()`。对于您的聊天应用程序,每条消息都有不同的 IV。什么构成消息取决于您如何编写代码。如果您创建一个 `CipherOutputStream` 并在聊天会话期间使用它来加密套接字的 `OutputStream`,那么就只有一个 IV。如果您将个人聊天发布到 Web 应用程序,那么每个聊天都会有一个唯一的 IV。基本上,应该在对 `Cipher` 调用 `doFinal()` 后生成一个新的 IV。 (2认同)
  • **警告**:*发送* CBC密文会导致填充oracle攻击方面的漏洞,并使您的纯文本容易受到更改的影响。要使用AES-256发送密文,请对AES-256密码套件使用TLS,并可能对派生密钥使用PSK。 (2认同)
  • java6和java 7不支持PBKDF2WithHmacSHA256 (2认同)
  • @ user2213684你错了.对于任何对称算法,PBKDF算法是从密码导出密钥的正确方法.并指定AES作为算法并使用"PBEKeySpec"将失败.当你没有任何经验或知识时,为什么要发表评论? (2认同)
  • @erickson:刚刚错过了!1.1.1 于 2018 年 9 月 11 日发布,在 `enc` 中添加了对 `-pbkdf2` 的支持——默认为 iter 10000 和 hmac-sha256,但这些可以更改 (2认同)

Joh*_*thy 70

考虑使用Spring Security Crypto Module

Spring Security Crypto模块支持对称加密,密钥生成和密码编码.代码作为核心模块的一部分进行分发,但不依赖于任何其他Spring Security(或Spring)代码.

它提供了一个简单的加密抽象,似乎符合这里的要求,

"标准"加密方法是使用PKCS#5的PBKDF2(基于密码的密钥导出功能#2)的256位AES.此方法需要Java 6.用于生成SecretKey的密码应保存在安全的位置而不能共享.在加密数据泄露的情况下,salt用于防止对密钥的字典攻击.还应用16字节随机初始化向量,因此每个加密消息是唯一的.

看看内部结构揭示了一个类似于埃里克森答案的结构.

如问题中所述,这还需要Java密码术扩展(JCE)无限强度管辖政策(否则您将遇到InvalidKeyException: Illegal Key Size).它可以下载Java 6,Java 7Java 8.

用法示例

import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;

public class CryptoExample {
    public static void main(String[] args) {
        final String password = "I AM SHERLOCKED";  
        final String salt = KeyGenerators.string().generateKey();

        TextEncryptor encryptor = Encryptors.text(password, salt);      
        System.out.println("Salt: \"" + salt + "\"");

        String textToEncrypt = "*royal secrets*";
        System.out.println("Original text: \"" + textToEncrypt + "\"");

        String encryptedText = encryptor.encrypt(textToEncrypt);
        System.out.println("Encrypted text: \"" + encryptedText + "\"");

        // Could reuse encryptor but wanted to show reconstructing TextEncryptor
        TextEncryptor decryptor = Encryptors.text(password, salt);
        String decryptedText = decryptor.decrypt(encryptedText);
        System.out.println("Decrypted text: \"" + decryptedText + "\"");

        if(textToEncrypt.equals(decryptedText)) {
            System.out.println("Success: decrypted text matches");
        } else {
            System.out.println("Failed: decrypted text does not match");
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

和样本输出,

Salt: "feacbc02a3a697b0"
Original text: "*royal secrets*"
Encrypted text: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
Decrypted text: "*royal secrets*"
Success: decrypted text matches

  • @theglauber是的,您可以使用没有Spring Security或Spring框架的模块.从查看[pom](https://github.com/spring-projects/spring-security/blob/master/crypto/pom.xml),唯一的运行时依赖是apache [commons-logging 1.1.1]( http://goo.gl/N3SYEU).您可以[使用maven拉入jar](http://goo.gl/yjxUBp)或[直接从官方二进制仓库下载](http://repo.spring.io/libs-release/org/springframework/security/spring-security-crypto /)(有关Spring二进制文件的更多信息,请参阅[Spring 4二进制文件下载](http://stackoverflow.com/q/20551709). (4认同)
  • Spring 实用程序使用的 `NULL_IV_GENERATOR` 不安全。如果应用没有提供IV,让提供者选择,初始化后查询。 (2认同)

wuf*_*foo 32

通读埃里克森的建议,并拾遗什么我可以从其他几个帖子和这个例子后在这里,我已经尝试与建议修改更新Doug的代码.随意编辑以使其更好.

  • 初始化向量不再固定
  • 加密密钥是使用erickson的代码派生的
  • 使用SecureRandom()在setupEncrypt()中生成8字节的salt
  • 解密密钥是从加密盐和密码生成的
  • 从解密密钥和初始化矢量生成解密密码
  • 删除了十六进制代替org.apache.commons 编解码器 Hex例程

一些注意事项:这使用128位加密密钥 - java显然不会开箱即用256位加密.实现256需要在java安装目录中安装一些额外的文件.

另外,我不是一个加密人.谨慎.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class Crypto
{
    String mPassword = null;
    public final static int SALT_LEN = 8;
    byte [] mInitVec = null;
    byte [] mSalt = null;
    Cipher mEcipher = null;
    Cipher mDecipher = null;
    private final int KEYLEN_BITS = 128; // see notes below where this is used.
    private final int ITERATIONS = 65536;
    private final int MAX_FILE_BUF = 1024;

    /**
     * create an object with just the passphrase from the user. Don't do anything else yet 
     * @param password
     */
    public Crypto (String password)
    {
        mPassword = password;
    }

    /**
     * return the generated salt for this object
     * @return
     */
    public byte [] getSalt ()
    {
        return (mSalt);
    }

    /**
     * return the initialization vector created from setupEncryption
     * @return
     */
    public byte [] getInitVec ()
    {
        return (mInitVec);
    }

    /**
     * debug/print messages
     * @param msg
     */
    private void Db (String msg)
    {
        System.out.println ("** Crypt ** " + msg);
    }

    /**
     * this must be called after creating the initial Crypto object. It creates a salt of SALT_LEN bytes
     * and generates the salt bytes using secureRandom().  The encryption secret key is created 
     * along with the initialization vectory. The member variable mEcipher is created to be used
     * by the class later on when either creating a CipherOutputStream, or encrypting a buffer
     * to be written to disk.
     *  
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidParameterSpecException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     */
    public void setupEncrypt () throws NoSuchAlgorithmException, 
                                                           InvalidKeySpecException, 
                                                           NoSuchPaddingException, 
                                                           InvalidParameterSpecException, 
                                                           IllegalBlockSizeException, 
                                                           BadPaddingException, 
                                                           UnsupportedEncodingException, 
                                                           InvalidKeyException
    {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;

        // crate secureRandom salt and store  as member var for later use
         mSalt = new byte [SALT_LEN];
        SecureRandom rnd = new SecureRandom ();
        rnd.nextBytes (mSalt);
        Db ("generated salt :" + Hex.encodeHexString (mSalt));

        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        /* Derive the key, given password and salt. 
         * 
         * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
         * The end user must also install them (not compiled in) so beware. 
         * see here:  http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
         */
        KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
        tmp = factory.generateSecret (spec);
        SecretKey secret = new SecretKeySpec (tmp.getEncoded(), "AES");

        /* Create the Encryption cipher object and store as a member variable
         */
        mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
        mEcipher.init (Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = mEcipher.getParameters ();

        // get the initialization vectory and store as member var 
        mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV();

        Db ("mInitVec is :" + Hex.encodeHexString (mInitVec));
    }



    /**
     * If a file is being decrypted, we need to know the pasword, the salt and the initialization vector (iv). 
     * We have the password from initializing the class. pass the iv and salt here which is
     * obtained when encrypting the file initially.
     *   
     * @param initvec
     * @param salt
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws DecoderException
     */
    public void setupDecrypt (String initvec, String salt) throws NoSuchAlgorithmException, 
                                                                                       InvalidKeySpecException, 
                                                                                       NoSuchPaddingException, 
                                                                                       InvalidKeyException, 
                                                                                       InvalidAlgorithmParameterException, 
                                                                                       DecoderException
    {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;
        SecretKey secret = null;

        // since we pass it as a string of input, convert to a actual byte buffer here
        mSalt = Hex.decodeHex (salt.toCharArray ());
       Db ("got salt " + Hex.encodeHexString (mSalt));

        // get initialization vector from passed string
        mInitVec = Hex.decodeHex (initvec.toCharArray ());
        Db ("got initvector :" + Hex.encodeHexString (mInitVec));


        /* Derive the key, given password and salt. */
        // in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
        // The end user must also install them (not compiled in) so beware. 
        // see here: 
      // http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);

        tmp = factory.generateSecret(spec);
        secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        /* Decrypt the message, given derived key and initialization vector. */
        mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
    }


    /**
     * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
     * Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
     * 
     * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
     * into uncertain problems with that. 
     *  
     * @param input - the cleartext file to be encrypted
     * @param output - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public void WriteEncryptedFile (File input, File output) throws 
                                                                                          IOException, 
                                                                                          IllegalBlockSizeException, 
                                                                                          BadPaddingException
    {
        FileInputStream fin;
        FileOutputStream fout;
        long totalread = 0;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        fout = new FileOutputStream (output);
        fin = new FileInputStream (input);

        while ((nread = fin.read (inbuf)) > 0 )
        {
            Db ("read " + nread + " bytes");
            totalread += nread;

            // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
            // and results in full blocks of MAX_FILE_BUF being written. 
            byte [] trimbuf = new byte [nread];
            for (int i = 0; i < nread; i++)
                trimbuf[i] = inbuf[i];

            // encrypt the buffer using the cipher obtained previosly
            byte [] tmp = mEcipher.update (trimbuf);

            // I don't think this should happen, but just in case..
            if (tmp != null)
                fout.write (tmp);
        }

        // finalize the encryption since we've done it in blocks of MAX_FILE_BUF
        byte [] finalbuf = mEcipher.doFinal ();
        if (finalbuf != null)
            fout.write (finalbuf);

        fout.flush();
        fin.close();
        fout.close();

        Db ("wrote " + totalread + " encrypted bytes");
    }


    /**
     * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
     * to disk as (output) File.
     * 
     * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
     *  and still have a correctly decrypted file in the end. Seems to work so left it in.
     *  
     * @param input - File object representing encrypted data on disk 
     * @param output - File object of cleartext data to write out after decrypting
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public void ReadEncryptedFile (File input, File output) throws 
                                                                                                                                            IllegalBlockSizeException, 
                                                                                                                                            BadPaddingException, 
                                                                                                                                            IOException
    {
        FileInputStream fin; 
        FileOutputStream fout;
        CipherInputStream cin;
        long totalread = 0;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        fout = new FileOutputStream (output);
        fin = new FileInputStream (input);

        // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
        cin = new CipherInputStream (fin, mDecipher);

        while ((nread = cin.read (inbuf)) > 0 )
        {
            Db ("read " + nread + " bytes");
            totalread += nread;

            // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
            byte [] trimbuf = new byte [nread];
            for (int i = 0; i < nread; i++)
                trimbuf[i] = inbuf[i];

            // write out the size-adjusted buffer
            fout.write (trimbuf);
        }

        fout.flush();
        cin.close();
        fin.close ();       
        fout.close();   

        Db ("wrote " + totalread + " encrypted bytes");
    }


    /**
     * adding main() for usage demonstration. With member vars, some of the locals would not be needed
     */
    public static void main(String [] args)
    {

        // create the input.txt file in the current directory before continuing
        File input = new File ("input.txt");
        File eoutput = new File ("encrypted.aes");
        File doutput = new File ("decrypted.txt");
        String iv = null;
        String salt = null;
        Crypto en = new Crypto ("mypassword");

        /*
         * setup encryption cipher using password. print out iv and salt
         */
        try
      {
          en.setupEncrypt ();
          iv = Hex.encodeHexString (en.getInitVec ()).toUpperCase ();
          salt = Hex.encodeHexString (en.getSalt ()).toUpperCase ();
      }
      catch (InvalidKeyException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchAlgorithmException e)
      {
          e.printStackTrace();
      }
      catch (InvalidKeySpecException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchPaddingException e)
      {
          e.printStackTrace();
      }
      catch (InvalidParameterSpecException e)
      {
          e.printStackTrace();
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (UnsupportedEncodingException e)
      {
          e.printStackTrace();
      }

        /*
         * write out encrypted file
         */
        try
      {
          en.WriteEncryptedFile (input, eoutput);
          System.out.printf ("File encrypted to " + eoutput.getName () + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (IOException e)
      {
          e.printStackTrace();
      }


        /*
         * decrypt file
         */
        Crypto dc = new Crypto ("mypassword");
        try
      {
          dc.setupDecrypt (iv, salt);
      }
      catch (InvalidKeyException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchAlgorithmException e)
      {
          e.printStackTrace();
      }
      catch (InvalidKeySpecException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchPaddingException e)
      {
          e.printStackTrace();
      }
      catch (InvalidAlgorithmParameterException e)
      {
          e.printStackTrace();
      }
      catch (DecoderException e)
      {
          e.printStackTrace();
      }

        /*
         * write out decrypted file
         */
        try
      {
          dc.ReadEncryptedFile (eoutput, doutput);
          System.out.println ("decryption finished to " + doutput.getName ());
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (IOException e)
      {
          e.printStackTrace();
      }
   }


}
Run Code Online (Sandbox Code Playgroud)

  • 这基本上和埃里克森的答案一样,周围都是一个 - 不是很好的编程在我看来 - 包装器.printStackTrace() (13认同)
  • @owlstead - 这是一个很好的答案.它展示了如何通过加密字节缓冲区来加密流,而不是将所有内容都放在内存中.Erickson的答案不适合大文件,不适合内存.所以+1到wufoo.:) (2认同)
  • @dynamokaj使用`CipherInputStream`和`CipherOutputStream`并不是什么大问题.在桌面下洗牌所有例外是一个问题.盐突然成为一个领域并且需要IV是一个问题.它不遵循Java编码约定的事实是一个问题.事实上,这只适用于没有被要求的文件是一个问题.并且其余的代码基本上都是副本也无济于事.但也许我会调整它以使其更好,正如所建议的那样...... (2认同)

One*_*ros 12

我在一个非常简单的类中实现了erickson的答案:
Java AES 256位加密/解密类

如果你得到了java.security.InvalidKeyException必须安装Java Cryptography Extension(JCE)无限强度管辖区域策略文件:

把罐子放在你的罐子里 {JDK HOME}\jre\lib\security

  • -1这应该是埃里克森答案下的评论.没有必要单独回答只是为了指出你已经实现了别人的代码. (8认同)
  • 您似乎在所有密钥实例之间共享相同的固定salt值.这可能不是一个好主意. (6认同)
  • 这不符合我的回答中的建议. (5认同)

waq*_*qas 7

从字节数组生成您自己的密钥很简单:

byte[] raw = ...; // 32 bytes in size for a 256 bit key
Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES");
Run Code Online (Sandbox Code Playgroud)

但是创建256位密钥是不够的.如果密钥生成器无法为您生成256位密钥,则Cipher该类可能也不支持AES 256位.你说你安装了无限制的域名补丁,所以应该支持AES-256密码(但是256位密钥应该也是如此,所以这可能是配置问题).

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
Run Code Online (Sandbox Code Playgroud)

缺乏AES-256支持的解决方法是采用一些免费的AES-256实现,并将其用作自定义提供程序.这涉及创建自己的Provider子类并使用它Cipher.getInstance(String, Provider).但这可能是一个涉及的过程.

  • 您应该始终指示模式和填充算法.Java默认使用不安全的ECB模式. (5认同)

Dar*_*uid 6

我过去所做的是通过SHA256之类的方法来键入密钥,然后将哈希中的字节提取到密钥字节[]中.

你有了字节[]后,你可以简单地做:

SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
Run Code Online (Sandbox Code Playgroud)

  • 对于其他人:这不是一个非常安全的方法.您应该使用PKCS#5中指定的PBKDF 2.埃里克森说如何做到这一点.DarkSquid的方法容易受到密码攻击,除非明文的大小是AES的块大小(128位)的倍数,否则它也不起作用,因为他省略了填充.它也没有指定模式; 阅读维基百科的分组密码操作模式以了解问题. (12认同)
  • 请勿在任何类型的生产环境中使用此方法.当开始使用基于密码的加密时,许多用户被代码墙所淹没,并且不理解字典攻击和其他简单的黑客攻击是如何工作的.尽管学习起来令人沮丧,但研究这项研究是值得的.这是一篇很好的初学者文章:http://adambard.com/blog/3-wrong-ways-to-store-a-password/ (2认同)