欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA 3DES加密 ECB模式 ZeroPadding填充

程序员文章站 2024-03-14 15:26:04
...

3DES ECB ZeroPadding

前言

因金融机构支付相关的接口需要对敏感数据加密,采用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;
	}
}

至此,终于解决了。心情瞬间舒畅!