Encrypt tool and Decrypter

This commit is contained in:
xiaoyu830411
2012-08-27 11:18:46 +08:00
parent f172f0a756
commit 13fbf8b483
23 changed files with 1034 additions and 0 deletions
@@ -0,0 +1,24 @@
package com.alibaba.druid.pool;
/**
* 加密出错
*
* @author Jonas Yang
*/
public class DecryptException extends Exception {
public DecryptException() {
super(); //To change body of overridden methods use File | Settings | File Templates.
}
public DecryptException(String s) {
super(s); //To change body of overridden methods use File | Settings | File Templates.
}
public DecryptException(String s, Throwable throwable) {
super(s, throwable); //To change body of overridden methods use File | Settings | File Templates.
}
public DecryptException(Throwable throwable) {
super(throwable); //To change body of overridden methods use File | Settings | File Templates.
}
}
@@ -0,0 +1,49 @@
package com.alibaba.druid.pool;
/**
* <pre>
* 解密敏感参数
* 使用解密器 只要简单实现这个类就可以了。
*
* public class MyDecrypter implement Decrypter {
* public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
* String url = ....;
* String username = ....;
* String password = ....;
*
* return new SensitiveParameters(url, username, password);
* }
* }
*
* DruidDataSource dataSource = new DruidDataSource();
* //设置DataSource的其他参数
* dataSource.setDecrypter(new MyDecrypter);
*
* [下面是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="decrypter"&gt;&lt;ref bean="myDecrypter"/&gt;&lt;/property&gt;</b>
* &lt;/bean&gt;
*
* &lt;bean id="myDecrypter" class="MyDecrypter"/&gt;
*
* </pre>
* <br/>
* <b>默认下, Druid 已经实现了AES, DES, RSA, Blowfish(兼容JBOSS)等加解密. AES, DES 解密的密文必须是通过Druid Tool的加密工具生成的</b>
* <br/>
* <b>使用加密工具: java -cp druid-x.x.x.jar com.alibaba.druid.support.security.tool.Main</b>
* @author Jonas Yang
*/
public interface Decrypter {
/**
* 返回一个解密后的(明文)参数
* @param parameters 密文参数
* @return
* @throws DecryptException 解密失败会抛出该异常
*/
public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException;
}
@@ -0,0 +1,35 @@
package com.alibaba.druid.pool;
/**
* <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;
}
}
@@ -0,0 +1,37 @@
package com.alibaba.druid.support.security.decryptor;
import com.alibaba.druid.pool.Decrypter;
import com.alibaba.druid.util.Base64;
import javax.crypto.Cipher;
/**
* 加密抽象类
*
* @author Jonas Yang
*/
public abstract class AbstractDecrypter implements Decrypter {
/**
* DES, AES 密钥都有长度限制, 如果不够, 默认使用该字段补长
*/
protected static final String KEY_PADDING = "FOLLOW YOUR HEART. YOU CAN DO BEST THAN ANY ONE.";
/**
* 传入密文, 返回明文
* @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);
}
}
@@ -0,0 +1,54 @@
package com.alibaba.druid.support.security.decryptor;
import com.alibaba.druid.pool.DecryptException;
import com.alibaba.druid.pool.SensitiveParameters;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* AES 解密器
*
* @author Jonas Yang
*/
public class AesDecrypter extends AbstractDecrypter {
private final static int KEY_LENGTH = 16; //128
private volatile SecretKeySpec spec;
public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
SecretKeySpec spec = getSpec();
if (spec == null) {
throw new DecryptException("No key.");
}
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);
}
}
protected SecretKeySpec getSpec() {
return this.spec;
}
public void setKey(String key) {
if (key == null) {
throw new IllegalArgumentException("The key cannot be null");
}
if (key.length() < 16) {
key = KEY_PADDING.substring(0, KEY_LENGTH - key.length()) + key;
}
this.spec = new SecretKeySpec(key.getBytes(), "AES");
}
}
@@ -0,0 +1,46 @@
package com.alibaba.druid.support.security.decryptor;
import com.alibaba.druid.pool.DecryptException;
import com.alibaba.druid.pool.Decrypter;
import com.alibaba.druid.pool.SensitiveParameters;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
/**
* 拷贝JBOSS的Blowfish解密
* @author Jonas Yang
*/
public class BlowfishDecrypter implements Decrypter {
public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
byte[] kbytes = "jaas is the way".getBytes();
SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
BigInteger n = new BigInteger(parameters.getPassword(), 16);
byte[] encoding = n.toByteArray();
if (encoding.length % 8 != 0) {
int length = encoding.length;
int newLength = ((length / 8) + 1) * 8;
int pad = newLength - length; //number of leading zeros
byte[] old = encoding;
encoding = new byte[newLength];
for (int i = old.length - 1; i >= 0; i--) {
encoding[i + pad] = old[i];
}
}
try {
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedBytes = cipher.doFinal(encoding);
return new SensitiveParameters(parameters.getUrl(), parameters.getUsername(), new String(decryptedBytes));
} catch (Exception e) {
throw new DecryptException("Failed to decrypt JDBC password.", e);
}
}
}
@@ -0,0 +1,63 @@
package com.alibaba.druid.support.security.decryptor;
import com.alibaba.druid.pool.DecryptException;
import com.alibaba.druid.pool.SensitiveParameters;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
/**
* DES 解密器
*
* @author Jonas Yang
*/
public class DesDecrypter extends AbstractDecrypter {
private static final int KEY_LENGTH = 24;
private volatile SecretKey key;
public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
SecretKey key = getKey();
if (key == null) {
throw new DecryptException("No key.");
}
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, this.key);
String passwordPlainText = decrypt(cipher, parameters.getPassword());
return new SensitiveParameters(parameters.getUrl(), parameters.getUsername(), passwordPlainText);
} catch (Exception e) {
throw new DecryptException("Failed to decrypt JDBC parameters", e);
}
}
public void setKey(String key) {
if (key == null) {
throw new IllegalArgumentException("The key cannot be null");
}
if (key.length() < 24) {
key = KEY_PADDING.substring(0, KEY_LENGTH - key.length()) + key;
}
try {
byte[] keyBytes = key.getBytes();
DESKeySpec spec = new DESKeySpec(keyBytes);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
this.key = factory.generateSecret(spec);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot get key.", e);
}
}
protected SecretKey getKey() {
return this.key;
}
}
@@ -0,0 +1,114 @@
package com.alibaba.druid.support.security.decryptor;
import com.alibaba.druid.pool.DecryptException;
import com.alibaba.druid.pool.SensitiveParameters;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.util.Base64;
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.CertificateFactory;
import java.security.spec.X509EncodedKeySpec;
import java.security.cert.Certificate;
/**
* RSA 解密器, 支持证书
*
*/
public class RsaDecrypter extends AbstractDecrypter {
public static Log log = LogFactory.getLog(RsaDecrypter.class);
private volatile PublicKey publicKey;
public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
PublicKey publicKey = getPublicKey();
if (publicKey == null) {
throw new DecryptException("No public Key.");
}
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 JDBC parameters", e);
}
}
protected PublicKey getPublicKey() {
return this.publicKey;
}
public void setPublicKeyString(String publicKeyString) {
if (publicKeyString == null) {
throw new IllegalArgumentException("The publicKeyString cannot be null");
}
try {
byte[] publicKeyBytes = Base64.base64ToByteArray(publicKeyString);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.publicKey = keyFactory.generatePublic(x509KeySpec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
}
}
public void setPublicKeyFile(String publicKeyFile) {
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");
this.publicKey = factory.generatePublic(spec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {}
}
}
}
public void setX509File(String x509File) {
FileInputStream in = null;
try {
in = new FileInputStream(x509File);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate cer = factory.generateCertificate(in);
this.publicKey = cer.getPublicKey();
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.warn("Cannot close file [" + x509File + "] in initialized public key.");
}
}
}
}
}
@@ -0,0 +1,76 @@
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) {
System.err.println("错误: 密码不能为空!");
return null;
}
String keyString = new String(keyChars);
if (keyString == null) {
return null;
}
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;
}
}
@@ -0,0 +1,15 @@
package com.alibaba.druid.support.security.tool;
/**
* @author Jonas Yang
*/
public interface Action {
/**
* DES, AES 密钥都有长度限制, 如果不够, 默认使用该字段补长
*/
public String KEY_PADDING = "FOLLOW YOUR HEART. YOU CAN DO BEST THAN ANY ONE.";
public String getId();
public void execute();
}
@@ -0,0 +1,32 @@
package com.alibaba.druid.support.security.tool;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* @author Jonas Yang
*/
public class AesAction extends AbstractAction {
public AesAction() {
super(6, 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 new BASE64Encoder().encode(encryptedBytes);
}
}
@@ -0,0 +1,47 @@
package com.alibaba.druid.support.security.tool;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
/**
* @author Jonas Yang
*/
public class BlowfishAction implements Action {
@Override
public String getId() {
return "Blowfish";
}
public void execute() {
System.out.println();
String plainString = System.console().readLine("[Blowfish]请输入要需要加密的明文: ");
if (plainString == null) {
System.err.println("输入不能为空.");
return;
}
try {
String encryptedString = encrypt(plainString);
System.out.println("请记住以下的密文, 长度为[" + encryptedString.length() + "].");
System.out.println();
System.out.println(encryptedString);
System.out.println();
} catch (Exception e) {
System.out.println("加密出错.");
e.printStackTrace();
}
}
private String encrypt(String plainString) throws Exception {
byte[] kbytes = "jaas is the way".getBytes();
SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encoding = cipher.doFinal(plainString.getBytes());
BigInteger n = new BigInteger(encoding);
return n.toString(16);
}
}
@@ -0,0 +1,37 @@
package com.alibaba.druid.support.security.tool;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
/**
* @author Jonas Yang
*/
public class DesAction extends AbstractAction {
public DesAction() {
super(6, 24, "DES");
}
@Override
public String getId() {
return "DES";
}
public String encrypt(String keyString, String plainString) throws Exception {
DESKeySpec spec = new DESKeySpec(keyString.getBytes());
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKey key = factory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(plainString.getBytes());
return new BASE64Encoder().encode(encryptedBytes);
}
}
@@ -0,0 +1,56 @@
package com.alibaba.druid.support.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;
}
}
}
@@ -0,0 +1,51 @@
package com.alibaba.druid.support.security.tool;
/**
* <pre>
* Druid 提供的加密工具. 如下运行
*
* <b>java -cp druid-x.x.x.jar com.alibaba.druid.support.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(Integer.toString(i++), new DesAction());
help.addAction(Integer.toString(i++), new AesAction());
help.addAction(Integer.toString(i++), new BlowfishAction());
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("**************************************");
}
}
@@ -0,0 +1,25 @@
package com.alibaba.druid.support.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("**************************************");
}
}
@@ -0,0 +1,132 @@
package com.alibaba.druid.support.security.tool;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* @author Jonas Yang
*/
public class RsaAction implements Action {
@Override
public String getId() {
return "RSA";
}
public void execute() {
help();
}
private void help() {
System.out.println();
System.out.println("RSA 选项: ");
System.out.println("1. 通过私钥加密");
System.out.println("2. 创建 RSA 密钥");
String input = System.console().readLine("[RSA]请输入选项: ");
if (input == null || input.length() == 0) {
System.err.println("不是一个合法的输入.");
}
if ("1".equals(input)) {
encrypt();
return;
}
if ("2".equals(input)) {
generateKeys();
}
}
private void encrypt() {
String filePath = System.console().readLine("[RSA]请输入私钥文件路径: ");
if (!new File(filePath).exists()) {
System.err.println("文件不存在[" + filePath + "]");
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream in = null;
try {
in = new FileInputStream(filePath);
byte[] b = new byte[512/8];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
} catch (Exception e) {
System.err.println("读取文件出错. " + e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
byte[] key = out.toByteArray();
String plainString = System.console().readLine("[RSA]请输入要需要加密的明文: ");
try {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = factory.generatePrivate(spec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedBytes = cipher.doFinal(plainString.getBytes());
String encryptedString = new BASE64Encoder().encode(encryptedBytes);
System.out.println("请记住以下的密文, 长度为[" + encryptedString.length() + "].");
System.out.println();
System.out.println(encryptedString);
System.out.println();
} catch (Exception e) {
System.err.println("加密错误. ");
e.printStackTrace();
}
}
private void generateKeys() {
String input = System.console().readLine("[RSA]请输入输出路径: ");
if (input == null) {
input = "";
}
try {
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(1024, new SecureRandom());
KeyPair pair = gen.generateKeyPair();
String privateKeyPath = input + ".private.key";
saveKey(pair.getPrivate(), privateKeyPath);
String publicKeyPath = input + ".public.key";
saveKey(pair.getPublic(), publicKeyPath);
System.out.println("Private Key: " + privateKeyPath);
System.out.println("public Key: " + publicKeyPath);
System.out.println();
} catch (Exception e) {
System.err.println("[RSA]创建 RSA 密钥出错. " + e.getMessage());
}
}
private void saveKey(Key key, String path) throws Exception {
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(key.getEncoded());
} finally {
if (out != null) {
out.close();
}
}
}
}