mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
Encrypt and Decrypt tool, and load remote config file
This commit is contained in:
@@ -16,15 +16,17 @@
|
||||
package com.alibaba.druid.filter.config;
|
||||
|
||||
import com.alibaba.druid.filter.FilterAdapter;
|
||||
import com.alibaba.druid.filter.config.loader.ConfigLoader;
|
||||
import com.alibaba.druid.filter.config.loader.ConfigLoaderFactory;
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.pool.DruidDataSourceFactory;
|
||||
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.support.security.decryptor.DecryptException;
|
||||
import com.alibaba.druid.support.security.decryptor.Decrypter;
|
||||
import com.alibaba.druid.support.security.decryptor.DecrypterFactory;
|
||||
import com.alibaba.druid.support.security.decryptor.SensitiveParameters;
|
||||
import com.alibaba.druid.filter.config.security.decrypter.DecryptException;
|
||||
import com.alibaba.druid.filter.config.security.decrypter.Decrypter;
|
||||
import com.alibaba.druid.filter.config.security.decrypter.DecrypterFactory;
|
||||
import com.alibaba.druid.filter.config.security.decrypter.SensitiveParameters;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
@@ -76,10 +78,6 @@ import java.util.Properties;
|
||||
* #远程文件路径
|
||||
* config.file=http://xxxxx(http://开头或者file:开头)
|
||||
*
|
||||
* #AES解密, key不指定, 使用默认的
|
||||
* config.decrypt=AES
|
||||
* config.decrypt.key=abcdfeg
|
||||
*
|
||||
* #RSA解密, Key不指定, 使用默认的
|
||||
* config.decrypt=RSA
|
||||
* config.decrypt.key=密钥字符串
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.alibaba.druid.filter.config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 配置文件装载器.
|
||||
*
|
||||
* 要实现自己的装载器有如下要求.
|
||||
* 1. 一定要有自己的协议格式, 例如是由 my-protocol: 开头的protocol是由我支持的.
|
||||
* public isSupported(String protocol) {
|
||||
* return protocol.startsWith("my-protocol:");
|
||||
* }
|
||||
*
|
||||
* 2. 要在class path的根目录下的ConfigLoader.properties注册(如果没有这个文件, 可以自己创建一个). 例如:
|
||||
* WEB-INF/classes/ConfigLoader.properties [格式如下]:
|
||||
* ConfigLoader.myConfigLoader=com.xxx.xxx.xx.MyConfigLoader
|
||||
*
|
||||
* 3. 配置<code>DruidDataSource</code>的时候的URL就要设置为装载器的协议格式
|
||||
* DruidDataSource dataSource = new DruidDataSource();
|
||||
* dataSource.setUrl("my-protocol:http://xxxxxx/");
|
||||
*
|
||||
* </pre>
|
||||
* @see com.alibaba.druid.filter.config.impl.FileConfigLoader
|
||||
* @see com.alibaba.druid.filter.config.impl.HttpConfigLoader
|
||||
*
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public interface ConfigLoader {
|
||||
|
||||
/**
|
||||
* 唯一的ID
|
||||
* @return
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* 获得配置内容, 如果没有, 返回<code>null</code>.
|
||||
* @param protocol
|
||||
* @return
|
||||
*/
|
||||
public Properties loadConfig(String protocol);
|
||||
|
||||
/**
|
||||
* 是否支持该协议, 如果支持, 将会使用该装载器获取配置文件
|
||||
* @param protocol
|
||||
* @return
|
||||
*/
|
||||
public boolean isSupported(String protocol);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.alibaba.druid.filter.config.loader;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 配置文件装载器.
|
||||
* </pre>
|
||||
*
|
||||
* @see com.alibaba.druid.filter.config.ConfigFilter
|
||||
* @see ConfigLoaderFactory
|
||||
*
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public interface ConfigLoader {
|
||||
|
||||
/**
|
||||
* 唯一的ID
|
||||
* @return
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* 获得配置内容, 如果没有, 返回<code>null</code>.
|
||||
* @param protocol
|
||||
* @return
|
||||
*/
|
||||
public Properties loadConfig(String protocol);
|
||||
|
||||
/**
|
||||
* 是否支持该协议, 如果支持, 将会使用该装载器获取配置文件
|
||||
* @param protocol
|
||||
* @return
|
||||
*/
|
||||
public boolean isSupported(String protocol);
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.filter.config;
|
||||
package com.alibaba.druid.filter.config.loader;
|
||||
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
@@ -11,9 +11,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* <pre>
|
||||
* 如果有扩展的装载器.
|
||||
* 可以放到classes目录下的ConfigLoader.properties
|
||||
* 可以放到classes目录下的druid-config-loader.properties
|
||||
*
|
||||
* ConfigLoader.properties 内容如下:
|
||||
* druid-config-loader.properties 内容如下:
|
||||
* ConfigLoader.x=com.xx.xxx.xx.MyConfigLoader
|
||||
*
|
||||
* </pre>
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.druid.filter.config.impl;
|
||||
package com.alibaba.druid.filter.config.loader.impl;
|
||||
|
||||
import com.alibaba.druid.filter.config.ConfigLoader;
|
||||
import com.alibaba.druid.filter.config.loader.ConfigLoader;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.druid.filter.config.impl;
|
||||
package com.alibaba.druid.filter.config.loader.impl;
|
||||
|
||||
import com.alibaba.druid.filter.config.ConfigLoader;
|
||||
import com.alibaba.druid.filter.config.loader.ConfigLoader;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
+1
-7
@@ -1,9 +1,8 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
package com.alibaba.druid.filter.config.security.decrypter;
|
||||
|
||||
import com.alibaba.druid.util.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 加密抽象类
|
||||
@@ -12,11 +11,6 @@ import java.util.Properties;
|
||||
*/
|
||||
public abstract class AbstractDecrypter implements Decrypter {
|
||||
|
||||
/**
|
||||
* DES, AES 密钥都有长度限制, 如果不够, 默认使用该字段补长
|
||||
*/
|
||||
public static final String KEY_PADDING = "FOLLOW YOUR HEART. YOU CAN DO BEST THAN ANY ONE.";
|
||||
|
||||
public final static String KEY = "config.decrypt.key";
|
||||
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
package com.alibaba.druid.filter.config.security.decrypter;
|
||||
|
||||
/**
|
||||
* 加密出错
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
package com.alibaba.druid.filter.config.security.decrypter;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -26,10 +26,10 @@ import java.util.Properties;
|
||||
*
|
||||
* </pre>
|
||||
* <br/>
|
||||
* <b>默认下, Druid 已经实现了AES, RSA加解密. AES, RSA 密文必须是通过Druid Tool的加密工具生成的</b>
|
||||
* <b>默认下, Druid 已经实现了RSA加解密. RSA 密文必须是通过Druid Tool的加密工具生成的</b>
|
||||
* <b>获取默认解密方式可以通过 DecrypterFactory.getDecrypter(String id)</b>
|
||||
* <br/>
|
||||
* <b>使用加密工具: java -cp druid-x.x.x.jar com.alibaba.druid.support.security.tool.Main</b>
|
||||
* <b>使用加密工具: java -cp druid-x.x.x.jar com.alibaba.druid.filter.config.security.tool.Main</b>
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public interface Decrypter {
|
||||
+1
-4
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
package com.alibaba.druid.filter.config.security.decrypter;
|
||||
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
@@ -17,10 +17,7 @@ public class DecrypterFactory {
|
||||
|
||||
static {
|
||||
RsaDecrypter rsa = new RsaDecrypter();
|
||||
AesDecrypter aes = new AesDecrypter();
|
||||
|
||||
decrypters.put(rsa.getId(), rsa);
|
||||
decrypters.put(aes.getId(), aes);
|
||||
}
|
||||
|
||||
private DecrypterFactory() {}
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
package com.alibaba.druid.filter.config.security.decrypter;
|
||||
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
@@ -8,7 +8,6 @@ import com.alibaba.druid.util.JdbcUtils;
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
@@ -50,7 +49,10 @@ public class RsaDecrypter extends AbstractDecrypter {
|
||||
*/
|
||||
public SensitiveParameters decrypt(SensitiveParameters parameters, Properties info) throws DecryptException {
|
||||
PublicKey publicKey = getPublicKey(info);
|
||||
return decrypt(parameters, publicKey);
|
||||
}
|
||||
|
||||
protected SensitiveParameters decrypt(SensitiveParameters parameters, PublicKey publicKey) throws DecryptException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
package com.alibaba.druid.filter.config.security.decrypter;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.alibaba.druid.filter.config.security.tool;
|
||||
|
||||
/**
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public interface Action {
|
||||
|
||||
public String getId();
|
||||
|
||||
public void execute();
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
package com.alibaba.druid.filter.config.security.tool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
+2
-3
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
package com.alibaba.druid.filter.config.security.tool;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Druid 提供的加密工具. 如下运行
|
||||
*
|
||||
* <b>java -cp druid-x.x.x.jar com.alibaba.druid.support.security.tool.Main</b>
|
||||
* <b>java -cp druid-x.x.x.jar com.alibaba.druid.filter.config.security.tool.Main</b>
|
||||
*
|
||||
* </pre>
|
||||
* @author Jonas Yang
|
||||
@@ -17,7 +17,6 @@ public class Main {
|
||||
static {
|
||||
int i = 1;
|
||||
help.addAction(Integer.toString(i++), new RsaAction());
|
||||
help.addAction(Integer.toString(i++), new AesAction());
|
||||
help.addAction("h", help);
|
||||
help.addAction("q", new QuitAction());
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
package com.alibaba.druid.filter.config.security.tool;
|
||||
|
||||
/**
|
||||
* @author Jonas Yang
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
package com.alibaba.druid.filter.config.security.tool;
|
||||
|
||||
import com.alibaba.druid.util.Base64;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.alibaba.druid.support.security.decryptor;
|
||||
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* AES 解密器, 如果不指定密钥, 使用默认的密钥.
|
||||
* 通过一下方式指定密钥:
|
||||
* Properties info = new Properties();
|
||||
* info.put("config.decrypt.key", "密钥");
|
||||
*
|
||||
* String 明文 = new AesDecrypter().decrypt("密文", info);
|
||||
* </pre>
|
||||
*
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public class AesDecrypter extends AbstractDecrypter {
|
||||
private static Log log = LogFactory.getLog(AesDecrypter.class);
|
||||
|
||||
private final static int KEY_LENGTH = 16; //128
|
||||
|
||||
public String getId() {
|
||||
return "AES";
|
||||
}
|
||||
|
||||
/**
|
||||
* 密钥 放在 <code>Properties</code> 参数中, key 是 config.decrypt.key, 如果不指定密钥, 将使用默认密钥
|
||||
* @param parameters 密文参数
|
||||
* @param info
|
||||
* @return
|
||||
* @throws DecryptException
|
||||
*/
|
||||
public SensitiveParameters decrypt(SensitiveParameters parameters, Properties info) throws DecryptException {
|
||||
SecretKeySpec spec = getSpec(info);
|
||||
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, spec);
|
||||
|
||||
String passwordPlainText = decrypt(cipher, parameters.getPassword());
|
||||
|
||||
return new SensitiveParameters(parameters.getUrl(), parameters.getUsername(), passwordPlainText);
|
||||
} catch (Exception e) {
|
||||
throw new DecryptException("Failed to decrypt JDBC password’", e);
|
||||
}
|
||||
}
|
||||
|
||||
SecretKeySpec getSpec(Properties info) {
|
||||
String key = null;
|
||||
|
||||
if (info != null) {
|
||||
key = info.getProperty(KEY);
|
||||
}
|
||||
|
||||
if (key == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Decrypt by default key");
|
||||
}
|
||||
key = "";
|
||||
}
|
||||
|
||||
if (key.length() < 16) {
|
||||
key = KEY_PADDING.substring(0, KEY_LENGTH - key.length()) + key;
|
||||
}
|
||||
|
||||
return new SecretKeySpec(key.getBytes(), "AES");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
|
||||
/**
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public abstract class AbstractAction implements Action {
|
||||
private int minKeyLength;
|
||||
|
||||
private int maxKeyLength;
|
||||
|
||||
private String prompt;
|
||||
|
||||
public AbstractAction(int minKeyLength, int maxKeyLength, String prompt) {
|
||||
if (minKeyLength < 0 || maxKeyLength < 0) {
|
||||
throw new IllegalArgumentException("The key length must be a positive.");
|
||||
}
|
||||
|
||||
if (maxKeyLength < minKeyLength) {
|
||||
throw new IllegalArgumentException("The max length of key cannot be less than min length.");
|
||||
}
|
||||
|
||||
this.minKeyLength = minKeyLength;
|
||||
this.maxKeyLength = maxKeyLength;
|
||||
this.prompt = "[" + prompt + "]";
|
||||
}
|
||||
|
||||
protected abstract String encrypt(String keyString, String plainString) throws Exception;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
System.out.println();
|
||||
|
||||
String keyString = readPassword();
|
||||
if (keyString == null) return;
|
||||
|
||||
String plainString = System.console().readLine(this.prompt + "请输入要需要加密的明文: ");
|
||||
if (plainString == null) {
|
||||
System.err.println("输入不能为空.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String encryptedString = encrypt(keyString, plainString);
|
||||
System.out.println("请记住以下的密文, 长度为[" + encryptedString.length() + "].");
|
||||
System.out.println();
|
||||
System.out.println(encryptedString);
|
||||
System.out.println();
|
||||
} catch (Exception e) {
|
||||
System.out.println("加密出错.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected String readPassword() {
|
||||
char[] keyChars = System.console().readPassword(this.prompt + "请输入密码[长度最大为" + this.maxKeyLength + "字节]: ");
|
||||
if (keyChars == null) {
|
||||
keyChars = new char[0];
|
||||
}
|
||||
|
||||
String keyString = new String(keyChars);
|
||||
|
||||
if (keyString.length() < this.minKeyLength || keyString.length() > this.maxKeyLength) {
|
||||
System.err.println("错误: 密码长度要大于等于" + this.minKeyLength + ", 小于等于" + this.maxKeyLength + ".");
|
||||
return null;
|
||||
}
|
||||
|
||||
keyString = KEY_PADDING.substring(0, this.maxKeyLength - keyString.length()) + keyString;
|
||||
|
||||
return keyString;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
|
||||
import com.alibaba.druid.support.security.decryptor.AbstractDecrypter;
|
||||
|
||||
/**
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public interface Action {
|
||||
/**
|
||||
* DES, AES 密钥都有长度限制, 如果不够, 默认使用该字段补长
|
||||
*/
|
||||
public String KEY_PADDING = AbstractDecrypter.KEY_PADDING;
|
||||
|
||||
public String getId();
|
||||
|
||||
public void execute();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.alibaba.druid.support.security.tool;
|
||||
|
||||
import com.alibaba.druid.util.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* @author Jonas Yang
|
||||
*/
|
||||
public class AesAction extends AbstractAction {
|
||||
|
||||
public AesAction() {
|
||||
super(0, 16, "AES");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "AES";
|
||||
}
|
||||
|
||||
protected String encrypt(String keyString, String plainString) throws Exception {
|
||||
SecretKeySpec spec = new SecretKeySpec(keyString.getBytes(), "AES");
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, spec);
|
||||
|
||||
byte[] encryptedBytes = cipher.doFinal(plainString.getBytes());
|
||||
|
||||
return Base64.byteArrayToBase64(encryptedBytes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user