项目最初采用RSA报文加密方式,由于临时需求要将RSA改为国密算法,然后就是我的踩坑之路:
首先,国密算法会用到hutool-all-xxx.jar和bcprov-jdk15on-xxx.jar两个jar包;
这里需要注意:hutool工具包会存在和jdk版本兼容的问题,博主使用的hutool-all-4.6.3需要jdk1.7+的支持,5.x则需要jdk1.8+的支持.
需要了解其他详情可以访问hutool的官网:Hutool — 🍬A set of tools that keep Java sweet.
废话不多说上代码:
public class Sm4Util{
//otherPublickey 对手公匙
//otherPrivatekey 对手私匙
//ownPublickey 己方公匙
//ownPrivatekey 己方私匙
//在两个系统进行加密数据互传时,己方是不可能获取对方的私匙的,同样对方不可能获取己方私匙
//例如,我用对方公匙加密后的内容发送给对手系统时,对方可以用他的私匙解密
//对传来的内容进行加签加密
public static String deliver(String msg,String otherPublickey,String ownPublickey){
return encryptAndSign(msg,otherPublickey,ownPublickey);
}
//对传来的内容进行解签解密
public static String receive(String msg,String ownPrivatekey,String otherPublickey){
return checkSigAndDecrypt(msg,ownPrivatekey,otherPublickey);
}
//验签和解密
public static String checkSignAndDecrypt(String msg,String ownPrivatekey,String
otherPublickey) throws AlipayApiException {
System.out.println("接收到的原始密文:"+msg);
if(!isBlank(msg)){//判断非空
Element e = XmlUtils.getRootElementFromString(msg);
//String content = XmlUtils.getElementValue(e, "response");
String corpCode = XmlUtils.getElementValue(e, "corpCode");
String sign = XmlUtils.getElementValue(e, "sign");
//先解外层非对称加密,用私钥
SM2 sm21 = SmUtil.sm2(ownPrivatekey,null);
String sm21decryptStr= sm21.decryptStr(sign, KeyType.PrivateKey);
System.out.println("解除外层后"+sm21decryptStr);
//再解内层对称加密,
String sSM4Key11 = otherPublickey.substring(0,16);
SymmetricCrypto sm41 = SmUtil.sm4(sSM4Key11.getBytes());
String contentDecryptstr = sm41.decryptStr(sm21decryptStr);
System.out.println("解密解签后----"+contentDecryptstr);
System.out.println(contentDecryptstr);
return contentDecryptstr;
}
return null;
}
//加签加密 这里因为报文是xml格式,所以加上了xml的标签,对手才能成功接收,懒得删了,无视即可
public static String encryptAndSign(String msg, String
otherPublickey,String ownPublickey) throws AlipayApiException {
System.out.println("对称加密加签前数据:"+msg);
StringBuilder sb = new StringBuilder();
if(!isBlank(msg)){
sb.append("<?xml version=\"1.0\" encoding=\"" + "UTF-8" + "\"?>");
sb.append("<lziig>");
//用我方公钥的前16位字符加密内层
String sSM4Key = ownPublickey.substring(0,16);
SymmetricCrypto sm4 = SmUtil.sm4(sSM4Key.getBytes());
String sSM4encrypt = sm4.encryptHex(msg,"UTF-8");
sb.append("<response></response>");
sb.append("<encryption_type>SM4</encryption_type>");
//通过对方公钥获取非对称加密外层
SM2 sm2 = SmUtil.sm2(null,otherPublickey);
//用完整的对方公钥加密数据
String sendJson = sm2.encryptHex(sSM4encrypt, KeyType.PublicKey);
sb.append("<sign>" + sendJson + "</sign>");
sb.append("<sign_type>SM2</sign_type>");
sb.append("</lziig>");
}
return sb.toString();
}
public static boolean isBlank(String str) {
int strLen;
if (str != null && (strLen = str.length()) != 0) {
for(int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(str.charAt(i))) {
return false;
}
}
return true;
} else {
return true;
}
}
}
我将上面的代码在我项目中封装成了一个工具类,在需要用的地方直接调用对应方法。这里的类是为了模仿客户端,测试能否正常加密发送到服务端和从服务端接收解密报文.
还有一点,在用对方公匙加密,对方接收到后要用他自己的私匙加密,对方用我方公匙加密,我方接受时用自己的私匙解密即可,当然这是针对非对称加密,如果是对称加密则简单很多
如有纰漏,还请各位老师指出错误.