SM2国密算法实战指南:从原理到Java实现与问题排查
1. 项目概述:为什么我们需要关注SM2?
如果你是一名开发者,尤其是在处理金融、政务、物联网或者任何对数据安全有高要求的应用场景时,你大概率听说过或者被要求使用国密算法。而在国密算法家族中,SM2非对称加密算法无疑是核心中的核心。它不仅仅是RSA算法的一个“国产替代”,更是在设计之初就考虑了现代密码学攻击手段,在安全性和性能上都有其独到之处。
最近几年,从“固件加密”到“数据安全法”的落地,从企业内部的敏感文件传输到物联网设备的身份认证,SM2的身影越来越常见。网上关于“sm2在线加解密”、“hutool sm2”、“java sm2摘要数据”的搜索热度持续攀升,恰恰说明了开发者们正在积极地将这套标准融入自己的项目。但与此同时,我也看到很多朋友在初次接触时感到困惑:参数怎么生成?加密解密流程到底是怎样的?和RSA用法有什么不同?为什么我的解密结果总是不对?
这篇指南的目的,就是帮你彻底搞懂SM2。我不会只给你一堆代码片段,而是会从原理到实践,从密钥对生成到数据加解密,完整地走一遍。你会明白每一个步骤背后的“为什么”,并掌握如何用Java(以Hutool工具库为例)和纯算法视角两种方式来实现它。无论你是要对接一个已有的国密系统,还是为自己的新产品设计安全模块,这篇文章都能给你提供可直接“抄作业”的解决方案。
2. SM2算法核心原理快速解读
在动手写代码之前,花几分钟理解SM2的基本原理至关重要。这能让你在遇到问题时,不再是盲目地试错,而是能有的放矢地进行排查。
2.1 SM2与RSA的本质区别
很多人习惯用RSA的思维来理解SM2,这是第一个容易踩坑的地方。它们虽然都是非对称加密算法,但根基完全不同。
- RSA:基于大数分解的难题。你的公钥和私钥是两个大素数乘积的衍生物。加密过程本质上是模幂运算。
- SM2:基于椭圆曲线离散对数问题(ECDLP)。它运行在一条精心设计的椭圆曲线上。你的私钥是一个随机大整数,公钥则是私钥与椭圆曲线一个公开基点相乘得到的曲线上的一个点。
这个根本差异带来了几个直观影响:
- 更高的安全强度:在相同的安全级别下(例如128位安全性),SM2所需的密钥长度(256位)远小于RSA(3072位以上)。这意味着SM2的密钥更短,计算更快,存储和传输开销更小。
- 内置数字签名标准:SM2的标准中已经包含了数字签名算法(SM2-with-SM3),而RSA签名则需要搭配PKCS#1等填充方案,设计上更为一体。
- 加密过程包含密钥协商:SM2的加密过程并非简单的“用公钥计算”,它内部融合了密钥协商机制,生成一个临时的会话密钥用于对称加密实际数据。这使其天然具备一些前向安全的特性。
2.2 理解SM2加密解密的核心流程
SM2的加密和解密过程可以概括为以下几步,我尽量用通俗的语言描述:
加密过程(发送方使用接收方的公钥):
- 生成临时密钥对:首先,随机生成一个临时私钥
k及其对应的临时公钥[k]G(G是曲线基点)。 - 计算共享密钥:利用接收方的公钥
P_B和临时私钥k,通过椭圆曲线点乘运算,计算出一个共享点(x, y)。从这个点的坐标衍生出最终的共享密钥(Key Derivation Function, KDF)。 - 加密消息:使用上一步生成的共享密钥,通过一个对称加密算法(如XOR流密码或SM4)来加密实际要发送的明文消息
M,得到密文C2。 - 组装密文:最终的密文由三部分组成:临时公钥
C1(即[k]G的压缩或未压缩形式)、加密消息C2、以及一个用于验证的杂凑值C3(通常是对(x, M)等数据做SM3摘要)。所以完整密文是C1 || C3 || C2。
解密过程(接收方使用自己的私钥):
- 解析密文:从密文中分离出
C1(临时公钥点)、C3(摘要)、C2(密文)。 - 恢复共享密钥:用自己的私钥
d_B与收到的临时公钥C1进行点乘运算,得到同一个共享点(x, y),并衍生出相同的共享密钥。 - 解密消息:用恢复的共享密钥解密
C2,得到明文M‘。 - 验证完整性:用同样的方法计算
M‘和恢复的坐标x的摘要,与收到的C3比对。如果一致,说明密文在传输过程中未被篡改,且解密正确。
注意:这里描述的
C1 || C3 || C2是SM2标准中定义的一种常见格式。在实际实现和某些库中,顺序可能是C1 || C2 || C3。这是导致跨平台、跨库加解密失败的最常见原因之一,务必与你对接的系统或库的文档确认格式。
3. 实战准备:环境与工具选型
理解了原理,我们开始动手。工欲善其事,必先利其器。选择一套成熟、稳定的工具库能避免很多底层坑。
3.1 为什么选择Hutool?
在Java生态中,实现SM2的库有不少,比如Bouncy Castle(BC)这个老牌密码学库。那为什么我这里主要推荐Hutool呢?
- 开箱即用,API友好:BC功能强大但API相对底层,需要开发者对密码学有较深理解才能正确使用。Hutool对BC进行了高级封装,提供了非常简洁的静态方法,如
SmUtil.sm2()、SmUtil.sm4(),几行代码就能完成加解密,极大降低了上手门槛。 - 符合国密标准:Hutool的SM2实现严格遵循《GM/T 0003.2-2012》标准,减少了因实现差异导致的兼容性问题。
- 功能全面:除了基本的加解密,还支持签名验签、密钥格式转换(如PEM、DER)、证书解析等常用功能,一站式解决大部分国密开发需求。
- 活跃的社区:作为国内优秀的工具类库,遇到问题时更容易找到中文资料和社区支持。
当然,如果你的项目对性能有极致要求,或者需要深度定制算法,直接使用Bouncy Castle可能是更优选择。但对于90%的应用场景,Hutool的便利性和可靠性已经足够。
3.2 项目依赖引入
以Maven项目为例,在你的pom.xml中添加以下依赖:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> <!-- 请使用最新稳定版本 --> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15to18</artifactId> <version>1.75</version> <!-- Hutool底层依赖BC,需要显式引入 --> </dependency>如果你使用Gradle,对应的配置是:
implementation 'cn.hutool:hutool-all:5.8.22' implementation 'org.bouncycastle:bcprov-jdk15to18:1.75'添加依赖后,建议先运行一个简单的测试,确保库被正确加载,没有版本冲突。
4. 密钥对生成与管理
安全的起点是密钥。SM2密钥对的生成和管理有几个关键点需要注意。
4.1 生成SM2密钥对
使用Hutool生成密钥对非常简单:
import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.SM2; import java.security.KeyPair; public class Sm2KeyGenDemo { public static void main(String[] args) { // 方法1:使用Hutool快速生成,默认使用BC作为提供者 SM2 sm2 = SmUtil.sm2(); // 获取生成的密钥对 KeyPair keyPair = sm2.getKeyPair(); System.out.println("私钥 (Hex): " + sm2.getPrivateKeyHex()); System.out.println("公钥 (Hex, 未压缩): " + sm2.getPublicKeyHex()); // 方法2:自定义生成(例如,指定特定的曲线参数,虽然SM2曲线是固定的) // 通常不需要,使用默认即可。 } }运行这段代码,你会得到两个十六进制字符串,分别是私钥和公钥。私钥是一个64位的十六进制字符串(对应256位),公钥则长得多(130位十六进制,04开头,表示未压缩的坐标点)。
4.2 密钥格式与存储安全
刚生成的密钥是Java内部的KeyPair对象。在实际项目中,你需要将它们持久化。
- 私钥存储(重中之重):
- 绝对不要以明文形式存储在代码、配置文件或数据库中。
- 推荐做法:使用密码学安全的密钥库(如Java Keystore - JKS,或PKCS#12文件)进行加密存储。访问时通过密码解密。
- 次选方案:如果必须存储为文件,使用强密码对私钥进行加密(例如用AES-256-GCM),并将加密后的密文和盐(Salt)、初始化向量(IV)一起存储。Hutool的
SecureUtil可以辅助完成加密。
// 示例:将私钥加密后存储(简化示例,生产环境需更严谨) import cn.hutool.core.codec.Base64; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; import java.nio.charset.StandardCharsets; String privateKeyHex = sm2.getPrivateKeyHex(); String password = "YourStrongPassword!@#"; byte[] salt = SecureUtil.generateKey("AES").getEncoded(); // 生成盐 // 使用基于密码的密钥派生函数 PBKDF2 生成AES密钥 byte[] aesKey = SecureUtil.generateKey("PBKDF2WithHmacSHA256", password.toCharArray(), salt, 65536, 256).getEncoded(); AES aes = SecureUtil.aes(aesKey); // 加密私钥 byte[] encryptedPrivateKey = aes.encrypt(privateKeyHex.getBytes(StandardCharsets.UTF_8)); // 存储时,需要保存:salt, iv(aes.getIv()), encryptedPrivateKey String dataToStore = Base64.encode(salt) + ":" + Base64.encode(aes.getIv()) + ":" + Base64.encode(encryptedPrivateKey); // 将 dataToStore 写入安全配置文件或数据库- 公钥分发:公钥可以公开。通常以PEM格式(
-----BEGIN PUBLIC KEY-----...)或DER二进制格式分发。Hutool可以方便地进行转换。
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm; import cn.hutool.crypto.asymmetric.AsymmetricCrypto; // 获取PEM格式的公钥 String publicKeyPem = sm2.getPublicKeyBase64(); // Base64编码的DER格式,通常可直接使用 // 或者通过BC库转换为更标准的PEM格式(需额外处理)实操心得:在微服务架构中,可以考虑建立一个统一的“密钥管理服务”(KMS)来集中生成、存储和分发密钥。应用服务通过API向KMS申请加解密操作,而不是本地持有私钥。这大大降低了私钥泄露的风险。
5. 数据加密与解密实现详解
现在,我们进入最核心的环节:用SM2加密一段数据,然后再解密它。
5.1 使用Hutool进行加密解密
Hutool让这个过程变得异常简单。我们假设你已经有了发送方的SM2实例(持有对方公钥)和接收方的SM2实例(持有自己私钥)。
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.SM2; public class Sm2EncryptDecryptDemo { public static void main(String[] args) { String originalText = "这是一段需要加密的敏感数据,比如合同金额或者用户身份证号。"; // **场景模拟:Bob要发送加密消息给Alice** // 1. Alice生成密钥对,并将公钥给Bob SM2 aliceSm2 = SmUtil.sm2(); // Alice的SM2对象,持有自己的私钥和公钥 String alicePublicKeyHex = aliceSm2.getPublicKeyHex(); // 2. Bob用Alice的公钥创建自己的SM2对象(仅用于加密) SM2 bobSm2 = SmUtil.sm2(null, alicePublicKeyHex); // 第一个参数私钥为null,第二个参数是Alice的公钥 // 3. Bob加密数据 // Hutool默认使用 `C1C3C2` 顺序,并使用SM3作为摘要算法 byte[] encryptBytes = bobSm2.encrypt(originalText.getBytes(CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey); String cipherTextHex = HexUtil.encodeHexStr(encryptBytes); System.out.println("加密后的密文 (Hex): " + cipherTextHex); // **现在,密文通过网络传输给了Alice** // 4. Alice用自己的私钥解密(aliceSm2对象持有私钥) byte[] decryptBytes = aliceSm2.decrypt(encryptBytes, KeyType.PrivateKey); // 这里传入byte[] // 或者从十六进制字符串解密 // byte[] decryptBytes = aliceSm2.decrypt(HexUtil.decodeHex(cipherTextHex), KeyType.PrivateKey); String decryptedText = new String(decryptBytes, CharsetUtil.CHARSET_UTF_8); System.out.println("解密后的明文: " + decryptedText); System.out.println("解密是否成功: " + originalText.equals(decryptedText)); } }关键点解析:
SmUtil.sm2():无参创建时,会随机生成新的密钥对。SmUtil.sm2(null, publicKeyHex):用给定的公钥创建实例,此实例只能用于加密和验证签名,不能解密或签名。encrypt(data, KeyType.PublicKey):指定使用公钥加密。Hutool内部已经实现了完整的SM2加密流程(生成临时密钥、计算共享密钥、使用SM4或流密码加密数据、计算C3)。decrypt(data, KeyType.PrivateKey):指定使用私钥解密。内部会解析密文结构、恢复共享密钥、解密数据并验证C3。
5.2 处理不同数据格式与编码
在实际开发中,你处理的数据可能不是简单的UTF-8字符串。
- 加密字节数组:如上例所示,
encrypt和decrypt方法直接操作byte[],这是最通用的方式。可以加密任何二进制数据,如图片、PDF文件流等。 - Base64编码密文:网络传输或文本存储时,二进制密文通常用Base64编码。
// 加密并输出Base64 byte[] encryptBytes = bobSm2.encrypt(originalText.getBytes(CharsetUtil.UTF_8), KeyType.PublicKey); String cipherTextBase64 = Base64.encode(encryptBytes); System.out.println("加密后的密文 (Base64): " + cipherTextBase64); // 从Base64解密 byte[] decryptBytes = aliceSm2.decrypt(Base64.decode(cipherTextBase64), KeyType.PrivateKey);- 处理大文件:非对称加密通常用于加密对称密钥(如一个随机的AES密钥),而不是直接加密大文件。标准做法是:
- 随机生成一个AES密钥(会话密钥)。
- 用SM2公钥加密这个AES密钥。
- 用AES密钥加密大文件。
- 将加密后的AES密钥(SM2密文)和加密后的文件一起发送。 接收方则先用自己的SM2私钥解密出AES密钥,再用AES密钥解密文件。
5.3 密文结构与兼容性深度剖析
这是SM2实现中最容易出错的环节。我们深入看一下Hutool生成的密文结构。
当你用Hex或Base64解码密文后,得到的字节数组并不是一堆乱码,它有严格的结构。根据国标,常见的两种结构是:
- ASN.1 DER编码格式:这是一种复杂的二进制编码格式,包含了
C1,C3,C2以及它们的长度信息,所有内容包裹在一个ASN.1序列中。Bouncy Castle的早期版本和一些硬件加密机可能默认输出这种格式。它的兼容性最好,但体积稍大。 - 简单拼接格式:即
C1 || C3 || C2或C1 || C2 || C3的简单字节拼接。Hutool默认使用的是C1C3C2的简单拼接格式,并且C1是未压缩的公钥点格式(以04开头)。
如何判断和转换?如果你在与第三方系统对接时发现无法解密,首先确认双方的密文格式。Hutool提供了方法进行转换:
import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; // 假设你收到一个第三方系统的密文,但Hutool解不开 // 你可以尝试分析或转换格式(以下代码需要更深入的BC知识,仅供参考思路) SM2 sm2 = SmUtil.sm2(); // ... 获取密文 cipherTextBytes ... // 有时需要手动解析ASN.1 // ASN1Sequence seq = ASN1Sequence.getInstance(cipherTextBytes); // BigInteger x = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue(); // BigInteger y = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue(); // byte[] c3 = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets(); // byte[] c2 = ASN1OctetString.getInstance(seq.getObjectAt(3)).getOctets(); // 更实用的方法是:如果对方系统文档说明了格式,尝试用Hutool的SM2Engine低级API或直接使用BC库按照相同格式组装/解析。避坑指南:在项目初期,与上下游系统(如客户端、服务端、硬件设备)联调时,第一件事就是统一密文格式。最好能互相提供一对测试密钥和测试数据,验证加解密能否成功。把格式(如“使用未压缩C1点+C3+C2的简单拼接,十六进制传输”)明确写入接口文档。
6. 签名与验签实现
数字签名是SM2的另一个核心功能,用于验证数据的完整性和来源真实性。流程是:发送方用私钥签名,接收方用公钥验签。
public class Sm2SignVerifyDemo { public static void main(String[] args) { String data = "这是一份重要电子合同的内容。"; byte[] dataBytes = data.getBytes(CharsetUtil.UTF_8); // 签名方(例如,服务端) SM2 signerSm2 = SmUtil.sm2(); // 签名方持有私钥 // 生成签名(默认使用SM3作为摘要算法) byte[] signatureBytes = signerSm2.sign(dataBytes); String signatureHex = HexUtil.encodeHexStr(signatureBytes); System.out.println("生成的签名 (Hex): " + signatureHex); // 验签方(例如,客户端,持有签名方的公钥) String signerPublicKeyHex = signerSm2.getPublicKeyHex(); SM2 verifierSm2 = SmUtil.sm2(null, signerPublicKeyHex); // 仅用公钥初始化,用于验签 // 验证签名 boolean isValid = verifierSm2.verify(dataBytes, signatureBytes); System.out.println("签名验证结果: " + isValid); // 尝试篡改数据后验签 String tamperedData = "这是一份被篡改的合同内容。"; boolean isValidAfterTamper = verifierSm2.verify(tamperedData.getBytes(CharsetUtil.UTF_8), signatureBytes); System.out.println("篡改后签名验证结果: " + isValidAfterTamper); // 应为 false } }签名注意事项:
- 摘要算法:SM2签名标准强制使用SM3作为摘要算法。你不需要手动先计算SM3,Hutool的
sign方法内部已经处理了。 - 签名结果:SM2签名结果通常是由两个大整数
(r, s)编码而成的字节数组。Hutool输出的是ASN.1 DER编码格式的签名,这是最通用的格式。 - 验签:
verify方法内部会重新计算数据的SM3摘要,然后用公钥验证签名是否匹配。
7. 常见问题排查与性能优化
即使按照指南操作,在实际集成中仍可能遇到问题。这里汇总一些典型场景和解决方法。
7.1 加解密失败问题排查清单
当你的SM2解密或验签失败时,请按以下顺序检查:
密钥是否正确配对?
- 症状:解密失败,或验签失败。
- 检查:确保解密方使用的私钥,正是加密时所用公钥对应的私钥。这是一个常见的低级错误,尤其是在测试时复制粘贴错了密钥。建议写单元测试,用固定的密钥对验证基本功能。
密文/签名格式是否一致?
- 症状:解密时抛出异常,如“Invalid point encoding”或“Malformed ciphertext”。
- 检查:这是最常见的问题。确认双方使用的是相同的密文结构(
C1C3C2还是C1C2C3?C1是压缩格式还是未压缩格式?)。同样,签名是ASN.1 DER格式还是裸的r||s拼接?与对方系统负责人确认标准。
数据编码是否一致?
- 症状:解密出的明文是乱码。
- 检查:加密前和解密后是否使用了相同的字符集(如UTF-8)。加密操作的是字节数组,如果加密时用
getBytes(“GBK”),解密后却用new String(bytes, “UTF-8”),必然乱码。
第三方库或环境问题?
- 症状:在Tomcat、Docker或Android等特定环境下失败。
- 检查:
- JCE无限制强度策略文件:早期Java版本对加密强度有限制,需要手动替换
local_policy.jar和US_export_policy.jar。Java 8以上版本通常已默认支持。 - Bouncy Castle版本冲突:项目中可能引入了多个不同版本的BC库。使用
mvn dependency:tree检查并排除冲突。 - Android平台:Android系统自带的BC库可能被阉割。需要在App中手动引入完整的BC库(
bcprov-jdk15to18),并通过Security.insertProviderAt(new BouncyCastleProvider(), 1)动态注册。
- JCE无限制强度策略文件:早期Java版本对加密强度有限制,需要手动替换
密文在传输过程中被修改?
- 症状:解密失败,但密钥和格式都确认无误。
- 检查:SM2密文中的
C3是完整性校验值。如果传输过程中密文被截断、多出空格或编码转换(如Base64解码错误),会导致解密时杂凑验证不通过。确保网络传输或存储过程是二进制安全的。对于文本传输,使用Base64编码。
7.2 性能考量与最佳实践
SM2虽然比RSA快,但在高并发场景下仍需优化。
密钥复用:不要每次加密都创建新的
SM2对象。对于固定的通信双方,应该将初始化好的SM2实例(包含公钥或私钥)缓存起来,作为单例或放在Spring容器的Bean中重复使用。对象的创建和密钥解析有一定开销。非对称加密只加密密钥:牢记一个原则:非对称加密不应直接用于加密大量数据。正如前面提到的,标准的“混合加密”模式是性能最佳实践。用SM2加密一个随机的对称密钥(如AES-256密钥),再用这个对称密钥去加密实际的大数据。这样既利用了SM2的安全特性,又获得了对称加密的高速度。
签名验签的优化:对于需要频繁签名的数据(如API请求),如果数据本身不变,可以缓存签名结果,避免重复计算。对于验签,如果公钥固定,也可以缓存初始化好的验签实例。
线程安全:Hutool的
SM2对象本身不是线程安全的,因为其内部可能持有状态(如密钥)。在并发环境下,建议为每个线程使用独立的实例,或者在外层进行同步控制。更推荐的做法是使用ThreadLocal来缓存实例。
// 使用ThreadLocal缓存SM2实例示例 public class Sm2ContextHolder { private static final ThreadLocal<SM2> SM2_CACHE = ThreadLocal.withInitial(() -> { // 这里从配置文件或KMS加载公钥 String publicKeyHex = loadPublicKeyFromConfig(); return SmUtil.sm2(null, publicKeyHex); }); public static SM2 getEncryptor() { return SM2_CACHE.get(); } public static void remove() { SM2_CACHE.remove(); } } // 使用时 SM2 sm2 = Sm2ContextHolder.getEncryptor(); byte[] encrypted = sm2.encrypt(data, KeyType.PublicKey);7.3 与前端及其他语言交互
现代应用往往是全栈的,你可能需要Java后端与JavaScript前端、Python数据分析服务或Go微服务进行SM2交互。
前后端加密:浏览器端可以使用
sm-crypto等JavaScript国密库。前后端必须约定:- 相同的椭圆曲线参数(通常都是标准的sm2p256v1)。
- 相同的密文格式(例如,都使用
C1C3C2未压缩点)。 - 相同的编码(后端返回Base64,前端解码后使用)。
- 公钥格式(前端库可能需要PEM格式,后端需提供)。
多服务间交互:在微服务架构中,可以建立一个统一的“密码服务”。该服务提供标准的RESTful API,其他服务通过调用该API来完成SM2加解密、签名验签操作,而不是在每个服务中都嵌入密钥和密码学库。这极大地简化了密钥管理和算法升级。
8. 进阶话题:证书、硬件与合规性
对于金融、政务等安全要求极高的场景,仅有软件实现还不够。
SM2数字证书:实际商业应用中,公钥通常以数字证书的形式分发。证书由可信的证书颁发机构(CA)签发,绑定了公钥和持有者身份。你需要使用BC或Hutool的证书工具来解析X.509证书,提取其中的SM2公钥进行验签或加密。流程是:获取对方证书 -> 验证证书链 -> 提取证书中的公钥 -> 用该公钥进行后续操作。
硬件加密设备(HSM/Smart Card):私钥的生命周期管理最高安全等级是存放在硬件安全模块(HSM)或智能卡中。私钥永远不出硬件,加解密和签名运算在硬件内部完成。Java通过JCE的Provider机制可以接入这些硬件设备(如PKCS#11接口)。这种情况下,你的代码不再直接持有私钥,而是通过
KeyStore加载一个“引用”,所有涉及私钥的操作都由硬件执行。合规性检查:在正式上线前,你的SM2实现可能需要通过国家密码管理局的合规性检测。确保你使用的密码库(如Bouncy Castle)是经过国密局认证的版本,或者使用商用的、已获认证的密码中间件。自行实现的算法核心很难通过检测。
从我个人的经验来看,从理解原理到实现基础功能,再到处理各种兼容性和性能问题,最后到满足高安全场景的合规要求,是一个逐步深入的过程。建议在项目初期就明确安全边界和合规要求,选择合适的工具和架构,能避免后期大量的重构和返工。SM2作为国密体系的基石,掌握其核心应用,对于构建安全可靠的应用系统来说,是一项越来越重要的技能。
