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

时间:2015-03-10 06:32:33

标签: java encryption

我是Java的新手,并试图为文本文件实现Java加密/解密。在这里,我可以看到第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

  

我的加密测试
  这是我的测试加密。

加密 TestEncryption.txt

  

fè“Ç2¨1.ñ|#¼Ê¯-Ã&amp;ýnfK[ùn§Ù@ÛPzΕ¯sö®õ〜óK8Äܨ`<¹*¢ÙF>Ã

解密 TestEncryption.txt

  

³‰XgÜOÂN<¶ƒ×N>刺
  这是我的测试加密。

1 个答案:

答案 0 :(得分:1)

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

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

不幸的是,您当前的代码确实如此。因此,您尝试使用错误的IV进行解密,并且可能会获得填充异常,该异常会被输入流吞噬。

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