解密时最初的几个字符被截断

Initial few characters are getting truncated while decrypting

我是 Java 的新手,正在尝试为文本文件实现 Java encryption/decryption。在这里,我可以看到在解密加密文件时,第 1 行中的一些起始字符被截断了。

下面是我的代码。如果我遗漏了什么,请提出建议。

package com.myprotection;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypto
{
    private String mPassword = null;
    byte [] mInitVec = null;
    byte [] mSalt = null;
    Cipher mEcipher = null;
    Cipher mDecipher = null;
    private final int KEYLEN_BITS = 256;
    private final int ITERATIONS = 65536;
    private final int MAX_FILE_BUF = 1024;


    public Crypto (String password, String hint) throws Exception
    {
        mPassword = password;
        mSalt = getSaltedByteFromString( hint );
    }


    public void WriteEncryptedFile (String input, String output) throws Exception {

        FileInputStream fin = new FileInputStream (new File(input));
        FileOutputStream fout = new FileOutputStream (new File(output));
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        //Initialize Encryption Vector
        initializeInitVec();

        while ((nread = fin.read (inbuf)) > 0 )
        {

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


    public void ReadEncryptedFile (String input, String output) throws Exception {

        FileInputStream fin = new FileInputStream (new File(input));
        FileOutputStream fout = new FileOutputStream (new File(output));
        CipherInputStream cin;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        //Initializing decrypting 
        setupDecrypt();

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

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

    private void setupDecrypt () throws Exception {

        SecretKeyFactory factory = null;
        SecretKey tmp = null;
        SecretKey secret = null;

        //Get initialized the vector
        initializeInitVec();

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

    private void initializeInitVec() throws Exception {

        SecretKeyFactory factory = null;
        SecretKey tmp = null;

        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        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();
    }

    private byte[] getSaltedByteFromString( String hintString ) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(hintString.getBytes("UTF-8"));
        return md.digest();
    }
}

以上代码的测试:

public class TestCrypto {
    public static void main(String[] args) throws Exception {

        String pwd = "Tipu";
        String hint = "My Dog Name";

        Crypto cpo = new Crypto(pwd, hint);
        cpo.WriteEncryptedFile("C:\Users\roul\Desktop\TestEncryption.txt", "C:\Users\roul\Desktop\en-TestEncryption.txt");
        cpo.ReadEncryptedFile("C:\Users\roul\Desktop\en-TestEncryption.txt", "C:\Users\roul\Desktop\de-TestEncryption.txt");


    }
}

测试用例文件内容TestEncryption.txt:

My Encryption testing
This is my test encryption.

加密TestEncryption.txt:

fè"Ç2¨1.ñ|#¼Ê¯—Ã&ýnfK[ùn§Ù @ÛPzΕ¯sö®õ˜ýóK8Äܨ`‹¹*¢ÙF›Ã

解密TestEncryption.txt:

³‰XgÜOÂN‹¶ƒ×­N›sting
This is my test encryption.

这里可能有更多的错误,但其中一个肯定是您对 IV 的处理。

每次你调用initializeInitVec(),它都会在mInitVec中存储一个随机 IV。因此,您不能将该方法用作检索先前加密操作中使用的 IV 的方法。

不幸的是,您当前的代码正是这样做的。结果,您尝试使用错误的 IV 进行解密,并且可能会得到一个填充异常,该异常被输入流吞没了。

要解决此问题,您必须将 IV 与加密数据一起存储。一种常见的方法是将您的 IV 写入您的文件,然后是您的加密内容。解密需要先阅读本IV,再解密以下内容