refactor ConfigFilter

This commit is contained in:
wenshao
2012-09-01 07:51:29 +08:00
parent f7be377c79
commit b0a519e44f
17 changed files with 261 additions and 951 deletions
@@ -15,23 +15,28 @@
*/
package com.alibaba.druid.filter.config;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.X509EncodedKeySpec;
import java.sql.SQLException;
import java.util.Properties;
import javax.crypto.Cipher;
import com.alibaba.druid.filter.FilterAdapter;
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 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.util.Base64;
import com.alibaba.druid.util.JdbcUtils;
/**
@@ -88,144 +93,236 @@ import com.alibaba.druid.util.JdbcUtils;
* config.decrypt.x509File=证书路径
*
* </pre>
* @author Jonas Yang
*/
public class ConfigFilter extends FilterAdapter {
private static Log log = LogFactory.getLog(ConfigFilter.class);
private static Log LOG = LogFactory.getLog(ConfigFilter.class);
public static final String CONFIG_FILE = "config.file";
public static final String CONFIG_FILE = "config.file";
public static final String CONFIG_DECRYPT = "config.decrypt";
public static final String CONFIG_DECRYPT = "config.decrypt";
public static final String SYS_PROP_CONFIG_FILE = "druid.config.file";
public static final String SYS_PROP_CONFIG_FILE = "druid.config.file";
public static final String KEY = "config.decrypt.key";
public static final String KEY_FILE = "config.decrypt.keyFile";
public static final String X509_FILE = "config.decrypt.x509File";
public static final String DEFAULT_PUBLIC_KEY_STRING = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHGwq7q2RmwuRgKxBypQHw0mYu4BQZ3eMsTrdK8E6igRcxsobUC7uT0SoxIjl1WveWniCASejoQtn/BY6hVKWsCAwEAAQ==";
public ConfigFilter(){
}
public void init(DataSourceProxy dataSourceProxy) {
if (!(dataSourceProxy instanceof DruidDataSource)) {
log.error("ConfigLoader only support DruidDataSource");
LOG.error("ConfigLoader only support DruidDataSource");
}
Properties info = null;
DruidDataSource dataSource = (DruidDataSource) dataSourceProxy;
Properties connectinProperties = dataSource.getConnectProperties();
// 获取远程配置文件
String configFile = connectinProperties.getProperty(CONFIG_FILE);
// 如果系统参数有指定
if (configFile == null) {
configFile = System.getProperty(SYS_PROP_CONFIG_FILE);
}
if (configFile != null && configFile.length() > 0) {
if (log.isDebugEnabled()) {
log.debug("Config file will be load from [" + configFile + "].");
}
info = loadConfig(configFile);
if (info == null) {
throw new IllegalArgumentException("Cannot load remote config file from the [config.file=" + configFile
+ "].");
}
} else {
info = new Properties();
if (log.isDebugEnabled()) {
log.debug("No remote config file.");
}
}
Properties configFileProperties = loadPropertyFromConfigFile(connectinProperties);
// 判断是否需要解密,如果需要就进行解密行动
String decrypterId = info.getProperty(CONFIG_DECRYPT);
boolean isRemotedSecurityConfig = true;
boolean decrypt = isDecrypt(connectinProperties, configFileProperties);
if (decrypterId == null) {
decrypterId = connectinProperties.getProperty(CONFIG_DECRYPT);
isRemotedSecurityConfig = false;
if (log.isDebugEnabled()) {
log.debug("Get decrypt method " + decrypterId + " from local config.");
}
} else {
if (log.isDebugEnabled()) {
log.debug("Get decrypt method " + decrypterId + " from remote config.");
}
}
if (decrypterId != null) {
if (log.isDebugEnabled()) {
log.debug("Use [" + decrypterId + "] to decrypt sensitive parameters.");
}
try {
Decrypter decrypter = DecrypterFactory.getDecrypter(decrypterId);
if (decrypter == null) {
throw new IllegalArgumentException("Druid doesn't support the decrypter [config.decrypt="
+ decrypterId + "].");
}
Properties securityInfo = isRemotedSecurityConfig ? info : connectinProperties;
SensitiveParameters originalParameter = getSensitiveParameters(dataSource, info);
SensitiveParameters parameters = decrypter.decrypt(originalParameter, securityInfo);
info.setProperty(DruidDataSourceFactory.PROP_URL, parameters.getUrl());
info.setProperty(DruidDataSourceFactory.PROP_USERNAME, parameters.getUsername());
info.setProperty(DruidDataSourceFactory.PROP_PASSWORD, parameters.getPassword());
} catch (DecryptException e) {
throw new IllegalArgumentException("Failed to decrypt.", e);
}
} else {
if (log.isDebugEnabled()) {
log.debug("No decrypt.");
}
}
// 没有任何配置
if (info.size() == 0) {
if (log.isDebugEnabled()) {
log.debug("No config.");
if (configFileProperties == null) {
if (decrypt) {
decrypt(dataSource, null);
}
return;
}
if (decrypt) {
decrypt(dataSource, configFileProperties);
}
try {
DruidDataSourceFactory.config(dataSource, info);
DruidDataSourceFactory.config(dataSource, configFileProperties);
} catch (SQLException e) {
throw new IllegalArgumentException("Config DataSource error.", e);
}
}
/**
* 如果配置文件中没有值, 就取默认值
*
* @param dataSource
* @param info
* @return
*/
SensitiveParameters getSensitiveParameters(DruidDataSource dataSource, Properties info) {
String username = info.getProperty(DruidDataSourceFactory.PROP_USERNAME);
String password = info.getProperty(DruidDataSourceFactory.PROP_PASSWORD);
String url = info.getProperty(DruidDataSourceFactory.PROP_URL);
public boolean isDecrypt(Properties connectinProperties, Properties configFileProperties) {
boolean decrypt = false;
if (username == null) {
username = dataSource.getUsername();
String decrypterId = connectinProperties.getProperty(CONFIG_DECRYPT);
if (configFileProperties != null && (decrypterId == null || decrypterId.length() == 0)) {
decrypterId = configFileProperties.getProperty(CONFIG_DECRYPT);
}
if (password == null) {
password = dataSource.getPassword();
if ("true".equals(decrypterId)) {
decrypt = true;
}
if (url == null) {
url = dataSource.getUrl();
}
return new SensitiveParameters(url, username, password);
return decrypt;
}
Properties loadPropertyFromConfigFile(Properties connectinProperties) {
String configFile = connectinProperties.getProperty(CONFIG_FILE);
if (configFile == null) {
configFile = System.getProperty(SYS_PROP_CONFIG_FILE);
}
if (configFile != null && configFile.length() > 0) {
if (LOG.isInfoEnabled()) {
LOG.info("DruidDataSource Config File load from : " + configFile);
}
Properties info = loadConfig(configFile);
if (info == null) {
throw new IllegalArgumentException("Cannot load remote config file from the [config.file=" + configFile
+ "].");
}
return info;
}
return null;
}
public void decrypt(DruidDataSource dataSource, Properties info) {
try {
String encryptedPassword = null;
if (info != null) {
encryptedPassword = info.getProperty(DruidDataSourceFactory.PROP_PASSWORD);
}
if (encryptedPassword == null || encryptedPassword.length() == 0) {
encryptedPassword = dataSource.getConnectProperties().getProperty(DruidDataSourceFactory.PROP_PASSWORD);
}
if (encryptedPassword == null || encryptedPassword.length() == 0) {
encryptedPassword = dataSource.getPassword();
}
PublicKey publicKey = getPublicKey(dataSource.getConnectProperties(), info);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
String passwordPlainText = decrypt(cipher, encryptedPassword);
if (info != null) {
info.setProperty(DruidDataSourceFactory.PROP_PASSWORD, passwordPlainText);
} else {
dataSource.setPassword(passwordPlainText);
}
} catch (Exception e) {
throw new IllegalArgumentException("Failed to decrypt.", e);
}
}
protected String decrypt(Cipher cipher, String cipherString) throws Exception {
if (cipherString == null || cipherString.length() == 0) {
return cipherString;
}
byte[] cipherBytes = Base64.base64ToByteArray(cipherString);
byte[] plainBytes = cipher.doFinal(cipherBytes);
return new String(plainBytes);
}
public PublicKey getPublicKey(Properties connectinProperties, Properties configFileProperties) {
String key = connectinProperties.getProperty(KEY);
String publicKeyFile = connectinProperties.getProperty(KEY_FILE);
String x509File = connectinProperties.getProperty(X509_FILE);
if (connectinProperties != null && (key == null || key.length() == 0)) {
key = connectinProperties.getProperty(KEY);
}
if (connectinProperties != null && (publicKeyFile == null || publicKeyFile.length() == 0)) {
publicKeyFile = connectinProperties.getProperty(KEY_FILE);
}
if (connectinProperties != null && (x509File == null || x509File.length() == 0)) {
x509File = connectinProperties.getProperty(X509_FILE);
}
if (publicKeyFile != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Decrypt by public key file");
}
return getPublicKeyByPublicKeyFile(publicKeyFile);
}
if (x509File != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Decrypt by X509 file");
}
return getPublicKeyByX509(x509File);
}
return getPublicKeyByString(key);
}
public PublicKey getPublicKeyByX509(String x509File) {
if (x509File == null || x509File.length() == 0) {
return getPublicKeyByString(null);
}
FileInputStream in = null;
try {
in = new FileInputStream(x509File);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate cer = factory.generateCertificate(in);
return cer.getPublicKey();
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
} finally {
JdbcUtils.close(in);
}
}
public PublicKey getPublicKeyByPublicKeyFile(String publicKeyFile) {
if (publicKeyFile == null || publicKeyFile.length() == 0) {
return getPublicKeyByString(null);
}
FileInputStream in = null;
try {
in = new FileInputStream(publicKeyFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] b = new byte[512 / 8];
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
byte[] publicKeyBytes = out.toByteArray();
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
} finally {
JdbcUtils.close(in);
}
}
public PublicKey getPublicKeyByString(String publicKeyString) {
if (publicKeyString == null || publicKeyString.length() == 0) {
publicKeyString = DEFAULT_PUBLIC_KEY_STRING;
}
try {
byte[] publicKeyBytes = Base64.base64ToByteArray(publicKeyString);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(x509KeySpec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
}
}
public Properties loadConfig(String filePath) {
Properties properties = new Properties();
@@ -249,7 +346,7 @@ public class ConfigFilter extends FilterAdapter {
}
if (inStream == null) {
log.error("load config file error, file : " + filePath);
LOG.error("load config file error, file : " + filePath);
return null;
}
@@ -261,7 +358,7 @@ public class ConfigFilter extends FilterAdapter {
return properties;
} catch (Exception ex) {
log.error("load config file error, file : " + filePath, ex);
LOG.error("load config file error, file : " + filePath, ex);
return null;
} finally {
JdbcUtils.close(inStream);
@@ -1,51 +1,64 @@
package com.alibaba.druid.filter.config.security.tool;
package com.alibaba.druid.filter.config;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import com.alibaba.druid.util.Base64;
import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* @author Jonas Yang
*/
public class RsaAction implements Action {
public class ConfigTools {
private static final String DEFAULT_PRIVATE_KEY_STRING = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAocbCrurZGbC5GArEHKlAfDSZi7gFBnd4yxOt0rwTqKBFzGyhtQLu5PRKjEiOXVa95aeIIBJ6OhC2f8FjqFUpawIDAQABAkAPejKaBYHrwUqUEEOe8lpnB6lBAsQIUFnQI/vXU4MV+MhIzW0BLVZCiarIQqUXeOhThVWXKFt8GxCykrrUsQ6BAiEA4vMVxEHBovz1di3aozzFvSMdsjTcYRRo82hS5Ru2/OECIQC2fAPoXixVTVY7bNMeuxCP4954ZkXp7fEPDINCjcQDywIgcc8XLkkPcs3Jxk7uYofaXaPbg39wuJpEmzPIxi3k0OECIGubmdpOnin3HuCP/bbjbJLNNoUdGiEmFL5hDI4UdwAdAiEAtcAwbm08bKN7pwwvyqaCBC//VnEWaq39DCzxr+Z2EIk=";
@Override
public String getId() {
return "RSA";
}
private static boolean isEnd = false;
public void execute() {
help();
}
public static void main(String[] args) {
System.out.println("**************************************");
System.out.println("* *");
System.out.println("* Druid 加密工具 *");
System.out.println("* *");
System.out.println("**************************************");
private void help() {
System.out.println();
System.out.println("RSA 选项: ");
System.out.println("1. 通过私钥加密");
System.out.println("2. 创建 RSA 密钥");
while (!isEnd) {
System.out.println();
System.out.println("RSA 选项: ");
System.out.println("1. 通过私钥加密");
System.out.println("2. 创建 RSA 密钥");
System.out.println("3. 退出");
System.out.println("");
String input = System.console().readLine("[RSA]请输入选项: ");
String input = System.console().readLine("[RSA]请输入选项: ");
input = input.toLowerCase();
if ("1".equals(input)) {
encrypt();
} else if ("2".equals(input)) {
generateKeys();
} else {
System.err.println("不是一个合法的输入.");
if ("1".equals(input)) {
encrypt();
} else if ("2".equals(input)) {
generateKeys();
} else if ("q".equals(input) || "3".equals(input)) {
System.exit(0);
} else {
System.err.println("不是一个合法的输入.");
}
}
}
private void encrypt() {
private static void encrypt() {
System.out.println("注意: 如果输入的不是该工具生成的私钥, 请转成PKCS#8格式, 使用下面命令");
System.out.println("openssl pkcs8 -topk8 -inform PEM -outform DER -in 原来的Key -nocrypt > 输出文件");
String filePath = System.console().readLine("[RSA]请输入私钥文件路径[如果直接回车将使用默认私钥]: ");
byte[] key = null;
if (filePath == null || filePath.length() == 0) {
key = Base64.base64ToByteArray(DEFAULT_PRIVATE_KEY_STRING);
@@ -68,7 +81,8 @@ public class RsaAction implements Action {
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedBytes = cipher.doFinal(plainString.getBytes());
String encryptedString = Base64.byteArrayToBase64(encryptedBytes);;
String encryptedString = Base64.byteArrayToBase64(encryptedBytes);
;
System.out.println("请记住以下的密文, 长度为[" + encryptedString.length() + "].");
System.out.println();
@@ -80,7 +94,7 @@ public class RsaAction implements Action {
}
}
private byte[] getPrivateKeyFromFile(String filePath) throws IOException {
private static byte[] getPrivateKeyFromFile(String filePath) throws IOException {
if (!new File(filePath).exists()) {
System.err.println("文件不存在[" + filePath + "]");
}
@@ -89,7 +103,7 @@ public class RsaAction implements Action {
FileInputStream in = null;
try {
in = new FileInputStream(filePath);
byte[] b = new byte[512/8];
byte[] b = new byte[512 / 8];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
@@ -106,7 +120,7 @@ public class RsaAction implements Action {
return out.toByteArray();
}
private void generateKeys() {
private static void generateKeys() {
System.out.println();
System.out.println("[RSA]请选择密钥长度: \n1. 512 \n2. 1024 \n3. 2048");
String keySizeString = System.console().readLine("[RSA]请输入选项: ");
@@ -145,7 +159,7 @@ public class RsaAction implements Action {
}
private void saveKey(Key key, String path) throws Exception {
private static void saveKey(Key key, String path) throws Exception {
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
@@ -156,5 +170,4 @@ public class RsaAction implements Action {
}
}
}
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.config.security.decrypter;
import com.alibaba.druid.util.Base64;
import javax.crypto.Cipher;
/**
* 加密抽象类
*
* @author Jonas Yang
*/
public abstract class AbstractDecrypter implements Decrypter {
public final static String KEY = "config.decrypt.key";
/**
* 传入密文, 返回明文
* @param cipher 解密器
* @param cipherString 密文
* @return
* @throws Exception
*/
protected String decrypt(Cipher cipher, String cipherString) throws Exception {
if (cipherString == null || cipherString.length() == 0) {
return cipherString;
}
byte[] cipherBytes = Base64.base64ToByteArray(cipherString);
byte[] plainBytes = cipher.doFinal(cipherBytes);
return new String(plainBytes);
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.config.security.decrypter;
/**
* 加密出错
*
* @author Jonas Yang
*/
public class DecryptException extends Exception {
private static final long serialVersionUID = 1L;
public DecryptException() {
super();
}
public DecryptException(String s) {
super(s);
}
public DecryptException(String s, Throwable throwable) {
super(s, throwable);
}
public DecryptException(Throwable throwable) {
super(throwable);
}
}
@@ -1,65 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.config.security.decrypter;
import java.util.Properties;
/**
* <pre>
* 解密敏感参数
* [代码例子]
*
* DruidDataSource dataSource = new DruidDataSource();
* //设置DataSource的其他参数
* dataSource.setFilters("config");
* dataSource.setUsername("test");
* dataSource.setPassword("加密的密码");
* dataSource.setConnectionProperties("config.decrypt=RSA");
*
* [下面是Spring的配置]
*
* &lt;bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"&gt;
* &lt;property name="url" value="${jdbc_url}" /&gt;
* &lt;property name="username" value="${jdbc_user}" /&gt;
* &lt;property name="password" value="${jdbc_password}" /&gt;
* <b>&lt;property name="filters" value="config" /&gt;</b>
* <b>&lt;property name="connectionProperties" value="config.decrypt=RSA" /&gt;</b>
* &lt;/bean&gt;
*
* </pre>
* <br/>
* <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.filter.config.security.tool.Main</b>
* @author Jonas Yang
*/
public interface Decrypter {
/**
* 标识符
* @return
*/
public String getId();
/**
* 返回一个解密后的(明文)参数
* @param parameters 密文参数
* @return
* @throws DecryptException 解密失败会抛出该异常
*/
public SensitiveParameters decrypt(SensitiveParameters parameters, Properties info) throws DecryptException;
}
@@ -1,90 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.config.security.decrypter;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通过 ID 获取解密器
* @author Jonas Yang
*/
public class DecrypterFactory {
private static Log log = LogFactory.getLog(DecrypterFactory.class);
private static ConcurrentHashMap<String, Decrypter> decrypters = new ConcurrentHashMap<String, Decrypter>();
static {
RsaDecrypter rsa = new RsaDecrypter();
decrypters.put(rsa.getId(), rsa);
}
private DecrypterFactory() {}
// static void initDecrypters(String resource, ClassLoader classLoader) {
// if (resource == null) {
// return;
// }
//
// InputStream inStream = null;
//
// try {
// inStream = classLoader.getResourceAsStream(resource);
// if (inStream == null) {
// return;
// }
//
// Properties p = new Properties();
// p.load(inStream);
//
// for (String key : p.stringPropertyNames()) {
// String clazzName = p.getProperty(key);
// Class clazz = classLoader.loadClass(clazzName);
// Decrypter decrypter = (Decrypter) clazz.newInstance();
//
// addDecrypter(decrypter);
// }
// } catch (Exception e) {
// log.warn("Fail to init config loaders.", e);
// } finally {
// JdbcUtils.close(inStream);
// }
// }
/**
* 通过 ID 获取解密器, 如果没有找到, 返回 <code>null</code>
* @param id
* @return
*/
public static Decrypter getDecrypter(String id) {
return decrypters.get(id);
}
public static void addDecrypter(Decrypter decrypter) {
Decrypter oldDecrypter = decrypters.putIfAbsent(decrypter.getId(), decrypter);
if (oldDecrypter != null) {
log.warn("Replace decrypter [" + decrypter.getId() + ", " + oldDecrypter.getClass() + "] with [" + decrypter.getClass() + "]");
}
}
public static Decrypter[] getDecrypters() {
return decrypters.values().toArray(new Decrypter[0]);
}
}
@@ -1,185 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.config.security.decrypter;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.util.Base64;
import com.alibaba.druid.util.JdbcUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.X509EncodedKeySpec;
import java.util.Properties;
/**
* RSA 解密器, 支持证书
*/
public class RsaDecrypter extends AbstractDecrypter {
private static Log log = LogFactory.getLog(RsaDecrypter.class);
public static final String KEY = "config.decrypt.key";
public static final String KEY_FILE = "config.decrypt.keyFile";
public static final String X509_FILE = "config.decrypt.x509File";
public static final String DEFAULT_PUBLIC_KEY_STRING = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHGwq7q2RmwuRgKxBypQHw0mYu4BQZ3eMsTrdK8E6igRcxsobUC7uT0SoxIjl1WveWniCASejoQtn/BY6hVKWsCAwEAAQ==";
public String getId() {
return "RSA";
}
/**
* <pre>
* 密钥 放在 <code>Properties</code> 参数中,
* 如果是密钥字符串, key 是 config.decrypt.key
* 如果是密钥文件, key 是 config.decrypt.keyFile
* 如果是证书文件, key 是 config.decrypt.x509File
*
* 如果不指定密钥, 将使用默认密钥
* </pre>
*
* @param parameters 密文参数
* @param info
* @return
* @throws DecryptException
*/
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);
String passwordPlainText = decrypt(cipher, parameters.getPassword());
return new SensitiveParameters(parameters.getUrl(), parameters.getUsername(), passwordPlainText);
} catch (DecryptException e) {
throw e;
} catch (Exception e) {
throw new DecryptException("Failed to decrypt parameters", e);
}
}
/**
* 通过指定
*
* @param info
* @return
*/
public PublicKey getPublicKey(Properties info) {
String key = null;
String publicKeyFile = null;
String x509File = null;
if (info != null) {
key = info.getProperty(KEY);
publicKeyFile = info.getProperty(KEY_FILE);
x509File = info.getProperty(X509_FILE);
}
if (publicKeyFile != null) {
if (log.isDebugEnabled()) {
log.debug("Decrypt by public key file");
}
return getPublicKeyByPublicKeyFile(publicKeyFile);
}
if (x509File != null) {
if (log.isDebugEnabled()) {
log.debug("Decrypt by X509 file");
}
return getPublicKeyByX509(x509File);
}
if (log.isDebugEnabled()) {
log.debug("Decrypt by public key string");
}
return getPublicKeyByString(key);
}
public PublicKey getPublicKeyByString(String publicKeyString) {
if (publicKeyString == null || publicKeyString.length() == 0) {
if (log.isDebugEnabled()) {
log.debug("Decrypt by default key");
}
publicKeyString = DEFAULT_PUBLIC_KEY_STRING;
}
try {
byte[] publicKeyBytes = Base64.base64ToByteArray(publicKeyString);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(x509KeySpec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
}
}
public PublicKey getPublicKeyByPublicKeyFile(String publicKeyFile) {
if (publicKeyFile == null || publicKeyFile.length() == 0) {
return getPublicKeyByString(null);
}
FileInputStream in = null;
try {
in = new FileInputStream(publicKeyFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] b = new byte[512 / 8];
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
byte[] publicKeyBytes = out.toByteArray();
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
} finally {
JdbcUtils.close(in);
}
}
public PublicKey getPublicKeyByX509(String x509File) {
if (x509File == null || x509File.length() == 0) {
return getPublicKeyByString(null);
}
FileInputStream in = null;
try {
in = new FileInputStream(x509File);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate cer = factory.generateCertificate(in);
return cer.getPublicKey();
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
} finally {
JdbcUtils.close(in);
}
}
}
@@ -1,50 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.config.security.decrypter;
/**
* <pre>
* 存放, 数据库的关键参数:URL, 用户名, 密码
* 如果存放的是明文, 请尽可能缩短实例的作用域
* </pre>
*
* @author Jonas Yang
*/
public class SensitiveParameters {
private transient final String url;
private transient final String username;
private transient final String password;
public SensitiveParameters(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
public String getUrl() {
return url;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
@@ -1,11 +0,0 @@
package com.alibaba.druid.filter.config.security.tool;
/**
* @author Jonas Yang
*/
public interface Action {
public String getId();
public void execute();
}
@@ -1,56 +0,0 @@
package com.alibaba.druid.filter.config.security.tool;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jonas Yang
*/
public class HelpAction implements Action {
private List<ActionDesc> list = new ArrayList<ActionDesc>();
@Override
public String getId() {
return "HELP";
}
public void execute() {
System.out.println();
for (ActionDesc desc : list) {
System.out.println(desc.getKey() + ". " + desc.getAction().getId());
}
}
public void addAction(String key, Action action) {
this.list.add(new ActionDesc(key, action));
}
public Action getAction(String key) {
for (ActionDesc desc : list) {
if (desc.getKey().equals(key)) {
return desc.getAction();
}
}
return null;
}
private static class ActionDesc {
private final String key;
private final Action action;
public ActionDesc(String key, Action action) {
this.key = key;
this.action = action;
}
public Action getAction() {
return action;
}
public String getKey() {
return key;
}
}
}
@@ -1,48 +0,0 @@
package com.alibaba.druid.filter.config.security.tool;
/**
* <pre>
* Druid 提供的加密工具. 如下运行
*
* <b>java -cp druid-x.x.x.jar com.alibaba.druid.filter.config.security.tool.Main</b>
*
* </pre>
* @author Jonas Yang
*/
public class Main {
private static boolean isEnd = false;
private static HelpAction help = new HelpAction();
static {
int i = 1;
help.addAction(Integer.toString(i++), new RsaAction());
help.addAction("h", help);
help.addAction("q", new QuitAction());
}
public static void main(String[] args) {
header();
help.execute();
while(!isEnd) {
String input = System.console().readLine("请输入选项: ");
input = input.toLowerCase();
Action action = help.getAction(input);
if (action == null) {
System.err.println(input + " 不是一个合法的选项.[如需要帮助请输入 h]");
continue;
} else {
action.execute();
}
}
}
public static void header() {
System.out.println("**************************************");
System.out.println("* *");
System.out.println("* Druid 加密工具 *");
System.out.println("* *");
System.out.println("**************************************");
}
}
@@ -1,25 +0,0 @@
package com.alibaba.druid.filter.config.security.tool;
/**
* @author Jonas Yang
*/
public class QuitAction implements Action {
@Override
public String getId() {
return "QUIT";
}
public void execute() {
foot();
System.exit(0);
}
private static void foot() {
System.out.println("**************************************");
System.out.println("* *");
System.out.println("* 再 见 了 *");
System.out.println("* *");
System.out.println("**************************************");
}
}
@@ -49,7 +49,6 @@ import javax.sql.DataSource;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.FilterChainImpl;
import com.alibaba.druid.filter.FilterManager;
import com.alibaba.druid.filter.config.ConfigFilter;
import com.alibaba.druid.pool.DruidDataSource.ActiveConnectionTraceInfo;
import com.alibaba.druid.pool.vendor.NullExceptionSorter;
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
@@ -73,7 +73,7 @@ public class ConfigFilterTest extends ConfigFileGenerator {
dataSource.setUsername("test");
dataSource.setPassword(encryptedString);
dataSource.setFilters("config");
dataSource.setConnectionProperties("config.decrypt=RSA");
dataSource.setConnectionProperties("config.decrypt=true");
try {
dataSource.init();
@@ -84,32 +84,11 @@ public class ConfigFilterTest extends ConfigFileGenerator {
}
}
@Test
public void testInitInvalidDecrypt() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriver(MockDriver.instance);
dataSource.setUrl("");
dataSource.setUsername("test");
dataSource.setPassword(encryptedString);
dataSource.setFilters("config");
dataSource.setConnectionProperties("config.decrypt=ABCDEF");
Exception error = null;
try {
dataSource.init();
} catch (IllegalArgumentException e) {
error = e;
} finally {
JdbcUtils.close(dataSource);
}
Assert.assertNotNull(error);
}
@Test
public void testInitRemoteConfigAndDecrypt() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setFilters("config");
dataSource.setConnectionProperties("config.decrypt=RSA;config.file=" + "file://" + this.filePath);
dataSource.setConnectionProperties("config.decrypt=true;config.file=" + "file://" + this.filePath);
try {
dataSource.init();
@@ -1,22 +0,0 @@
package com.alibaba.druid.bvt.filter.config;
import junit.framework.Assert;
import org.junit.Test;
import com.alibaba.druid.filter.config.security.decrypter.DecryptException;
import com.alibaba.druid.filter.config.security.decrypter.DecrypterFactory;
import com.alibaba.druid.filter.config.security.decrypter.RsaDecrypter;
/**
* @author Jonas Yang
*/
public class DecrypterFactoryTest {
@Test
public void testGetDecrypter() throws DecryptException {
Assert.assertNotNull(DecrypterFactory.getDecrypter("RSA"));
Assert.assertEquals(DecrypterFactory.getDecrypter("RSA").getClass(), RsaDecrypter.class);
Assert.assertNull(DecrypterFactory.getDecrypter("ajfdjksfklds;"));
}
}
@@ -1,135 +0,0 @@
package com.alibaba.druid.bvt.filter.config;
import com.alibaba.druid.filter.config.security.decrypter.DecryptException;
import com.alibaba.druid.filter.config.security.decrypter.RsaDecrypter;
import com.alibaba.druid.filter.config.security.decrypter.SensitiveParameters;
import com.alibaba.druid.util.Base64;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
import java.security.PublicKey;
import java.util.Properties;
/**
* @author Jonas Yang
*/
public class RsaDecrypterTest {
String publicKeyContent = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2YdTlkEw7JfRNItCosRf4QO8vOL54ZEdj0LZm" +
"tKhNFnNRTtHrLDFT44BPYqFSLVv3TOhLxJcEs31kyQhXEC5b9ozpZBvYrJDSt8QQMoYDAMLSojs+" +
"K4vTJavgbRKKads0di+DuoMmJ5g5dcbDNd1Kb4kgWhrZtUwhfSjQklaiIwIDAQAB";
String encryptedString = "Zaa9qpyB9snnBUz7UBvcaHcNfWMKEWvhnx0dbWmwxBOJ9vkeZNXjV9MEJezSzL+6yQh7aO29vD67" +
"qedtD9ublVwwf+jTPuO1NjzAKy0+yWVQ1KdR2KaQuvCxFWiM1n6nbpkUGB6OVpoQgFnFzdKlyC9D" +
"KU3Q6evGa1hlZ8jnHcA=";
String keyFilePath;
@Before
public void setUp() throws IOException {
File tmp = File.createTempFile("druid_public_key", Long.toString(System.currentTimeMillis()));
byte[] keyContent = Base64.base64ToByteArray(this.publicKeyContent);
FileOutputStream out = null;
try {
out = new FileOutputStream(tmp);
out.write(keyContent);
} finally {
if (out != null) {
out.close();
}
}
this.keyFilePath = tmp.getAbsolutePath();
}
@After
public void tearDown() {
File tmp = new File(this.keyFilePath);
if (tmp.exists()) {
tmp.delete();
}
this.keyFilePath = null;
}
@Test
public void testGetPublicKeyByString() {
RsaDecrypter rsaDecrypter = new RsaDecrypter();
PublicKey key = rsaDecrypter.getPublicKeyByString(this.publicKeyContent);
Assert.assertNotNull("It is null", key);
key = rsaDecrypter.getPublicKeyByString(null);
Assert.assertNotNull("It is null", key);
Assert.assertEquals(RsaDecrypter.DEFAULT_PUBLIC_KEY_STRING, Base64.byteArrayToBase64(key.getEncoded()));
try {
key = rsaDecrypter.getPublicKeyByString("abcdef");
Assert.assertTrue("It is here", false);
} catch (Exception e) {
}
}
@Test
public void testGetPublicKeyByPublicKeyFile() {
RsaDecrypter rsaDecrypter = new RsaDecrypter();
PublicKey key = rsaDecrypter.getPublicKeyByPublicKeyFile(this.keyFilePath);
Assert.assertNotNull("It is null", key);
key = rsaDecrypter.getPublicKeyByPublicKeyFile(null);
Assert.assertNotNull("It is null", key);
Assert.assertEquals(RsaDecrypter.DEFAULT_PUBLIC_KEY_STRING, Base64.byteArrayToBase64(key.getEncoded()));
try {
key = rsaDecrypter.getPublicKeyByPublicKeyFile("abcdef");
Assert.assertTrue("It is here", false);
} catch (Exception e) {
}
}
@Test
public void testGetPublicKeyByX509File() {
//No test
}
@Test
public void testGetPublicKey() {
RsaDecrypter rsaDecrypter = new RsaDecrypter();
PublicKey key = rsaDecrypter.getPublicKey(null);
Assert.assertNotNull("It is null", key);
Assert.assertEquals(RsaDecrypter.DEFAULT_PUBLIC_KEY_STRING, Base64.byteArrayToBase64(key.getEncoded()));
Properties info = new Properties();
info.put(RsaDecrypter.KEY, this.publicKeyContent);
key = rsaDecrypter.getPublicKey(info);
Assert.assertNotNull("It is null", key);
Assert.assertEquals(this.publicKeyContent, Base64.byteArrayToBase64(key.getEncoded()));
info = new Properties();
info.put(RsaDecrypter.KEY_FILE, this.keyFilePath);
key = rsaDecrypter.getPublicKey(info);
Assert.assertNotNull("It is null", key);
Assert.assertEquals(this.publicKeyContent, Base64.byteArrayToBase64(key.getEncoded()));
}
@Test
public void testDecrypt() throws DecryptException {
RsaDecrypter decrypter = new RsaDecrypter();
Properties info = new Properties();
info.put(RsaDecrypter.KEY_FILE, this.keyFilePath);
SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", encryptedString), info);
Assert.assertEquals("It is not same.", "xiaoyu", parameters.getPassword());
}
@Test
public void testDecryptWithEmptyKey() throws DecryptException {
RsaDecrypter decrypter = new RsaDecrypter();
SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "OJfUm6WCHi7EuXqE6aEc+Po2xFrAGBeSNy8O2jWhV2FTG8/5kbRRr2rjNKhptlevm/03Y0048P7h88gdUOXAYg=="), (Properties) null);
Assert.assertEquals("It is not same.", "xiaoyu", parameters.getPassword());
}
}
@@ -1,6 +1,8 @@
package com.alibaba.druid.filter.config;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.util.JdbcUtils;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
@@ -32,11 +34,7 @@ public class ConfigFileGenerator {
} catch (IOException e) {
Assert.assertNull("Failed to init resource.", e);
} finally {
if (out != null) {
try {
out.close();
} catch(Exception e){}
}
JdbcUtils.close(out);
}
}