国密算法简介
国密算法是我国自主研发创新的一套数据加密处理系列算法。从SM1-SM4分别实现了对称、非对称、摘要等算法功能。
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为 128 位。
- SM1
对称加密。算法 不公开 ,加密强度与AES相当。调用该算法时,需要通过加密芯片的接口进行调用。目前在软件开发过程中使用较少。 - SM2
非对称加密。该算法已公开。算法基于ECC,签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。软件开发过程中应用广泛。 - SM3
消息摘要。该算法已公开,校验结果为256位。算法的安全性要高于 MD5 算法和 SHA-1 算法。在报文签名方面应用广泛。 - SM4
无线局域网标准的分组数据算法。软件开发领域应用较少。
国密改造
国密改造是相对国际算法来说的。
密码算法是保障信息安全的核心技术,尤其是最关键的银行业核心领域长期以来都是沿用 3DES、SHA-1、RSA 等国际通用的密码算法体系及相关标准。
2010年底,国家密码管理局公布了我国自主研制的“椭圆曲线公钥密码算法”(SM2算法)。为保障重要经济系统密码应用安全,国家密码管理局于2011年发布了《关于做好公钥密码算法升级工作的通知》,要求“自2011年3月1日起,在建和拟建公钥密码基础设施电子认证系统和密钥管理系统应使用国密算法。自2011年7月1日起,投入运行并使用公钥密码的信息系统,应使用SM2算法。”
银行内部大多有成熟厂商提供的硬件加密机完成国密(及国际)算法的加解密过程。加密机作为专业设备,加解密效率高,稳定可靠。
但随着银行向产业生态的拓展,经常遭遇硬件加解密不方便的场景。
java 纯软件实现国密算法
java 开源社区的 bouncycastle
提供了大量的哈希算法和加密算法;hutool
的 hutool-crypto
模块对加解密方法做了很好的封装,使用起来非常的方便。
本文借助这两个开源库完成相关功能。
引入开源库
如果是使用 maven 管理的项目,引入方式如下:
org.bouncycastle
bcprov-jdk15on
${bcprov.version}
cn.hutool
hutool-crypto
${hutool.version}
版本号选择最新即可,如:
1.68
5.6.3
如果不是使用 maven 管理的项目,请自行完成依赖引入。
SM2 加解密
引入上述的 jar 包后, SM2 加密已经变得非常容易。
/**
* 创建 SM2 公私钥对
*/
private static void createSM2Key() {
SM2 sm2 = new SM2();
System.out.println("公钥:" + sm2.getPublicKeyBase64());
System.out.println("私钥:" + sm2.getPrivateKeyBase64());
}
/**
* SM2 公钥加密
*
* @param publicKey
* @param plainText
*/
public static String sm2Encrypt(String publicKey, String plainText) {
System.out.println("SM2 公钥加密");
System.out.println("明文:" + plainText);
SM2 sm2 = new SM2(null, publicKey);
String ciphertext = sm2.encryptBase64(plainText, KeyType.PublicKey);
System.out.println("密文:" + ciphertext);
return ciphertext;
}
/**
* SM2 私钥解密
*
* @param privateKey
* @param ciphertext
*/
public static String sm2Decrypt(String privateKey, String ciphertext) {
System.out.println("SM2 私钥解密");
System.out.println("密文:" + ciphertext);
SM2 sm2 = new SM2(privateKey, null);
String plainText = sm2.decryptStr(ciphertext, KeyType.PrivateKey);
System.out.println("明文:" + plainText);
return plainText;
}
PS: 示例中的公私钥及密文均使用 base64 进行编码。如果有其他编码需求,调用其他方法即可。
SM3 计算摘要
SM3 的使用更加简单。
private static final byte[] SM3_SALT = "AABBCC112233".getBytes();
// SM3 取摘要
SM3 sm3 = new SM3(SM3_SALT);
System.out.println(sm3.digestHex(plainText));
PS:摘要算法的盐值可根据需要自行替换。SM3 亦可计算文件摘要,调用同类其他方法即可。
javascript + java 纯软件实现 SM2 加解密
非对称加解密实际应用场景中最常见的就是用户口令加密。
用户在前端页面(h5+js)输入口令,使用 js 完成口令加密,通过 ajax 请求上送密文至服务端,服务端解密校验后完成用户认证。
具体过程为:
- js 使用公钥通过 SM2 算法对口令加密,上送密文至服务端;
- 服务端使用私钥通过 SM2 算法对密文解密;
- 服务端使用 SM3 算法(或其他不可逆算法)计算摘要与数据库中存储密码进行比对。(原因是数据库不允许存储用户明文密码,一般使用非可逆算法加密后存储)
此时即要求前端加密算法的实现与后端解密算法的实现一致。
在 javascript 中继续使用开源库完成实现 SM2 算法加密,本次使用 crypto-js.js
.
引入 js 加密库
根据前端项目的依赖管理方式,自行引入即可。js 源文件可从下文参考链接获取,包含crypto-js.js
和sm2.js
。
完成加密
调用 sm2.js 中预置的方法即可完成加密
sm2Encrypt('明文', '公钥', 1);
注意:
- 此处为与上一章节 java 实现保持一致,公钥使用 base64 编码。
在 sm2.js 中完成 base64 编码转换为 Hex 编码。
// base64转Hex,若秘钥是hex格式则无需进行转换
var pubkeyHex = "04"+CryptoJS.enc.Hex.stringify(CryptoJS.enc.Base64.parse(publickey));
如果使用 Hex 编码秘钥,
var pubkeyHex = publickey;
生成 Hex 编码的java 代码为:
/**
* 创建 SM2 公私钥对
*/
private static void createSM2KeyForJs() {
SM2 sm2 = new SM2();
System.out.println("公钥:" + sm2.getPublicKeyBase64());
System.out.println("公钥 Hex(用于前端加密):" + Hex.toHexString(sm2.getQ(false)));
System.out.println("私钥:" + sm2.getPrivateKeyBase64());
}
- 此处生成的密文为 Hex 编码。
如果想要获取 base64 格式的密文,可使用 sm2.js 中的实现。
// 可通过此方法生成 base64 格式的密文,
var encryptDataBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(encryptDataHex));
java 代码进行解密时密文编码为 base64 和 Hex 都支持(解密前判断了编码类型,分别进行解码)。
参考链接
java 代码参考链接 https://github.com/hou-xx/encryption-and-decryption
的 SmDemo.java
和 SmDemoForJs.java
前端代码(h5+js)参考链接 https://github.com/hou-xx/SM2-front