引入:

现在我们来研究密码学的加密解密过程,这个十分重要,因为它是明文到密文的桥梁,从类型上分,我们又分为加密解密字符串和加密解密文件,我们这文章就讲解这些细节,主要的核心类是Cipher类。


实践:

加密字符串的一般过程为:

(1)获取一个加密用的密钥

(2)实例化Cipher对象

(3)初始化Cipher对象,指定其现在处于加密模式(ENCRYPT_MODE),并指定加密使用的密钥

(4)调用doFinal方法,传入被加密的字符串对应的字节数组,返回加密后的字节数组


解密字符串的一般过程为:

(1)获取一个解密用的密钥

(2)实例化Cipher对象

(3)初始化Cipher对象,指定其现在处于解密模式(DECRYPT_MODE),并指定解密用的密钥

(4)调用doFinal方法,传入密文字节数组,返回解密后的字节数组


加密文件的一般过程为:

(1)获取一个加密用的密钥

(2)实例化Cipher对象

(3)初始化Cipher对象,指定其现在处于加密模式(ENCRYPT_MODE),并指定加密使用的密钥

(4)打开文件输入流,指向要被加密的文件

(5)打开文件输出流,指向加密后存放的文件

(6)打开加密输入流,包装指向被加密文件的输入流

(7)读取,并且加密,加密结果写入目标文件


解密文件的一般过程为:

(1)获取一个解密用的密钥

(2)实例化Cipher对象

(3)初始化Cipher对象,指定其现在处于解密模式(DECRYPT_MODE),并指定解密用的密钥

(4)打开文件输入流,指向要被解密的文件

(5)打开文件输出流,指向解密后存放的文件

(6)打开解密输出流,包装指向解密后文件的输出流

(7)读取,并且解密,解密结果写入目标文件


为了演示上面过程,我们写了一个工具类:

package com.charles.encryptstudy;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
/**
 *
 * Description: 这个类提供了加密解密的一般方法
 *
 * @author charles.wang
 * @created Oct 29, 2013 9:17:48 AM
 *
 */
public class EncryptUtil {
                                                                                                                                                               
    //加密解密字符串的操作方法
    /**
     * 用指定的密钥加密,吧字符串加密成字节数组
     *
     * @param data  被加密的字符串
     * @param key   加密使用的密钥
     * @return      加密后的字节流
     * @throws  InvalidKeyException
     * @throws  NoSuchPaddingException
     * @throws  NoSuchAlgorithmException
     * @throws  BadPaddingException
     * @throws  IllegalBlockSizeException
     *
     */
    public static byte[] encryptString(String data, Key key)
        throws InvalidKeyException,NoSuchPaddingException,NoSuchAlgorithmException,BadPaddingException,IllegalBlockSizeException {
        // 实例化Cipher对象用于加密操作
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        // 初始化Cipher对象,表明这个Cipher现在处于加密模式,并且指定加密使用的密钥
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 加密过程,返回加密后的内容
        return cipher.doFinal(data.getBytes());
    }
    /**
     * 用指定的密钥解密,还原字符串
     *
     * @param data  要被解密的字节流
     * @param key   解密使用的密钥
     * @return      解密后还原的字符串
     * @throws  InvalidKeyException
     * @throws  NoSuchPaddingException
     * @throws  NoSuchAlgorithmException
     * @throws  BadPaddingException
     * @throws  IllegalBlockSizeException
     */
    public static String decryptString(byte[] data, Key key) throws InvalidKeyException,NoSuchPaddingException,NoSuchAlgorithmException,BadPaddingException,IllegalBlockSizeException {
        // 实例化Cipher对象用于解密操作
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        // 初始化Cipher对象,表明这个Cipher现在处于解密模式,并且指定解密使用的密钥
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(data));
    }
                                                                                                                                                               
                                                                                                                                                               
                                                                                                                                                               
    //加密解密文件的操作方法
                                                                                                                                                               
    /**
     * 用指定的密钥加密文件(sourceFile),结果保存在destFile中
     *
     * @param sourceFile 要加密的文件
     * @param destFile   加密后的文件
     * @param key        加密使用的密钥
     * @throws Exception
     */
    public static void encryptFile(String sourceFile,String destFile,Key key) throws Exception{
        //实例化Cipher对象用于加密操作
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        //初始化Cipher对象,表明这个Cipher现在处于加密模式,并且指定加密使用的密钥
        cipher.init(Cipher.ENCRYPT_MODE, key);
        //打开文件输入流,指向要被加密的文件
        InputStream is = new FileInputStream(sourceFile);
        //打开文件输出流,指向加密后存放的文件
        OutputStream os = new FileOutputStream(destFile);
                                                                                                                                                                   
        //打开加密输入流,包装指向被加密文件的输入流
        CipherInputStream cis = new CipherInputStream(is,cipher);
                                                                                                                                                                   
        //缓冲区
        byte[] buffer = new byte[1024];
        int result;
                                                                                                                                                                   
        //读取,并且加密,加密结果写入目标文件
        while ((result = cis.read(buffer)) > 0) {
            os.write(buffer, 0, result);
        }
        cis.close();
        is.close();
        os.close();
                                                                                                                                                                 
                                                                                                                                                                   
    }
                                                                                                                                                               
                                                                                                                                                               
    /**
     * 用指定的密钥解密文件(encryptedFile),结果保存在originalFile中
     *
     * @param encryptedFile 要解密的文件
     * @param originalFile   解密后的文件
     * @param key        解密使用的密钥
     * @throws Exception
     */
    public static void decryptFile(String encryptedFile,String originalFile,Key key) throws Exception{
        //实例化Cipher对象用于解密操作
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        //初始化Cipher对象,表明这个Cipher现在处于解密模式,并且指定解密使用的密钥
        cipher.init(Cipher.DECRYPT_MODE, key);
        //打开文件输入流,指向要被解密的文件
        InputStream is = new FileInputStream(encryptedFile);
        //打开文件输出流,指向解密后存放的文件
        OutputStream os = new FileOutputStream(originalFile);
                                                                                                                                                                   
        //打开解密输出流,包装指向解密后文件的输出流
        CipherOutputStream cos = new CipherOutputStream(os,cipher);
                                                                                                                                                                   
        //缓冲区
        byte[] buffer = new byte[1024];
        int result;
                                                                                                                                                                   
        //读取,并且解密,解密结果写入目标文件
        while ((result = is.read(buffer)) > 0) {
            cos.write(buffer, 0, result);
        }
        cos.close();
        is.close();
        os.close();
                                                                                                                                                                 
                                                                                                                                                                   
    }
}



对于加密解密字符串,我们写了一个演示Demo类,来演示这2个操作结果,我们指定任意字符串,先用我们的加密方法对其加密,然后用我们的解密方法解密还原原始字符串.

package com.charles.encryptstudy;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import sun.misc.BASE64Encoder;
import com.charles.keystudy.SymmetricKeyUtil;
/**
 *
 * Description: 这个类用来演示如何加密和解密字符串
 *
 * @author charles.wang
 * @created Oct 28, 2013 4:37:41 PM
 *
 */
public class EncryptStringDemo {
    public static void main(String[] args) throws Exception {
                                                                                                                                        
        //我们演示加密解密字符串
        System.out.println("加密解密字符串:");
                                                                                                                                        
        String stringNeedEncrypt="加密这段文本";
        System.out.println("被加密的文本为:"+stringNeedEncrypt);
                                                                                                                                        
        //产生一个密钥
        KeyGenerator keyGen = KeyGenerator.getInstance("DES");
        Key key=keyGen.generateKey();
        System.out.println("\n加密使用的密钥为:"+(new BASE64Encoder()).encode(key.getEncoded()));
        System.out.println("加密使用的算法为:"+key.getAlgorithm());
                                                                                                                                        
        //加密字符串过程
        System.out.println("\n开始加密字符串...");
        byte[] encryptedValue = EncryptUtil.encryptString(stringNeedEncrypt, key);
        System.out.println("加密后的密文为:"+(new BASE64Encoder()).encode(encryptedValue));
                                                                                                                                        
        //解密字符串过程
        System.out.println("\n开始解密字符串...");
        String decryptedString = EncryptUtil.decryptString(encryptedValue, key);
        System.out.println("解密后还原的字符串为:"+decryptedString);
    }
}


运行这个类的main()方法,我们可以很清楚的看到,代码正确的被执行了:

105248505.png



对于加密解密文件,我们写了一个演示Demo类,来演示这2个操作结果,我们指定原始文件,先用我们的加密方法对其加密,产生密文文件,然后用我们的解密方法解密还原原始文件。

package com.charles.encryptstudy;
import java.io.File;
import java.io.FileInputStream;
import java.security.Key;
import javax.crypto.KeyGenerator;
import sun.misc.BASE64Encoder;
/**
 *
 * Description: 这个类是用来演示如何加密和解密文件的
 *
 * @author charles.wang
 * @created Oct 29, 2013 10:06:01 AM
 *
 */
public class EncryptFileDemo {
    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
                                                                                                     
        //我们演示加密解密文件
        System.out.println("加密解密文件:");
                                                                                                     
        //给定被加密的文件,你可以吧相应任意文件放在任意目录并且更改这里路径
        String filePathNeedEncrypt="travel.jpg";
                                                                                                     
        //获取被加密的文件信息
        File fileNeedEncrypt= new File(filePathNeedEncrypt);
        System.out.println("被加密的文件为:"+fileNeedEncrypt.getName());
        System.out.println("被加密文件的大小为:"+fileNeedEncrypt.length()+" bytes");
                                                                                                     
                                                                                                     
        //产生一个密钥
        KeyGenerator keyGen = KeyGenerator.getInstance("DES");
        Key key=keyGen.generateKey();
        System.out.println("\n加密使用的密钥为:"+(new BASE64Encoder()).encode(key.getEncoded()));
        System.out.println("加密使用的算法为:"+key.getAlgorithm());
                                                                                                     
        //加密文件过程
        System.out.println("\n开始加密文件...");
        String filePathEncrypted="travel-encrypt.encrypt";
        EncryptUtil.encryptFile(filePathNeedEncrypt,filePathEncrypted,key);
                                                                                                     
        //获取加密后的密文文件信息
        File fileEncrypted = new File(filePathEncrypted);
        System.out.println("加密后的密文文件为:"+fileEncrypted.getName());
        System.out.println("加密后的密文文件大小为:"+fileEncrypted.length()+" bytes");
                                                                                                      
                                                                                                     
        //解密文件过程
        System.out.println("\n开始解密文件...");
        String filePathRestored="travel-restored.jpg";
        EncryptUtil.decryptFile(filePathEncrypted,filePathRestored,key);
                                                                                                     
        //获取解密后的明文文件信息
        File fileRestored = new File(filePathRestored);
        System.out.println("解密后的明文文件为:"+fileRestored.getName());
        System.out.println("解密后的明文文件大小为:"+fileRestored.length()+" bytes");
                                                                                                     
    }
                                                                                                 
}


运行这个类的main()方法,我们可以很清楚的看到,代码正确的被执行了,尤其比较下文件大小:

105558330.png


如果不放心的话,我们去文件系统看:

对于原始文件travel.jpg:

105733106.png


对于加密后的密文文件travel-encrypt.encrypt:

105917500.png


再比较解密后还原的文件travel-restored.jpg:

110001512.png

可以发现它和原始文件是完全一样的。