先解释一下什么是数字信封:  将对称密钥通过非对称加密(即:有公钥私钥两个)的结果分发对称密钥的方法。大白话讲: 用对称秘钥对文件或字节加密,然后用非对称秘钥对对称秘钥的钥匙进行加密.

这里再解释一下对称秘钥和非对称秘钥:

  • 非对称秘钥: 非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法
  • 对称秘钥算法:加密是双方使用相同的密钥,必须以绝对安全的形式传送密钥才能保证安全。若果密钥泄露,加密数据将受到威胁,这点不如非对称密钥。只有一个秘钥

数字信封是实现信息保密性验证的技术。

  • 文件加密流程:

java 国密数字信封 提取出私钥 保存成文件_https

  • 文件解密流程:

java 国密数字信封 提取出私钥 保存成文件_java_02

  • 我的实现思路:  对称算法选择国密的sm4算法, 采用SM4/ECB/PKCS5Padding, 非对称算法采用国密sm2 ,

具体实现代码:

  • 数字信封加密

数字信封加密



package com.ymwk;
 
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
 
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
 
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
import com.ymwk.base64.Base64Utils;
import com.ymwk.file.FileUtils;
import com.ymwk.result.Result;
import com.ymwk.sm2.GMBaseUtil;
import com.ymwk.sm2.SM2CertUtil;
import com.ymwk.sm2.SM2Util;
import com.ymwk.sm4.Sm4Utils;
 
/**
 * 对文件的数字信封
 * @author Saxon 
 * @Date   2021年7月8日
 */
public class FileEncrypt extends GMBaseUtil{
	
	
	
	/**
	 * @param publicKey 公钥对象
	 * @param fileBytes  文件字节数组
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 * @throws IOException 
	 * @throws NoSuchProviderException 
	 * @throws NoSuchAlgorithmException 
	 */
	public static Result fileEncrypt(PublicKey publicKey,byte[] fileBytes)  {
		try {
			//创建对称秘钥对
			byte[] key = Sm4Utils.generateKey();
			//对文件加密
			byte[] fileEncryptBytes = Sm4Utils.encryptEcbPadding(key, fileBytes);
			//对秘钥对加密
			byte[] encryptKey = SM2Util.encrypt((BCECPublicKey)publicKey, key);
			//将返回值base64加密
			String encryptKeyStr = Base64Utils.encode(encryptKey);
			return new Result(Result.CODE_SUCCESS,"文件加密成功",encryptKeyStr,fileEncryptBytes);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return new Result(Result.CODE_EXCEPTION, "NoSuchAlgorithmException,没有找到该摘要算法");
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
			return new Result(Result.CODE_EXCEPTION, "NoSuchProviderException,没有找到该加密机");
		}catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | InvalidCipherTextException e ) {
			e.printStackTrace();
			return new Result(Result.CODE_EXCEPTION, "文件加密失败:"+e.getMessage());
		}
	}
	
	/**
	 * 
	 * @param publicKey 公钥对象
	 * @param fileInput  文件输入流
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 * @throws IOException 
	 */
	public static Result fileEncrypt(PublicKey publicKey,InputStream fileInput)   {
		 ByteArrayOutputStream output = new ByteArrayOutputStream();
	     byte[] buffer = new byte[1024*4];
	     int n = 0;
	     try {
			while (-1 != (n = fileInput.read(buffer))) {
			    output.write(buffer, 0, n);
			 }
		} catch (IOException e) {
			e.printStackTrace();
		}
		return fileEncrypt(publicKey,output.toByteArray());
	}
	
	/**
	 * @param cert  证书对象
	 * @param file  文件
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 */
	public static Result fileEncrypt(X509Certificate cert,File file)   {
		try {
			return fileEncrypt(cert,new FileInputStream(file));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return new Result(Result.CODE_ERROR_PARAM, "文件不存在:"+e.getMessage());
		}
	}
	
	/**
	 * 
	 * @param cert  证书对象
	 * @param fileInput  文件输入流
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 * @throws IOException 
	 */
	public static Result fileEncrypt(X509Certificate cert,InputStream fileInput)  {
		return fileEncrypt(SM2CertUtil.getBCECPublicKey(cert),fileInput);
	}
	
	/**
	 * @param certStr  证书base64
	 * @param fileInput  文件输入流
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 * @throws IOException 
	 */
	public static Result fileEncrypt(String certStr,InputStream fileInput) {
		try {
		   ByteArrayInputStream bais = new ByteArrayInputStream(Base64Utils.decode(certStr));
		   CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
		   X509Certificate cert =  (X509Certificate) cf.generateCertificate(bais);
		   return fileEncrypt(cert,fileInput);
		}catch (Exception e) {
			e.printStackTrace();
			return new Result(Result.CODE_EXCEPTION, "证书base64转换证书对象失败:"+e.getMessage());
		}
	}
	/**
	 * @param certStr  证书base64
	 * @param filePath  文件路径
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 */
	public static Result fileEncrypt(String certStr,String filePath){
			return fileEncrypt(certStr,new File(filePath));
	}
	
	/**
	 * @param certStr  证书base64
	 * @param file  文件
	 * @return Result if(code== 200)success else fail   
	 * encryptFileStr 加密后的文件字符串
	 * encryptKeyStr  加密后的秘钥字符串
	 * @throws FileNotFoundException 
	 * @throws IOException 
	 */
	public static Result fileEncrypt(String certStr,File file)  {
		try {
			return fileEncrypt(certStr,new FileInputStream(file));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return new Result(Result.CODE_ERROR_PARAM, "文件不存在:"+e.getMessage());
		}
	}
	
	public static void main(String[] args) throws CertificateException, NoSuchProviderException, IOException, NoSuchAlgorithmException, InvalidKeySpecException {
		 Security.addProvider(new BouncyCastleProvider());
		// FileInputStream bais = new FileInputStream("D:\\test.cer");
		 //CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
		// X509Certificate cert =  (X509Certificate) cf.generateCertificate(bais);
		// String cert = "MIICzzCCAnSgAwIBAgIJAdInmr8fIJiNMAoGCCqBHM9VAYN1MIGPMQswCQYDVQQGEwJDTjEzMDEGA1UECgwq5rmW5Y2X5b6h56CB572R5o6n5L+h5oGv5oqA5pyv5pyJ6ZmQ5YWs5Y+4MQ4wDAYDVQQLDAVIVU5ZTTETMBEGA1UEAwwKSFVOQU5ZTVNNMjESMBAGA1UEBwwJ6ZW/5rKZ5biCMRIwEAYDVQQIDAnmuZbljZfnnIEwHhcNMjEwNzEyMDczMjMxWhcNNDEwNzA3MDczMjMxWjCBqDELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCea5luWNl+ecgTESMBAGA1UEBwwJ5bi45b635biCMTYwNAYDVQQKDC3muZbljZfnnIHmlbDlrZforqTor4HmnI3liqHkuK3lv4PmnInpmZDlhazlj7gxITAfBgNVBAsMGOW4uOW+t+W4gua1i+ivleS4k+eUqOeroDEWMBQGA1UEAwwNNDMwMzAxMDAwMzAwNDBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABOK7wKAe409JYZkd8tx6Ey6cb0OxeHZc1AG1TKrTiuM7ErQPFwPTRjkCrmnk19+xu4vZKuPR+KmYSOshoAIlm5+jgZ0wgZowHQYDVR0OBBYEFJbAkoS2vkJuuXZT+Gj0XMtEjBHwMB8GA1UdIwQYMBaAFJS9gxFrCotSpHOaUiZRiKVK6SPiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgO4MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAYBgNVHREEETAPgg00MzAzMDEwMDAzMDA0MAoGCCqBHM9VAYN1A0kAMEYCIQCNBSkNdrrO0ZqZC6zwpDo7ZpWwr8PqkJdge8IEBgVjbQIhAIZHM5FZY+0Bb3Kui9ZaJr5eAZb83jGXpOGSpMCQUIr6";
		 String publicKeyStr = "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/v8AAAAA//8wRAQg/v8AAAAA//wEICjp+p6dn140TVqeS89lCafzl4n1FauPkt28vUFNlA6TBEEEMsSuLB8ZgRlfmQRGajnJlI/jC7/yZgvhcVpFiTNMdMe8Nzai9PZ3nFm9zuNraSFT0KmHfMYqR0AC3zLlITnwoAIhAP7///9yA99rIcYFK1O79Ak51UEjAgEBA0IABMWwe7cKGGDCHWVCCmI195Ump1xqhZcXn7oYa23gNPUYs8vAxmC/nf7t+8Kq7Iq2KR5zlafOR5+Igs/BX4AkUkQ=";
		 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64Utils.decode(publicKeyStr));
		 KeyFactory keyFactory = KeyFactory.getInstance("EC");
	     PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		 Result fileEncrypt = fileEncrypt(publicKey,new FileInputStream("C:\\Users\\Saxon\\Desktop\\Test.java"));
		 BufferedOutputStream bos = null;
	     FileOutputStream fos = null;
	     File file = null;
		  file = new File("d://test.java");
          if (!file.getParentFile().exists()){
              //文件夹不存在 生成
              file.getParentFile().mkdirs();
          }
          fos = new FileOutputStream(file);
          bos = new BufferedOutputStream(fos);
          bos.write(fileEncrypt.getEncryptFileByte());
          bos.close();
          fos.close();
		 System.out.println(fileEncrypt);
	}
}



  •  数字信封解密

数字信封解密



/**
	 * 
	 * @param privateKey   私钥
	 * @param file 加密后的文件
	 * @param keyString   加密后的秘钥
	 */
	public static void decrypt(PrivateKey privateKey,File file,String keyString) throws IOException, InvalidCipherTextException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
		byte[] keyByte = Base64Utils.decode(keyString);
		FileInputStream fileInputStream = new FileInputStream(file);
		ByteArrayOutputStream output = new ByteArrayOutputStream();
	    byte[] buffer = new byte[4096];
	    int n = 0;
	    while (-1 != (n = fileInputStream.read(buffer))) {
	        output.write(buffer, 0, n);
	    }
	     byte[] byteArray = output.toByteArray();
		//用私钥解密秘钥
		byte[] key = SM2Util.decrypt((BCECPrivateKey)privateKey, keyByte);
		byte[] decryptEcbPadding = Sm4Utils.decryptEcbPadding(key, byteArray);
                //将解密后的文件输出到本地
		OutputStream out = new FileOutputStream("D:\\bbb.txt");
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decryptEcbPadding);
		byte[] buff = new byte[1024];
	        int len = 0;
	        while((len=byteArrayInputStream.read(buff))!=-1){
	            out.write(buff, 0, len);
	        }
	        byteArrayInputStream.close();
	        out.close();
	}



以上就是数字信封的加解密操作的全部内容, 想要代码中其他工具类的朋友可以加我微信或者访问我的gitee,所有的代码都在里面.

gitee地址: https://gitee.com/saxonkiku/encrypt_and_decrypt_file.git