先解释一下什么是数字信封: 将对称密钥通过非对称加密(即:有公钥和私钥两个)的结果分发对称密钥的方法。大白话讲: 用对称秘钥对文件或字节加密,然后用非对称秘钥对对称秘钥的钥匙进行加密.
这里再解释一下对称秘钥和非对称秘钥:
- 非对称秘钥: 非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法
- 对称秘钥算法:加密是双方使用相同的密钥,必须以绝对安全的形式传送密钥才能保证安全。若果密钥泄露,加密数据将受到威胁,这点不如非对称密钥。只有一个秘钥
数字信封是实现信息保密性验证的技术。
- 文件加密流程:
- 文件解密流程:
- 我的实现思路: 对称算法选择国密的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