Skip to content

Commit

Permalink
增加IV向量安全性
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnGao authored and JohnGao committed Sep 5, 2024
1 parent 6c9e208 commit b684252
Show file tree
Hide file tree
Showing 17 changed files with 357 additions and 230 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align=center><img src="https://github.com/gaoxianglong/encryption-dog/blob/master/resources/logo.png"/></div>

[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) ![License](https://img.shields.io/badge/build-passing-brightgreen.svg) ![License](https://img.shields.io/badge/version-2.0.1--RELEASE-blue)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) ![License](https://img.shields.io/badge/build-passing-brightgreen.svg) ![License](https://img.shields.io/badge/version-2.0.2--RELEASE-blue)
> Encryption program with high performance, high security and rich functionsm.<br/>
> Supports binding the same physical device for file encryption and decryption.<br/>
Expand All @@ -9,12 +9,12 @@
```shell
git clone git@github.com:gaoxianglong/encrypt-dog-new.git
mvn package
alias dog = 'java -Xms1g -Xmx1g -Xmn384m -jar dog-2.0.1.jar'
alias dog = 'java -Xms1g -Xmx1g -Xmn384m -jar dog-2.0.2.jar'
```
or
```shell
$ wget https://github.com/gaoxianglong/encrypt-dog-new/releases/download/v2.0.1/dog-2.0.1.jar
alias dog = 'java -Xms512m -Xmx512m -Xmn384m -jar dog-2.0.1.jar'
$ wget https://github.com/gaoxianglong/encrypt-dog-new/releases/download/v2.0.2/dog-2.0.2.jar
alias dog = 'java -Xms512m -Xmx512m -Xmn384m -jar dog-2.0.2.jar'
```
### use
```shell
Expand All @@ -25,7 +25,7 @@ Welcome to
/ _// _ \/ __/ __/ // / _ \/ __/ / _ \/ _ \/ // / _ \/ _ `/
/___/_//_/\__/_/ \_, / .__/\__/_/\___/_//_/____/\___/\_, /
/___/_/ /___/
version: 2.0.1-RELEASE
version: 2.0.2-RELEASE
Missing required options: '--secret-key', '--source-file=<source file>'
Usage: encrypt-dog [-dehoV] -k [-k]... [-a=<encryptAlgorithm>] [-t=<storage
Expand Down
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>encrypt-dog-new</artifactId>
<version>2.0.1-RELEASE</version>
<version>2.0.2-RELEASE</version>
<description>
2.0.2-RELEASE 增加IV向量安全性
2.0.1-RELEASE 性能优化和代码结构调整,同时扩展了AES和XOR加密算法
2.0.0-RELEASE 完全重写版本,支持并发性能模式
1.6.0-RELEASE 缺省文件加/解密后输出到原路径下,文件匹配正则扩展
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public class OperationVO implements Serializable {
* 源文件容量大小
*/
private long sourceFileCapacity;
/**
* 初始化向量
*/
private byte[] iv;

/**
* 设置目标文件的全限定名
Expand Down Expand Up @@ -170,4 +174,8 @@ public void setEncryptAlgorithm(EncryptTypeEnum encryptAlgorithm) {
public void setSourceFileCapacity(long sourceFileCapacity) {
this.sourceFileCapacity = sourceFileCapacity;
}

public void setIv(byte[] iv) {
this.iv = iv;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public abstract class AbstractDecrypt extends AbstractOperationTemplate {
* 加密类型长度1bytes
*/
private static final int ENCRYPT_LENGTH_BYTES = 1;
/**
* IV向量长度1bytes
*/
private static final int IV_LENGTH_BYTES = 1;
/**
* 文件唯一id长度为64bit
*/
Expand Down Expand Up @@ -102,6 +106,31 @@ public void checkEncryptType(EncryptContext encryptContext) throws ValidateExcep
}
}

/**
* 读取向量IV
* @param encryptContext
* @throws OperationException
*/
@Override
public void initVector(EncryptContext encryptContext) throws OperationException {
// 获取文件句柄
var in = encryptContext.getInputStream();
var ivLength = new byte[IV_LENGTH_BYTES];
try {
in.read(ivLength);

// 读取向量值的长度
var length = Utils._1byte2Int(ivLength);
var iv = new byte[length];
// 读取IV向量值
in.read(iv);
// 设置iv到领域模型中
encryptContext.getOperationVO().setIv(iv);
} catch (Throwable e) {
throw new DecryptException("IV reading failed.", e);
}
}

@Override
public void bind(EncryptContext encryptContext) throws EncryptException {
var in = encryptContext.getInputStream();
Expand Down Expand Up @@ -161,7 +190,7 @@ public void store(EncryptContext encryptContext) throws EncryptException {
content = temp;
}
// 获取解密后的数据
var decryptData = dataDecrypt(content, encryptContext.getOperationVO().getSecretKey());
var decryptData = dataDecrypt(content, encryptContext.getOperationVO().getSecretKey(), encryptContext.getOperationVO().getIv());
out.write(decryptData, 0, decryptData.length);
out.flush();

Expand Down Expand Up @@ -218,7 +247,7 @@ public void restoreKey(long fileId, OperationVO operationVO) throws OperationExc
return;
}
// 使用原秘钥解密对应的随机秘钥
var sk = dataDecrypt(rsk.getBytes(Charsets.UTF_8), operationVO.getSecretKey());
var sk = dataDecrypt(rsk.getBytes(Charsets.UTF_8), operationVO.getSecretKey(), operationVO.getIv());
operationVO.setSecretKey(new String(sk, Charsets.UTF_8).toCharArray());
} catch (Throwable e) {
throw new OperationException(e.getMessage(), e);
Expand All @@ -229,10 +258,11 @@ public void restoreKey(long fileId, OperationVO operationVO) throws OperationExc
* 数据解密操作
* @param content
* @param secretKey
* @param iv
* @return
* @throws DecryptException
*/
protected abstract byte[] dataDecrypt(byte[] content, char[] secretKey) throws DecryptException;
protected abstract byte[] dataDecrypt(byte[] content, char[] secretKey, byte[] iv) throws DecryptException;

/**
* 构建EstimatedTimeEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,37 @@ public void checkEncryptType(EncryptContext encryptContext) throws ValidateExcep
try {
// 写入u1/8bit的加密类型
out.write(Utils.int2Byte(encryptAlgorithm.getId()));
out.flush();
} catch (Throwable e) {
throw new EncryptAlgorithmException("Encrypt algorithm write failed", e);
}
}

/**
* 由具体的加密算法生成IV向量,这里在加密验证后追加u1/8bit向量长度和具体的向量值
* @param encryptContext
* @throws OperationException
*/
@Override
public void initVector(EncryptContext encryptContext) throws OperationException {
// 获取文件句柄
var out = encryptContext.getOutputStream();
try {
// 获取IV
var iv = encryptContext.getOperationVO().getIv();
// 获取IV长度
var ivLength = iv.length;

// 写入u1/8bit的IV向量长度
out.write(Utils.int2Byte(ivLength));
// 写入具体向量值
out.write(iv, 0, ivLength);
out.flush();
} catch (Throwable e) {
throw new EncryptException("IV write failed", e);
}
}

/**
* 是否仅限在相同的物理设备上完成加/解密操作
*
Expand All @@ -123,6 +149,7 @@ public void bind(EncryptContext encryptContext) throws EncryptException {
// 写入文件唯一id
out.write(Utils.long2Bytes(encryptContext.setFileId(idWorker.getId()).getFileId()));
} else {
// 未开启OnlyLocal时写入一个空字节
out.write(new byte[1]);
}
out.flush();
Expand Down Expand Up @@ -163,7 +190,7 @@ public void store(EncryptContext encryptContext) throws EncryptException {
content = temp;
}
// 获取加密后的数据
var encryptData = dataEncrypt(content, encryptContext.getOperationVO().getSecretKey());
var encryptData = dataEncrypt(content, encryptContext.getOperationVO().getSecretKey(), encryptContext.getOperationVO().getIv());
out.write(encryptData, 0, encryptData.length);
out.flush();

Expand Down Expand Up @@ -216,10 +243,11 @@ public int getDefaultCapacity(long capacity) {
* 数据加密操作
* @param content
* @param secretKey
* @param iv
* @return
* @throws EncryptException
*/
protected abstract byte[] dataEncrypt(byte[] content, char[] secretKey) throws EncryptException;
protected abstract byte[] dataEncrypt(byte[] content, char[] secretKey, byte[] iv) throws EncryptException;

/**
* 存储--only-local场景下的真实秘钥
Expand All @@ -240,7 +268,7 @@ private synchronized void saveSecretKey(EncryptContext encryptContext) throws Re
// 真实秘钥
var secretKey = new String(encryptContext.getOperationVO().getSecretKey());
// 使用源秘钥加密真实秘钥
secretKey = new String(dataEncrypt(secretKey.getBytes(Charsets.UTF_8), sourceSecretKey), Charsets.UTF_8);
secretKey = new String(dataEncrypt(secretKey.getBytes(Charsets.UTF_8), sourceSecretKey, encryptContext.getOperationVO().getIv()), Charsets.UTF_8);
// 固化加密后的真实秘钥
properties.put(key, secretKey);
properties.store(new BufferedOutputStream(new FileOutputStream(SECRET_KEY_FILE)), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ public void store(EncryptContext encryptContext) throws EncryptException {

}

@Override
public void initVector(EncryptContext encryptContext) throws OperationException {

}

@Override
public ChannelEnum getChannel() {
return null;
Expand Down Expand Up @@ -261,19 +266,20 @@ private void onlyLocal(EncryptContext encryptContext) throws EncryptException, I
}

/**
* 解析文件头信息
* 解析文件头
*
* File Header Structure:
* +------------------------+---------------------------+---------------------+------------------+---------------------+------------------+------------------------+--------------+
* | magic-number: u4/32bit | encryption-type: u1/8bit | iv-length: u1/8bit | iv: uN/variable | hid-length: u1/8bit | hardware-id: uX/36bit | file-id: u8/64bit | file data... |
* | Value: 0x19890225 | (e.g., encryption type) | (length of IV) | (IV value) | | (e.g., 36 bits) | | |
* +------------------------+---------------------------+---------------------+------------------+---------------------+------------------+------------------------+--------------+
*
* +----------------------------+---------------------------+---------------------+-------------------------+-------------------+----------------+
* | magic-number: u4/32bit | encryption-type: u1/8bit | hid-length: u1/8bit | hardware-id: uX/36bit | file-id: u8/64bit | file data... |
* | Value: 0x19890225 | (e.g., encryption type) | | (e.g., 36 bits) | | |
* +----------------------------+---------------------------+---------------------+-------------------------+-------------------+----------------+
*
* @param context
* throws ParseException
* @throws ValidateException
* @throws OperationException
* @throws IOException
*/
private void parseHeader(EncryptContext context) throws ValidateException, EncryptException, IOException {
private void parseHeader(EncryptContext context) throws ValidateException, OperationException, IOException {
if (Objects.isNull(context)) {
throw new ValidateException("The context info is null");
}
Expand All @@ -284,6 +290,9 @@ private void parseHeader(EncryptContext context) throws ValidateException, Encry
// 加密类型检测,如果是加密操作,则在魔术后写入u1/8bit加密类型
checkEncryptType(context);

// IV向量操作
initVector(context);

// 最高安全性的本地化处理,这里跟UUID有关
onlyLocal(context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ public interface OperationStrategy {
*/
void bind(EncryptContext encryptContext) throws EncryptException;

/**
* 初始化向量IV操作
* @param encryptContext
* @throws OperationException
*/
void initVector(EncryptContext encryptContext) throws OperationException;

/**
* 获取渠道
* @return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,74 +1,31 @@
package com.gxl.encryptdog.core.operation.impl.decrypt;

import com.gxl.encryptdog.base.enums.ChannelEnum;
import com.gxl.encryptdog.base.enums.EncryptTypeEnum;
import com.gxl.encryptdog.base.error.DecryptException;
import com.gxl.encryptdog.base.error.EncryptException;
import com.gxl.encryptdog.core.event.observer.ObServerContext;
import com.gxl.encryptdog.core.operation.AbstractDecrypt;
import com.gxl.encryptdog.utils.Utils;
import com.gxl.encryptdog.core.operation.type.Aes;

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

/**
* AES-256算法解密
* AES-256解密
*
* @author gxl
* @version Id: 1.0.0
* @since 2024/9/3 17:00
*/
public class AesDecrypt extends AbstractDecrypt {
/**
* 加密算法名称
*/
private static final String ALGORITHM_TYPE = EncryptTypeEnum.AES.getAlgorithmType();

/**
* 加密算法名称/分组加密/分组填充
* 即使在ECB模式下,AES-256的密钥长度确保了非常高的加密强度,暴力破解几乎是不可能的
* 使用向量IV需要切换到CBC、GCM模式
*/
private static final String CIPHER_ALGORITHM = String.format("%s/ECB/PKCS5Padding", ALGORITHM_TYPE);

public class AesDecrypt extends AbstractDecrypt implements Aes {
/**
* 解密时缺省每次读取 10485776 bytes
*/
public static final int DEFAULT_DECRYPT_CONTENT_SIZE = 0xa00010;

/**
* 基于PBKDF2算法使用密码派生函数
*/
private static final String KEY_DERIVATION = "PBKDF2WithHmacSHA256";
public static final int DEFAULT_DECRYPT_CONTENT_SIZE = 0xa00010;

public AesDecrypt(ObServerContext obServer) {
super(obServer);
}

/**
* 数据解密操作
* @param content
* @param secretKey
* @return
* @throws DecryptException
*/
@Override
protected byte[] dataDecrypt(byte[] content, char[] secretKey) throws DecryptException {
try {
// 获取秘钥器
var key = getGenerateKey(secretKey);
var cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);

// 执行数据解密
return cipher.doFinal(content);
} catch (Throwable e) {
throw new DecryptException(e);
}
}

/**
* 获取分段操作容量
*
Expand All @@ -90,25 +47,23 @@ public ChannelEnum getChannel() {
}

/**
* 返回秘钥器
* 数据解密操作
* @param content
* @param secretKey
* @param iv
* @return
* @throws DecryptException
*/
private SecretKeySpec getGenerateKey(char[] secretKey) throws DecryptException {
@Override
protected byte[] dataDecrypt(byte[] content, char[] secretKey, byte[] iv) throws DecryptException {
try {
// 生成随机盐值
var salt = Utils.chars2Bytes(secretKey);
// 迭代次数,越大越安全
var iterationCount = 65536;
// 秘钥长度
var keyLength = 256;
// 获取秘钥器
var key = getGenerateKey(secretKey);
var cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));

// 基于PBKDF2算法使用密码派生函数
var factory = SecretKeyFactory.getInstance(KEY_DERIVATION);
var spec = new PBEKeySpec(secretKey, salt, iterationCount, keyLength);
// 返回秘钥key
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), ALGORITHM_TYPE);
// 执行数据解密
return cipher.doFinal(content);
} catch (Throwable e) {
throw new DecryptException(e);
}
Expand Down
Loading

0 comments on commit b684252

Please sign in to comment.