diff --git a/README.md b/README.md
index cbda796..49f7b58 100644
--- a/README.md
+++ b/README.md
@@ -13,10 +13,13 @@
- - [x] AES
- - [x] DES
- - [x] RSA
+ - - [x] CUSTOM
+
- 可进行解密的方式有:
- - [x] AES
- - [x] DES
- - [x] RSA
+ - - [x] CUSTOM
## 引入注册
### 导入依赖
在项目的`pom.xml`中引入依赖:
@@ -116,6 +119,41 @@ public class User implements Serializable {
}
```
+
+## 使用自定义加密/解密
+````java
+@Data
+@EncryptBody
+@FieldBody
+public class User implements Serializable {
+
+ @CustomDecryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", decryptMethodName = "myDecryptionFunction")
+ private String name;
+
+ @CustomEncryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", encryptMethodName = "myEncryptionFunction")
+ private String numberValue;
+
+
+
+}
+````
+
+使用 myEncryptionFunction 实现 MyCryptoProvider 类,如下所示。
+```java
+package com.myorg.mypkg;
+
+public class MyCryptoProvider {
+ String myEncryptionFunction(String input) {
+ // 用于加密输入并返回预期加密数据的代码
+ }
+
+ String myDecryptionFunction(String input) {
+ // 解密输入并返回预期解密数据的代码
+ }
+
+}
+```
+
## 注解一览表
- [编码/加密注解一览表](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/加密注解一览表)
- [解密注解一览表](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/解密注解一览表)
diff --git a/README_EN.md b/README_EN.md
index 73620d4..fbe4a1e 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -13,10 +13,12 @@
- - [x] AES
- - [x] DES
- - [x] RSA
+ - - [x] CUSTOM
- The methods that can be decrypted are:
- - [x] AES
- - [x] DES
- - [x] RSA
+ - - [x] CUSTOM
## Import registration
### Import dependencies
Introduce dependencies in the project's `pom.xml`:
@@ -116,6 +118,43 @@ public class User implements Serializable {
}
````
+
+## Using CUSTOM encryption/decryption
+````java
+@Data
+@EncryptBody
+@FieldBody
+public class User implements Serializable {
+
+ @CustomDecryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", decryptMethodName = "myDecryptionFunction")
+ private String name;
+
+ @CustomEncryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", encryptMethodName = "myEncryptionFunction")
+ private String numberValue;
+
+
+
+}
+````
+
+Implement the `MyCryptoProvider` class with `myEncryptionFunction` as shown below.
+```java
+package com.myorg.mypkg;
+
+public class MyCryptoProvider {
+ String myEncryptionFunction(String input) {
+ // code to encrypt input and return the expected encrypted data
+ }
+
+ String myDecryptionFunction(String input) {
+ // code to decrypt input and return the expected decrypted data
+ }
+
+}
+```
+
+
+
## Annotation list
- [Encryption/Encryption Annotation List](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/加密注解一览表)
- [Decryption Annotation List](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/解密注解一览表)
diff --git a/pom.xml b/pom.xml
index f44eaed..e6de8f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -180,6 +180,32 @@
+
+
+ maven-shade-plugin
+ 3.2.4
+
+
+ package
+
+ shade
+
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
diff --git a/src/main/java/cn/licoy/encryptbody/advice/DecryptRequestBodyAdvice.java b/src/main/java/cn/licoy/encryptbody/advice/DecryptRequestBodyAdvice.java
index 6c0e2fd..09a45e5 100644
--- a/src/main/java/cn/licoy/encryptbody/advice/DecryptRequestBodyAdvice.java
+++ b/src/main/java/cn/licoy/encryptbody/advice/DecryptRequestBodyAdvice.java
@@ -7,6 +7,7 @@
import cn.hutool.crypto.asymmetric.RSA;
import cn.licoy.encryptbody.annotation.FieldBody;
import cn.licoy.encryptbody.annotation.decrypt.AESDecryptBody;
+import cn.licoy.encryptbody.annotation.decrypt.CustomDecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.DESDecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.DecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.RSADecryptBody;
@@ -203,6 +204,14 @@ private DecryptAnnotationInfoBean getDecryptAnnotation(AnnotatedElement annotate
return DecryptAnnotationInfoBean.builder().decryptBodyMethod(DecryptBodyMethod.RSA).key(decryptBody.key()).rsaKeyType(decryptBody.type()).build();
}
}
+
+ if (annotatedElement.isAnnotationPresent(CustomDecryptBody.class)) {
+ CustomDecryptBody decryptBody = annotatedElement.getAnnotation(CustomDecryptBody.class);
+ if (decryptBody != null) {
+ return DecryptAnnotationInfoBean.builder().decryptBodyMethod(DecryptBodyMethod.CUSTOM).providerClassName(decryptBody.providerClassName()).decryptMethodName(decryptBody.decryptMethodName()).build();
+ }
+ }
+
return null;
}
@@ -232,6 +241,15 @@ private String switchDecrypt(String formatStringBody, DecryptAnnotationInfoBean
RSA rsa = CommonUtils.infoBeanToRsaInstance(infoBean);
return rsa.decryptStr(formatStringBody, infoBean.getRsaKeyType().toolType);
}
+ if (method == DecryptBodyMethod.CUSTOM) {
+ try {
+ Class> clazz = Class.forName(infoBean.getProviderClassName());
+ Method m = clazz.getMethod(infoBean.getDecryptMethodName(), String.class);
+ return m.invoke(null, formatStringBody).toString();
+ } catch( Exception e) {
+ return "failed to encrypt: " + formatStringBody;
+ }
+ }
throw new DecryptBodyFailException();
}
}
diff --git a/src/main/java/cn/licoy/encryptbody/advice/EncryptResponseBodyAdvice.java b/src/main/java/cn/licoy/encryptbody/advice/EncryptResponseBodyAdvice.java
index 006ef33..b724347 100644
--- a/src/main/java/cn/licoy/encryptbody/advice/EncryptResponseBodyAdvice.java
+++ b/src/main/java/cn/licoy/encryptbody/advice/EncryptResponseBodyAdvice.java
@@ -31,6 +31,7 @@
import java.lang.reflect.Method;
+
/**
* 响应数据的加密处理
* 本类只对控制器参数中含有{@link org.springframework.web.bind.annotation.ResponseBody}
@@ -75,45 +76,55 @@ private boolean hasEncryptAnnotation(AnnotatedElement annotatedElement) {
if (annotatedElement == null) {
return false;
}
- return annotatedElement.isAnnotationPresent(EncryptBody.class) || annotatedElement.isAnnotationPresent(AESEncryptBody.class) || annotatedElement.isAnnotationPresent(DESEncryptBody.class) || annotatedElement.isAnnotationPresent(RSAEncryptBody.class) || annotatedElement.isAnnotationPresent(MD5EncryptBody.class) || annotatedElement.isAnnotationPresent(SHAEncryptBody.class);
+ return annotatedElement.isAnnotationPresent(EncryptBody.class)
+ || annotatedElement.isAnnotationPresent(AESEncryptBody.class)
+ || annotatedElement.isAnnotationPresent(DESEncryptBody.class)
+ || annotatedElement.isAnnotationPresent(RSAEncryptBody.class)
+ || annotatedElement.isAnnotationPresent(MD5EncryptBody.class)
+ || annotatedElement.isAnnotationPresent(SHAEncryptBody.class)
+ || annotatedElement.isAnnotationPresent(CustomEncryptBody.class);
}
+
+
@Override
- public String beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
+ public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body == null) {
return null;
}
String str = CommonUtils.convertToStringOrJson(body, objectMapper);
- response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
+
Method method = returnType.getMethod();
if (method != null) {
- // 从方法上
+
+ //check if only some fields is response should be encrypted or the whole response should be encrypted.
+ //if only field encryption is required, we should not change the content-type and keep the returned object intact.
+ Class> methodReturnType = method.getReturnType();
+ if (methodReturnType.isAnnotationPresent(FieldBody.class)) {
+ return this.eachClassField(body, method.getReturnType());
+ }
+
EncryptAnnotationInfoBean methodAnnotation = this.getEncryptAnnotation(method);
if (methodAnnotation != null) {
+ response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return switchEncrypt(str, methodAnnotation);
}
- // 从方法返回值上
- Class> methodReturnType = method.getReturnType();
- if (methodReturnType.isAnnotationPresent(FieldBody.class)) {
- Object encryptResult = this.eachClassField(body, method.getReturnType());
- try {
- return objectMapper.writeValueAsString(encryptResult);
- } catch (JsonProcessingException e) {
- throw new EncryptBodyFailException(e.getMessage());
- }
- } else {
- EncryptAnnotationInfoBean returnTypeClassAnnotation = this.getEncryptAnnotation(methodReturnType);
- if (returnTypeClassAnnotation != null) {
- return switchEncrypt(str, returnTypeClassAnnotation);
- }
+
+ EncryptAnnotationInfoBean returnTypeClassAnnotation = this.getEncryptAnnotation(methodReturnType);
+ if (returnTypeClassAnnotation != null) {
+ response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
+ return switchEncrypt(str, returnTypeClassAnnotation);
}
}
// 从声明类上
EncryptAnnotationInfoBean classAnnotation = this.getEncryptAnnotation(returnType.getDeclaringClass());
if (classAnnotation != null) {
+ response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return switchEncrypt(str, classAnnotation);
}
+
+
throw new EncryptBodyFailException();
}
@@ -197,6 +208,12 @@ private EncryptAnnotationInfoBean getEncryptAnnotation(AnnotatedElement annotate
return EncryptAnnotationInfoBean.builder().encryptBodyMethod(EncryptBodyMethod.RSA).key(encryptBody.key()).rsaKeyType(encryptBody.type()).build();
}
}
+ if (annotatedElement.isAnnotationPresent(CustomEncryptBody.class)) {
+ CustomEncryptBody encryptBody = annotatedElement.getAnnotation(CustomEncryptBody.class);
+ if (encryptBody != null) {
+ return EncryptAnnotationInfoBean.builder().encryptBodyMethod(EncryptBodyMethod.CUSTOM).providerClassName(encryptBody.providerClassName()).encryptMethodName(encryptBody.encryptMethodName()).build();
+ }
+ }
return null;
}
@@ -236,6 +253,16 @@ private String switchEncrypt(String formatStringBody, EncryptAnnotationInfoBean
RSA rsa = CommonUtils.infoBeanToRsaInstance(infoBean);
return rsa.encryptHex(formatStringBody, infoBean.getRsaKeyType().toolType);
}
+ if (method == EncryptBodyMethod.CUSTOM) {
+ try {
+ Class> clazz = Class.forName(infoBean.getProviderClassName());
+ Method m = clazz.getMethod(infoBean.getEncryptMethodName(), String.class);
+ return m.invoke(null, formatStringBody).toString();
+ } catch(Exception e) {
+ return "failed to encrypt: " + formatStringBody;
+ }
+
+ }
throw new EncryptBodyFailException();
}
diff --git a/src/main/java/cn/licoy/encryptbody/annotation/decrypt/CustomDecryptBody.java b/src/main/java/cn/licoy/encryptbody/annotation/decrypt/CustomDecryptBody.java
new file mode 100644
index 0000000..8375e95
--- /dev/null
+++ b/src/main/java/cn/licoy/encryptbody/annotation/decrypt/CustomDecryptBody.java
@@ -0,0 +1,33 @@
+package cn.licoy.encryptbody.annotation.decrypt;
+
+import java.lang.annotation.*;
+
+@Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+
+public @interface CustomDecryptBody {
+ /**
+ * Custom encryption/decryption provider class name.
+ *
+ * @return provider class name
+ */
+ public String providerClassName() default "";
+
+ /**
+ * Name of the static method in provider class to be used for encryption.
+ * NOTE: encryption method should have a signature like `public String encrypt(String)`.
+ *
+ * @return
+ */
+ public String encryptMethodName() default "encrypt";
+
+ /**
+ * Name of the static method in provider class to be used for decryption.
+ * NOTE: decryption method should have a signature like `public String decrypt(String)`.
+ *
+ * @return
+ */
+ public String decryptMethodName() default "decrypt";
+
+}
diff --git a/src/main/java/cn/licoy/encryptbody/annotation/encrypt/CustomEncryptBody.java b/src/main/java/cn/licoy/encryptbody/annotation/encrypt/CustomEncryptBody.java
new file mode 100644
index 0000000..8dc55c1
--- /dev/null
+++ b/src/main/java/cn/licoy/encryptbody/annotation/encrypt/CustomEncryptBody.java
@@ -0,0 +1,36 @@
+package cn.licoy.encryptbody.annotation.encrypt;
+
+
+
+
+import java.lang.annotation.*;
+
+@Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface CustomEncryptBody {
+
+ /**
+ * Custom encryption/decryption provider class name.
+ *
+ * @return provider class name
+ */
+ public String providerClassName() default "";
+
+ /**
+ * Name of the static method in provider class to be used for encryption.
+ * NOTE: encryption method should have a signature like `public String encrypt(String)`.
+ *
+ * @return
+ */
+ public String encryptMethodName() default "encrypt";
+
+ /**
+ * Name of the static method in provider class to be used for decryption.
+ * NOTE: decryption method should have a signature like `public String decrypt(String)`.
+ *
+ * @return
+ */
+ public String decryptMethodName() default "decrypt";
+
+}
diff --git a/src/main/java/cn/licoy/encryptbody/bean/DecryptAnnotationInfoBean.java b/src/main/java/cn/licoy/encryptbody/bean/DecryptAnnotationInfoBean.java
index 5330ee9..46708b2 100644
--- a/src/main/java/cn/licoy/encryptbody/bean/DecryptAnnotationInfoBean.java
+++ b/src/main/java/cn/licoy/encryptbody/bean/DecryptAnnotationInfoBean.java
@@ -21,4 +21,10 @@ public class DecryptAnnotationInfoBean implements ISecurityInfo {
private RSAKeyType rsaKeyType;
+ private String providerClassName;
+
+ private String encryptMethodName;
+
+ private String decryptMethodName;
+
}
diff --git a/src/main/java/cn/licoy/encryptbody/bean/EncryptAnnotationInfoBean.java b/src/main/java/cn/licoy/encryptbody/bean/EncryptAnnotationInfoBean.java
index 587a2bc..fabf175 100644
--- a/src/main/java/cn/licoy/encryptbody/bean/EncryptAnnotationInfoBean.java
+++ b/src/main/java/cn/licoy/encryptbody/bean/EncryptAnnotationInfoBean.java
@@ -25,4 +25,11 @@ public class EncryptAnnotationInfoBean implements ISecurityInfo {
private RSAKeyType rsaKeyType;
+ private String providerClassName;
+
+ private String encryptMethodName;
+
+ private String decryptMethodName;
+
+
}
diff --git a/src/main/java/cn/licoy/encryptbody/bean/ISecurityInfo.java b/src/main/java/cn/licoy/encryptbody/bean/ISecurityInfo.java
index 74139cb..766e2e7 100644
--- a/src/main/java/cn/licoy/encryptbody/bean/ISecurityInfo.java
+++ b/src/main/java/cn/licoy/encryptbody/bean/ISecurityInfo.java
@@ -2,6 +2,7 @@
import cn.licoy.encryptbody.enums.RSAKeyType;
+
import java.io.Serializable;
/**
@@ -26,4 +27,26 @@ public interface ISecurityInfo extends Serializable {
*/
RSAKeyType getRsaKeyType();
+
+ /**
+ * Get the class name of custom encryption/decryption provider
+ *
+ * @return class name
+ */
+ String getProviderClassName();
+
+ /**
+ * Get the method name to be used to encryption.
+ *
+ * @return encryption method name.
+ */
+ String getEncryptMethodName();
+
+ /**
+ * Get the method name to be used to decryption.
+ *
+ * @return decryption method name.
+ */
+ String getDecryptMethodName();
+
}
diff --git a/src/main/java/cn/licoy/encryptbody/enums/DecryptBodyMethod.java b/src/main/java/cn/licoy/encryptbody/enums/DecryptBodyMethod.java
index 796b719..f4627e3 100644
--- a/src/main/java/cn/licoy/encryptbody/enums/DecryptBodyMethod.java
+++ b/src/main/java/cn/licoy/encryptbody/enums/DecryptBodyMethod.java
@@ -18,6 +18,10 @@ public enum DecryptBodyMethod {
/**
* RAS
*/
- RSA
+ RSA,
+ /**
+ * CUSTOM
+ */
+ CUSTOM
}
diff --git a/src/main/java/cn/licoy/encryptbody/enums/EncryptBodyMethod.java b/src/main/java/cn/licoy/encryptbody/enums/EncryptBodyMethod.java
index b54469a..2a81de0 100644
--- a/src/main/java/cn/licoy/encryptbody/enums/EncryptBodyMethod.java
+++ b/src/main/java/cn/licoy/encryptbody/enums/EncryptBodyMethod.java
@@ -26,6 +26,11 @@ public enum EncryptBodyMethod {
/**
* RSA
*/
- RSA
+ RSA,
+ /**
+ * CUSTOM
+ */
+ CUSTOM
+
}
diff --git a/src/main/java/cn/licoy/encryptbody/util/CommonUtils.java b/src/main/java/cn/licoy/encryptbody/util/CommonUtils.java
index fd5a293..397eeb2 100644
--- a/src/main/java/cn/licoy/encryptbody/util/CommonUtils.java
+++ b/src/main/java/cn/licoy/encryptbody/util/CommonUtils.java
@@ -37,19 +37,49 @@ public static String checkAndGetKey(String k1, String k2, String keyName) {
*/
public static RSA infoBeanToRsaInstance(ISecurityInfo info) {
RSA rsa;
- switch (info.getRsaKeyType()) {
- case PUBLIC:
- rsa = new RSA(null, SecureUtil.decode(info.getKey()));
- break;
- case PRIVATE:
- rsa = new RSA(SecureUtil.decode(info.getKey()), null);
- break;
- default:
- throw new IllegalSecurityTypeException();
+
+ try {
+ switch (info.getRsaKeyType()) {
+ case PUBLIC:
+ rsa = loadRsaPublicKey(info.getKey());
+ break;
+ case PRIVATE:
+ rsa = loadRsaPrivateKey(info.getKey());
+ break;
+ default:
+ throw new IllegalSecurityTypeException();
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("failed to load rsa, " + e.getMessage());
}
+
+
return rsa;
}
+ private static RSA loadRsaPublicKey(String key) throws Exception {
+ //Try to load the key as value. If exception occurs, treat it as a file and try again.
+ try {
+ return new RSA(null, SecureUtil.decode(key));
+ } catch (Exception e) {
+ key = new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(key)), java.nio.charset.StandardCharsets.UTF_8);
+ return new RSA(null, SecureUtil.decode(key));
+ }
+ }
+
+ private static RSA loadRsaPrivateKey(String key) throws Exception {
+ //Try to load the key as value. If exception occurs, treat it as a file and try again.
+ try {
+ return new RSA(SecureUtil.decode(key), null);
+ } catch (Exception e) {
+ key = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(key)).toString();
+ return new RSA(SecureUtil.decode(key), null);
+ }
+
+ }
+
+
/**
* 是否转换为string
*