基于加密

我需要实现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();

代码取自这里

编辑

我实际上是将密码填充到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)

你需要做的“TODO”位:-)


共享password (一个char[] )和salt (一个byte[] -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");

幻数(可以定义为常量)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"));

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

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


带有“非法密钥大小或默认参数”消息的java.security.InvalidKeyException意味着密码强度受限; 无限强度管辖权政策文件不在正确的位置。 在JDK中,它们应该放置在${jdk}/jre/lib/security

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


考虑使用Spring Security加密模块

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 7和Java 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");
        }       
    }
}

和样本输出,

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

在阅读了erickson的建议之后,我从其他一些帖子和这里的例子中收集了一些可能的信息,我试图用推荐的更改来更新Doug的代码。 随意编辑,使其更好。

  • 初始化向量不再是固定的
  • 加密密钥是使用来自erickson的代码派生的
  • 在setupEncrypt()中使用SecureRandom()生成8字节的salt
  • 解密密钥由加密盐和密码生成
  • 解密密码由解密密钥和初始化向量生成
  • 删除十六进制代替org.apache.commons编解码器十六进制例程
  • 一些注意事项:这使用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();
            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 + "nn");
          }
          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();
          }
       }
    
    
    }
    
    链接地址: http://www.djcxy.com/p/49149.html

    上一篇: Based Encryption

    下一篇: How is Node.js inherently faster when it still relies on Threads internally?