/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* 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 jef.tools.security;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import jef.common.log.LogUtil;
import jef.tools.Assert;
import jef.tools.IOUtils;
import jef.tools.io.ReaderInputStream;
import jef.tools.reflect.BeanUtils;
import jef.tools.reflect.MethodEx;
import jef.tools.support.JefBase64;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
public class EncrypterUtil {
// static{
// Security.addProvider(new
// org.bouncycastle.jce.provider.BouncyCastleProvider());//添加PKCS7Padding支持
// }
/**
* 算法分成这些大类
*/
public enum AlgorithmType {
KeyGenerator, // 密钥生成算法(KeyGenerator)
KeyFactory, // 密钥生成算法(KeyFactory)
KeyAgreement, // 公开密钥协商算法
Cipher, // 加密/解密算法
KeyStore, // 密钥编码/解码算法
MessageDigest, // 消息摘要算法
Signature, // 数字签名算法
AlgorithmParameterGenerator, AlgorithmParameters, CertificateFactory, CertPathBuilder, CertPathValidator, Mac, Policy, SaslClientFactory, SaslServerFactory, SecretKeyFactory, SecureRandom, SSLContext, TerminalFactory, TransformService, TrustManagerFactory, XMLSignatureFactory
}
/**
* 安全算法元数据查询,查询类安全服务所支持的全部算法服务
*
* @param type
* @return
*/
public static Service[] getSupportedAlgorithm(AlgorithmType type) {
List<Service> list = new ArrayList<Service>();
for (Provider p : Security.getProviders()) {
for (Service s : p.getServices()) {
if (type == null || s.getType().equals(type.name())) {
list.add(s);
}
}
}
return list.toArray(new Service[0]);
}
/**
* 安全算法元数据查询,得到目前已支持的算法名称(含别名)
*
* @param type
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static String[] getSupportedAlgorithmName(AlgorithmType type)
throws Exception {
Service[] algoms = getSupportedAlgorithm(type);
MethodEx m = BeanUtils.getCompatibleMethod(Service.class, "getAliases");
List<String> names = new ArrayList<String>();
for (Service s : algoms) {
names.add(s.getAlgorithm());
List<String> alias = ((List<String>) m.invoke(s));
names.addAll(alias);
}
return names.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
}
/**
* 安全算法元数据查询,根据指定的算法,获得所有使用该算法的服务
*
* @param name
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static Service[] getAlgorithmService(String name) throws Exception {
List<Service> list = new ArrayList<Service>();
MethodEx m = BeanUtils.getCompatibleMethod(Service.class, "getAliases");
for (Provider p : Security.getProviders()) {
for (Service s : p.getServices()) {
boolean flag = false;
if (s.getAlgorithm().equalsIgnoreCase(name)) {
flag = true;
} else {
List<String> aliases = ((List<String>) m.invoke(s));
for (String alias : aliases) {
if (alias.equalsIgnoreCase(name)) {
flag = true;
break;
}
}
}
if (flag) {
list.add(s);
}
}
}
return list.toArray(new Service[0]);
}
/**
* 将密码转换为DESKey
*
* @param password
* @return
*/
public static final SecretKey toDESKey(String password) {
byte[] bb = password.getBytes();
Assert.isTrue(bb.length > 7, "the secretKey for DES must be 8 bytes at least.");
try {
KeySpec keySpec = new DESKeySpec(bb);
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(
keySpec);
return key;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 将密码转换为3DESKey
*
* @param password
* @return
*/
public static final SecretKey toDESedeKey(String password) {
byte[] bb = password.getBytes();
Assert.isTrue(bb.length > 23, "the secretKey for 3DES must be 24 bytes at least.");
try {
KeySpec keySpec = new DESedeKeySpec(bb);
SecretKey key = SecretKeyFactory.getInstance("DESede")
.generateSecret(keySpec);
return key;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 根据输入的数据,给出对应的密钥对象(RAW KEY)
*
* @param value
* @param algom
* @return
*/
public static final SecretKey toSecretKey(byte[] value, String algorithm) {
if (algorithm.indexOf("/") > -1) {
// 某些时候指定的算法不能直接作为key的算法。
// 比如Chiper算法指定为DES/ECB/NOPADDING时,key的算法必须是DES,否则会报错。
return new RawSecretKeySpec(value, algorithm,
StringUtils.substringBefore(algorithm, "/"));
} else {
return new SecretKeySpec(value, algorithm);
}
}
// TODO 自动填充空格,防止key太短的问题
static class RawSecretKeySpec extends SecretKeySpec {
String chiperAlgomrithm;
public RawSecretKeySpec(byte[] abyte0, String s) {
super(abyte0, s);
}
public RawSecretKeySpec(byte[] abyte0, int i, int j, String s) {
super(abyte0, i, j, s);
}
public RawSecretKeySpec(byte[] abyte0, String algorithm, String keyAlgom) {
super(abyte0, keyAlgom);
this.chiperAlgomrithm = algorithm;
}
private static final long serialVersionUID = 3865527433731129466L;
}
/**
* 生成指定算法的KEY
*
* @param value
* @param algom
* 对称加密算法
* @return
*/
public static final SecretKey generateKey(String algom,int keylength) {
try {
KeyGenerator keygen = KeyGenerator.getInstance(algom);
keygen.init(keylength);
SecretKey deskey = keygen.generateKey();
return deskey;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
/**
* 生成指定算法的KEY对
*
* @param algom
* 非对称加密算法 DSA RSA等
* @return
*/
public static final KeyPair generateKeyPair(String algom) {
try {
java.security.KeyPairGenerator keygen = java.security.KeyPairGenerator
.getInstance(algom);
SecureRandom secrand = new SecureRandom();
secrand.setSeed("\n".getBytes()); // 初始化随机产生器
// 密钥长度:其范围必须在 512 到 1024 之间,且必须为 64 的倍数
keygen.initialize(1024, secrand); // 初始化密钥生成器
// keygen.initialize(512);
KeyPair keys = keygen.generateKeyPair(); // 生成密钥组
return keys;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
/**
* 得到基于DSA算法的数字签名
*
* @param in
* @return
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws SignatureException
*/
public static byte[] getDSASign(InputStream in, PrivateKey key) {
try {
java.security.Signature signet = java.security.Signature
.getInstance("DSA");
signet.initSign(key);
byte[] b = new byte[1024];
int len;
while ((len = in.read(b)) != -1) {
signet.update(b, 0, len);
}
byte[] signed = signet.sign();
return signed;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
/**
* 得到基于DSA算法的数字签名
*
* @param in
* @return
*/
public static byte[] getDSASign(Reader in, PrivateKey key) {
return getDSASign(new ReaderInputStream(in), key);
}
/**
* 得到基于DSA算法的数字签名
*
* @param in
* @return
*/
public static byte[] getDSASign(String in, PrivateKey key) {
return getDSASign(new StringReader(in), key);
}
/**
* 校验数据和签名是否一致
*
* @param in
* @param key
* @param sign
* @return
*/
public static boolean verifyDSASign(InputStream in, byte[] sign,
PublicKey key) {
Signature signetcheck;
try {
signetcheck = java.security.Signature.getInstance("DSA");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
try {
signetcheck.initVerify(key);
byte[] b = new byte[1024];
int len;
while ((len = in.read(b)) != -1) {
signetcheck.update(b, 0, len);
}
return signetcheck.verify(sign);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (SignatureException e) {
return false;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 校验数据和签名是否一致
*
* @param in
* @param key
* @param sign
* @return
*/
public static boolean verifyDSASign(Reader in, byte[] sign, PublicKey key) {
return verifyDSASign(new ReaderInputStream(in), sign, key);
}
/**
* 校验数据和签名是否一致
*
* @param in
* @param key
* @param sign
* @return
*/
public static boolean verifyDSASign(String in, byte[] sign, PublicKey key) {
return verifyDSASign(new StringReader(in), sign, key);
}
/**
* 将KEY保存为文件
*
* @param key
* @param file
* 指定要保持的文件
* @return File,实际保存的文件,不会覆盖已有的文件,会自动改名。
*/
public static File saveKey(SecretKey key, File file) {
try {
File f = IOUtils.escapeExistFile(file);
IOUtils.saveAsFile(f, false, key.getEncoded());
return f;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 载入x509的密钥
*
* @param f
* @param algom
* 算法,可用 getSupportedAlgorithmName (AlgorithmType.KeyFactory)查询
* @param isPublic
* true生成公钥对象,false生成私钥对象
* @return
*/
public static Key loadX509Key(File f, String algom, boolean isPublic) {
try {
byte[] keyData = IOUtils.toByteArray(f);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyData);
KeyFactory keyFactory = KeyFactory.getInstance(algom);
Key result = (isPublic) ? keyFactory.generatePublic(keySpec)
: keyFactory.generatePrivate(keySpec);
return result;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
/**
* 载入PKCS8的密钥
*
* @param f
* @param algom
* 算法
* @param isPublic
* true生成公钥对象,false生成私钥对象
* @return
*/
public static Key loadPKCS8Key(File f, String algom, boolean isPublic) {
try {
byte[] keyData = IOUtils.toByteArray(f);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyData);
KeyFactory keyFactory = KeyFactory.getInstance(algom);
Key result = (isPublic) ? keyFactory.generatePublic(keySpec)
: keyFactory.generatePrivate(keySpec);
return result;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
/**
* 使用指定的KEY来解密
*
* @param schemaIndex
* @param key
* 密钥,包含算法.对称非对称均可,非对称要注意使用公钥来解密
* @return AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
* iterationCount);
*/
public static byte[] decrypt(InputStream in, Key key,
AlgorithmParameterSpec spec) {
Assert.notNull(key, "SecretKey Key must not null");
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
Cipher c1 = Cipher
.getInstance((key instanceof RawSecretKeySpec) ? ((RawSecretKeySpec) key).chiperAlgomrithm
: key.getAlgorithm());
c1.init(Cipher.DECRYPT_MODE, key, spec);
byte[] b = new byte[1024];
int len;
while ((len = in.read(b)) != -1) {
out.write(c1.update(b, 0, len));
}
out.write(c1.doFinal());
return out.toByteArray();
} catch (GeneralSecurityException e) {
LogUtil.exception(e);
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 使用指定的KEY来解密
*
* @param schemaIndex
* @param key
* 密钥,包含算法.对称非对称均可,非对称要注意使用公钥来解密
* @return AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
* iterationCount);
*/
public static byte[] decrypt(InputStream in, SecretKey key) {
return decrypt(in, key, null);
}
/**
* 使用指定的KEY来解密
*
* @param schemaIndex
* @param key
* 密钥,包含算法.对称非对称均可,非对称要注意使用公钥来解密
* @return AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
* iterationCount);
*/
public static byte[] decrypt(byte[] in, SecretKey key) {
return decrypt(new ByteArrayInputStream(in), key);
}
public static byte[] decrypt(byte[] in, PrivateKey key) {
return decrypt(new ByteArrayInputStream(in), key,null);
}
/**
* 使用指定的KEY来解密
*
* @param schemaIndex
* @param key
* 密钥,包含算法.对称非对称均可,非对称要注意使用公钥来解密
* @return AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
* iterationCount);
*/
public static byte[] decrypt(Reader in, SecretKey key) {
return decrypt(new ReaderInputStream(in), key);
}
/**
* 使用指定的KEY来解密
*
* @param schemaIndex
* @param key
* 密钥,包含算法.对称非对称均可,非对称要注意使用公钥来解密
* @return AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
* iterationCount);
*/
public static byte[] decrypt(String in, SecretKey key) {
return decrypt(new StringReader(in), key);
}
/**
* 获得基于密码的加密器实例
*
* @return
*/
public static PasswordEncryptor getDefaultPBE() {
return new PasswordEncryptor(new byte[] { (byte) 0xA9, (byte) 0x9B,
(byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35,
(byte) 0xE3, (byte) 0x03 }, 19);
}
/**
* 获得基于密码的加密器实例
*
* @param salt
* 盐
* @param iterationCount
* 迭代次数
* @return
*/
public static PasswordEncryptor getPBE(byte[] salt, int iterationCount) {
return new PasswordEncryptor(salt, iterationCount);
}
/**
* 使用指定的KEY来加密
*
* @param schemaIndex
* @param key
* 密钥,包含算法,对称或非对称均可,非对称要注意使用私钥加密 支持的算法名称如下:DES DESede(TripleDES)
* DESedeWrap PBEWithMD5AndDES(OID.1.2.840.113549.1.5.3 或
* 1.2.840.113549.1.5.3) PBEWithMD5AndTripleDES
* PBEWithSHA1AndRC2_40(OID.1.2.840.113549.1.12.1.6 或
* 1.2.840.113549.1.12.1.6)
* PBEWithSHA1AndDESede(OID.1.2.840.113549
* .1.12.1.3,1.2.840.113549.1.12.1.3) Blowfish AES(Rijndael)
* AESWrap RC2 ARCFOUR(RC4) RSA RSA/ECB/PKCS1Padding RSA 共计15种
* @return
*/
public static byte[] encrypt(InputStream in, Key key,
AlgorithmParameterSpec spec,boolean padding) {
Assert.notNull(key, "SecretKey Key must not null");
String alg=(key instanceof RawSecretKeySpec) ? ((RawSecretKeySpec) key).chiperAlgomrithm
: key.getAlgorithm();
if(padding && alg.indexOf('/')==-1){
alg=alg+"/ECB/PKCS1Padding";
}
try {
Cipher c1 = Cipher.getInstance(alg);
c1.init(Cipher.ENCRYPT_MODE, key, spec);
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
byte[] b = new byte[1024];
int len;
while ((len = in.read(b)) != -1) {
out.write(c1.update(b, 0, len));
}
out.write(c1.doFinal());
return out.toByteArray();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static final IvParameterSpec DEFAULT_IvParameterSpec = new IvParameterSpec(
"12345678".getBytes());
/**
* 使用指定的KEY来加密
*
* @param schemaIndex
* @param key
* 密钥,包含算法,对称或非对称均可,非对称要注意使用私钥加密
* @return
*/
public static byte[] encrypt(InputStream in, SecretKey key) {
return encrypt(in, key,null,false);
}
/**
* 使用指定的KEY来加密
*
* @param data
* @param key
* 密钥,包含算法,对称或非对称均可,非对称要注意使用私钥加密
* @return
*/
public static byte[] encrypt(byte[] data, SecretKey key) {
return encrypt(new ByteArrayInputStream(data), key);
}
public static byte[] encrypt(byte[] data, PublicKey key) {
return encrypt(new ByteArrayInputStream(data), key,null,true);
}
/**
* 使用指定的KEY来加密
*
* @param data
* @param key
* 密钥,包含算法,对称或非对称均可,非对称要注意使用私钥加密
* @return
*/
public static byte[] encrypt(Reader data, SecretKey key,AlgorithmParameterSpec... param) {
if(param!=null && param.length>0){
return encrypt(new ReaderInputStream(data), key,param[0],false);
}else{
return encrypt(new ReaderInputStream(data), key,null,false);
}
}
/**
* 使用指定的KEY来加密
*
* @param data
* @param key
* 密钥,包含算法,对称或非对称均可,非对称要注意使用私钥加密
* @return
*/
public static byte[] encrypt(String data, SecretKey key,AlgorithmParameterSpec... param) {
return encrypt(new StringReader(data), key,param);
}
public interface Transport{
void send(byte[] encoded) throws IOException;
}
/**
* 将byte[]包装成InputStream对象
*
* @param data
* @return
*/
public static InputStream wrap(byte[] data) {
return new ByteArrayInputStream(data);
}
/**
* 将String包装成InputStream对象
*
* @param data
* @param code
* @return
*/
public static InputStream wrap(String data, String code) {
try {
return new ByteArrayInputStream(data.getBytes(code));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* 字节数据编码为可见字符(base64)
*
* @param in
* @return
*/
public static String base64Encode(byte[] in) {
return JefBase64.encode(in);
}
/**
* 字节数据编码为可见字符(base64)
*
* @param in
* @return
*/
public static String base64Encode(InputStream in) {
try {
return JefBase64.encode(IOUtils.toByteArray(in));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 可见字符(base64)解码为字节组(base64)
*
* @param in
* @return
*/
public static byte[] base64Decode(CharSequence in) {
return JefBase64.decodeFast(in);
}
/**
* 字节组编码为可见字符(base64)
*
* @param data
* @return
*/
public static byte[] base64Decode(byte[] data) {
return JefBase64.decodeFast(data,0,data.length);
}
private EncrypterUtil() {
};
static {
Provider p = (Provider) BeanUtils
.newInstance("com.sun.crypto.provider.SunJCE");
if (p == null) {
LogUtil.show("Current JDK is not sun JDK compatible...");
} else {
Security.addProvider(p);
}
if(p==null){
p = (Provider) BeanUtils
.newInstance("com.ibm.crypto.provider.IBMJCE");
if (p == null) {
LogUtil.show("Current JDK is not IBM JDK compatible...");
} else {
Security.addProvider(p);
}
}
}
/**
* 3des密码加密解密程序的DES实现。(允许使用8位密码)
* 3DES:是在DES的基础上采用三重DES,即用两个56位的密钥K1,K2,发送方用K1加密
* ,K2解密,再使用K1加密.接收方使用K1解密,K2加密,再使用K1解密, 3DES实现: 主要有CBC,ECB实现,java默认是ECB
* 对于待加密解密的数据的填充方式:NoPadding、PKCS5Padding、SSL3Padding,默认填充方式为,PKCS5Padding
* java中要求key的size必须为24;对于CBC模式下的向量iv的size两者均要求必须为8,
* 所以在处理8字节的key的时候,直接使用DES三次, 加密时候为(加密--解密--加密),解密时候为:(解密--加密--解密)
*/
public static class DESede {
public static byte[] encrypt(byte[] msg, byte[] pass) throws Exception {
byte[] input = msg;
byte[] keyBytes = pass;
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
Cipher cipher = Cipher.getInstance("DES/ECB/NOPADDING"); // TripleDES/ECB/NoPadding
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return cipherText;
}
public static byte[] decrypt(byte[] s, byte[] k) throws Exception {
byte[] input = s;
byte[] keyBytes = k;
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
Cipher cipher = Cipher.getInstance("DES/ECB/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return cipherText;
}
}
public static String decryptString(byte[] data, SecretKey key,
String charset) {
try {
if (charset == null) {
return new String(decrypt(data, key));
} else {
return new String(decrypt(data, key), charset);
}
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}