前言
因金融机构支付相关的接口需要对敏感数据加密,采用3DES加密。刚开始以为这个加密网上找一大堆,没想到一无所获。
解决
由于不熟悉这个加密,对数据的位数不足填充不是特别了解,功力不够。最后终于在github找到了。
github地址:https://github.com/quanqiang-li/knowledge
相关类
加密类 CipherUtil
.
//
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* 公钥加密,私钥解密
* <p>
* 证书算法: x509Certificate.getSigAlgName() 用来加签/验签 <br>
* keytool -genkeypair -sigalg SHA1withRSA <br>
* 密钥算法:key.getAlgorithm() 用来加密/解密 创建密钥对的时候指定的 <br>
* keytool -genkeypair -keyalg RSA<br>
*
* @author aisino 参考顺序
* MessageDigestUtil--》KeyToolUtil--》SignatureUtil--》CipherUtil
*/
public class CipherUtil {
public static final String CHARSET_UTF8 = "UTF-8";
public static final String Algorithm_AES = "AES";
public static final String Algorithm_DES = "DES";
public static final String Algorithm_DESede = "DESede";// 也叫3DES/模式/填充,默认等同于DESede/ECB/PKCS5Padding
public static final String Algorithm_RSA = "RSA";
public static final String Algorithm_PBEWithMD5AndDES = "PBEWithMD5AndDES";
public static void main(String[] args) {
// String msg = "你好hello12a c";
// Certificate certificate = KeyToolUtil.getCert("carl.crt");
// String encryptMsg = encryptMsg(msg, certificate);
// System.out.println(certificate.getPublicKey().getAlgorithm() + "加密串:" + encryptMsg);
// PrivateKey privateKey = KeyToolUtil.getPrivateKeyFromKeyStore("JKS", "carl.keystore", "qq2476056494", "carl",
// "qq2476056494");
// String decryptMsg = decryptMsg(encryptMsg, privateKey);
// System.out.println(privateKey.getAlgorithm() + "解密后:" + decryptMsg);
// System.out.println("==============以下是采用密钥串来加解密的===================");
// // 3des的密钥串只能是24个字节的长度,采用utf编码则24个字母数字,或者8个中文
// String key = "abcdefghijklmnopqrstuvwx";
// String encryptMsg2 = encryptMsg(key, Algorithm_DESede, msg);
// System.out.println("\n" + Algorithm_DESede + "加密串:" + encryptMsg2);
// String decryptMsg2 = decryptMsg(key, Algorithm_DESede, encryptMsg2);
// System.out.println("\n" + Algorithm_DESede + "解密后:" + decryptMsg2);
System.out.println("==============以下是3des/ecb/zeroPadding加解密的===================");
String deskey = "111111111111111111111111";
String encryptWithDESCEBZeroPading = encryptWithDESCEBZeroPading(deskey, "511113197310071016");
System.out.println("\n加密串" + encryptWithDESCEBZeroPading);
String decryptWithDESCEBZeroPading = decryptWithDESCEBZeroPading(deskey, encryptWithDESCEBZeroPading);
System.out.println("\n解密串" + decryptWithDESCEBZeroPading);
}
/**
* 加密数据,证书里公钥的算法
*
* @param msg
* 数据
* @param certificate
* 证书(公钥)
* @return base64编码处理的字符串
*/
public static String encryptMsg(String msg, Certificate certificate) {
try {
// 使用证书公钥的算法,生成密钥对的时候指定的算法
Cipher instance = Cipher.getInstance(certificate.getPublicKey().getAlgorithm());
// ENCRYPT_MODE 加密模式
instance.init(Cipher.ENCRYPT_MODE, certificate);
instance.update(msg.getBytes(CHARSET_UTF8));
byte[] doFinal = instance.doFinal();
return Base64.getEncoder().encodeToString(doFinal);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密数据,私钥的算法
*
* @param encryptMsg
* 加密后字符串,且base64编码处理过
* @param privateKey
* 私钥
* @return 解密后的数据
*/
public static String decryptMsg(String encryptMsg, PrivateKey privateKey) {
try {
// 使用私钥的算法,生成密钥对的时候指定的算法
Cipher instance = Cipher.getInstance(privateKey.getAlgorithm());
// DECRYPT_MODE 解密模式
instance.init(Cipher.DECRYPT_MODE, privateKey);
instance.update(Base64.getDecoder().decode(encryptMsg));
byte[] doFinal = instance.doFinal();
return new String(doFinal, CHARSET_UTF8);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* 没有证书的情况下,采用指定密钥串加密
*
* @param key
* 密钥
* @param Algorithm
* 加密算法
* @param msg
* 消息
* @return Base64编码后的字符串
*/
public static String encryptMsg(String key, String Algorithm, String msg) {
try {
// 生成密钥
byte[] bytes = key.getBytes(CHARSET_UTF8);
System.out.println("密钥字节长度:" + bytes.length);
SecretKey deskey = new SecretKeySpec(bytes, Algorithm);
// 加密工具
Cipher c1 = Cipher.getInstance(Algorithm);
// 加密
c1.init(Cipher.ENCRYPT_MODE, deskey);
byte[] msgBytes = msg.getBytes(CHARSET_UTF8);
System.out.println("原数据字节如下:");
for (int i = 0; i < msgBytes.length; i++) {
System.out.print(msgBytes[i] + "\t");
}
// FIXME 此处不能使用update
byte[] doFinal = c1.doFinal(msgBytes);
System.out.println("\n加密后的字节如下:");
for (int i = 0; i < doFinal.length; i++) {
System.out.print(doFinal[i] + "\t");
}
return Base64.getEncoder().encodeToString(doFinal);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 3des ecb 0填充,利用NoPading,自行补0
*
* @param key
* 密钥串,必须为24长度字符串
* @param msg
* 消息
* @return Base64编码后的字符串
*/
public static String encryptWithDESCEBZeroPading(String key, String msg) {
try {
// 生成密钥
byte[] bytes = key.getBytes(CHARSET_UTF8);
System.out.println("密钥字节长度:" + bytes.length);
SecretKey deskey = new SecretKeySpec(bytes, "DESede");
// 加密工具
Cipher c1 = Cipher.getInstance("DESede/ECB/NoPadding");
// 加密
c1.init(Cipher.ENCRYPT_MODE, deskey);
byte[] msgBytes = msg.getBytes(CHARSET_UTF8);
System.out.println("原数据字节如下:");
for (int i = 0; i < msgBytes.length; i++) {
System.out.print(msgBytes[i] + "\t");
}
// FIXME 自行补位,达到8字节的倍数
int remainder = msgBytes.length % 8;
if (0 != remainder) {
int oldLength = msgBytes.length;
// 1.扩展自身长度
msgBytes = Arrays.copyOf(msgBytes, msgBytes.length + 8 - remainder);
// 2.填充扩展内容为0
Arrays.fill(msgBytes, oldLength, msgBytes.length, (byte) 0);
}
// FIXME 此处不能使用update,自行补位,
byte[] doFinal = c1.doFinal(msgBytes);
System.out.println("\n加密后的字节如下:");
for (int i = 0; i < doFinal.length; i++) {
System.out.print(doFinal[i] + "\t");
}
return Base64.getEncoder().encodeToString(doFinal);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 没有证书的情况下,使用密钥串解密
*
* @param key
* 密钥串
* @param Algorithm
* 算法
* @param encryptMsg
* 已加密的字节经base64编码后的字符串
* @return 原始数据,utf-8编码
*/
public static String decryptMsg(String key, String Algorithm, String encryptMsg) {
try {
// 生成密钥
byte[] bytes = key.getBytes(CHARSET_UTF8);
System.out.println("密钥字节长度:" + bytes.length);
SecretKey deskey = new SecretKeySpec(bytes, Algorithm);
// 初始工具
Cipher instance = Cipher.getInstance(Algorithm);
// DECRYPT_MODE 解密模式
instance.init(Cipher.DECRYPT_MODE, deskey);
byte[] encryptMsgBytes = Base64.getDecoder().decode(encryptMsg);
System.out.println("待解密的字节如下:");
for (int i = 0; i < encryptMsgBytes.length; i++) {
System.out.print(encryptMsgBytes[i] + "\t");
}
// FIXME 此处不能使用update
byte[] doFinal = instance.doFinal(encryptMsgBytes);
System.out.println("\n解密后的字节为如下:");
for (int i = 0; i < doFinal.length; i++) {
System.out.print(doFinal[i] + "\t");
}
return new String(doFinal, CHARSET_UTF8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 3des ecb 0填充,解密;利用NoPading,自行去除填充的0
*
* @param key
* 密钥字符串,必须是24长度
* @param encryptMsg
* base64的密文
* @return
*/
public static String decryptWithDESCEBZeroPading(String key, String encryptMsg) {
try {
// 生成密钥
byte[] bytes = key.getBytes(CHARSET_UTF8);
System.out.println("密钥字节长度:" + bytes.length);
SecretKey deskey = new SecretKeySpec(bytes, "DESede");
// 初始工具
Cipher instance = Cipher.getInstance("DESede/ECB/NoPadding");
// DECRYPT_MODE 解密模式
instance.init(Cipher.DECRYPT_MODE, deskey);
byte[] encryptMsgBytes = Base64.getDecoder().decode(encryptMsg);
System.out.println("待解密的字节如下:");
for (int i = 0; i < encryptMsgBytes.length; i++) {
System.out.print(encryptMsgBytes[i] + "\t");
}
// FIXME 此处不能使用update
byte[] doFinal = instance.doFinal(encryptMsgBytes);
// 去除填充的0,倒数第一个不为0的位置,copy到另一个数组
int zeroIndex = doFinal.length;
for (int i = doFinal.length - 1; i > 0; i--) {
if (doFinal[i] == (byte) 0) {
zeroIndex = i;
} else {
break;
}
}
doFinal = Arrays.copyOf(doFinal, zeroIndex);
System.out.println("\n解密后的字节为如下:");
for (int i = 0; i < doFinal.length; i++) {
System.out.print(doFinal[i] + "\t");
}
return new String(doFinal, CHARSET_UTF8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
依赖的工具类 KeyToolUtil
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import sun.security.tools.keytool.Main;
/**
* 密钥和证书管理工具
*
* @author aisino
* 参考顺序
* MessageDigestUtil--》KeyToolUtil--》SignatureUtil--》CipherUtil
*/
public class KeyToolUtil {
public static void main(String[] args) throws Exception {
//命令:keytool -list -keystore test.keystore
//这里省略开头的keytool,这里Main本身就是keytool工具,使用空格拆分的命令
//printAllCommand();
//printCommandHelp("export");
//生成密钥对,会提示输入密码,机构;域名liquanqiang.aisino.com来倒着填,按地址倒着填,keyalg 加解密用,sigalg 加签验签用,这两个算法要属于一个体系内
//execCommand("-genkeypair -alias carl1 -keyalg RSA -sigalg SHA1withRSA -keysize 1024 -validity 365 -keystore carl1.keystore");
//列出密钥库中的条目
//execCommand("-list -keystore carl1.keystore");
//从密钥库导出公钥到证书
//execCommand("-export -alias carl1 -file carl1.crt -keystore carl1.keystore");
//打印证书内容
execCommand("-printcert -file carl1.crt");
// System.out.println(getStrFromKey(getPublicKeyFromCert(getCertFromKeyStore("JKS", "carl.keystore", "qq2476056494", "carl"))));
// System.out.println(getStrFromKey(getPublicKeyFromCert(getCert("carl.crt"))));
// System.out.println(getStrFromKey(getPrivateKeyFromKeyStore("JKS", "carl.keystore", "qq2476056494", "carl","qq2476056494")));
}
/**
* 打印keytool的所有命令
*/
public static void printAllCommand(){
try {
Main.main(("-help").split("\\s"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打印keytool的指定命令帮助信息
* @param commandName
*/
public static void printCommandHelp(String commandName){
try {
Main.main(("-"+commandName + " -help").split("\\s"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 通用的命令执行
* @param command
*/
public static void execCommand(String command){
try {
Main.main((command).split("\\s"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取密钥库中的证书
* @param keyStoreType 密钥库的条目类型,-list可以查看
* @param keyStorePath 密钥库的文件路径
* @param keyStorePass 密钥库密码
* @param alias 条目别名
* @return
*/
public static Certificate getCertFromKeyStore(String keyStoreType,String keyStorePath,String keyStorePass,String alias){
try {
//因为生成证书的类型为JKS 也有其他的格式 -list可以查看密钥库的条目类型
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
//读取keystore文件转换为keystore密钥库对象
FileInputStream fis = new FileInputStream(keyStorePath);
//该密钥库的密码
keyStore.load(fis, keyStorePass.toCharArray());
fis.close();
// 从keystore中获取证书然后进一步获取公钥
Certificate certificate = keyStore.getCertificate(alias);
return certificate;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 直接读取证书
* @param certPath 证书目录
* @return
*/
public static Certificate getCert(String certPath){
try {
//证书类型只支持X.509
CertificateFactory factory = CertificateFactory.getInstance("X.509");
// 取得证书文件流
FileInputStream inputStream = new FileInputStream(certPath);
// 生成证书
Certificate certificate = factory.generateCertificate(inputStream);
inputStream.close();
return certificate;
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 从证书读取公钥,java只支持X509Certificate
* @param certificate
* @return
*/
public static PublicKey getPublicKeyFromCert(Certificate certificate){
//读取公钥对象
X509Certificate x509Certificate = (X509Certificate) certificate;
PublicKey publicKey = certificate.getPublicKey();
System.out.println("证书算法:" + x509Certificate.getSigAlgName());
System.out.println("公钥算法:" + publicKey.getAlgorithm());
return publicKey;
}
/**
* 获取密钥库中的私钥内容
* @param keyStoreType 密钥库的条目类型,keytool -list可以查看
* @param keyStorePath 密钥库的文件路径
* @param keyStorePass 密钥库密码
* @param alias 条目别名
* @param aliasPass 条目密码
* @return
*/
public static PrivateKey getPrivateKeyFromKeyStore(String keyStoreType,String keyStorePath,String keyStorePass,String alias,String aliasPass){
try {
//因为生成证书的类型为JKS 也有其他的格式 -list可以查看密钥库的条目类型
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
//读取keystore文件转换为keystore密钥库对象
FileInputStream fis = new FileInputStream(keyStorePath);
//该密钥库的密码
keyStore.load(fis, keyStorePass.toCharArray());
fis.close();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, aliasPass.toCharArray());
return privateKey;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
return null;
}
/**
* 从key获取密钥串
* @param key
* @return base64编码处理后的字符串
*/
public static String getStrFromKey(Key key){
//64位编码处理
return Base64.getEncoder().encodeToString(key.getEncoded());
}
/**
* 从字符串还原回key
* @param encodedKey base64编码处理后的字符串
* @param algorithm 指定算法
* @return 还原的key
*/
public static SecretKey getKeyFromStr(String encodedKey,String algorithm){
// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, algorithm);
return originalKey;
}
}
至此,终于解决了。心情瞬间舒畅!