第1章 国密算法概述与Java开发环境准备

这一章,我们先把地基打牢。国密算法本身不算复杂,但很多人在第一步就被环境折腾得够呛。我之前带团队做金融项目时,就因为JDK版本和Bouncy Castle的兼容性问题浪费了一整天——说白了,就是没提前看版本对应关系。

咱们这课程的目标,是用Java把国密SM2/SM3/SM4封装成一套顺手、可复用的SDK。所以开头先把这些算法的本质讲清楚,再把开发环境配好。走起。

1.1 国密三剑客:SM2/SM3/SM4

国密算法是国家密码管理局发布的标准算法。咱们实际开发中最常碰到的就是这三个:

  • SM2 —— 非对称加密,基于椭圆曲线。主要用来做密钥交换和数字签名。速度比RSA慢一点,但安全强度更高。
  • SM3 —— 密码杂凑算法,输出256位哈希值。类似SHA-256,但国内商用环境必须用它。
  • SM4 —— 对称加密,分组密码。密钥长度128位,适合大批量数据加密。我当年做物联网平台时,设备端的传感器数据就是用SM4加密的,性能完全扛得住。
算法类型密钥长度典型用途
SM2非对称256位签名、密钥交换
SM3哈希256位输出完整性校验、数字摘要
SM4对称128位数据加密、传输加密

一个小经验: 实际项目里SM2和SM4常搭配使用——用SM2加密SM4的密钥,再用SM4加密业务数据。这就是"混合加密"模式,性能和安全性兼顾。

1.2 JDK版本怎么选?

我个人习惯长期使用JDK 11。为什么?它既有LTS的稳定性,又带够了现代Java特性,而且国密SDK的主流实现(如Bouncy Castle)对JDK 11支持得最好。

如果你非要赶时髦用JDK 17或21,也不是不行,但只要注意一点:Bouncy Castle的某些旧版本在JDK 17+下会因为模块化限制报错。我踩过这个坑——当时项目急着上线,升级JDK后BC一直抛InaccessibleObjectException,最后发现是--add-opens参数没配。

避坑: 不要在JDK 8上运行本课程示例——Java 8对国密算法支持极其有限,很多特性需要手动拷贝JCE扩展包,非常麻烦。直接用JDK 11或17。

我推荐两个版本:

  • JDK 11 LTS —— 最稳妥,社区生态成熟。
  • JDK 17 LTS —— 如果你想提前体验密封类、模式匹配等新语法,可以选它,但要记得配好JVM参数。

1.3 项目构建工具与依赖管理

Maven还是Gradle?其实都行。我这章用Maven举例,因为大多数Java企业项目还是Maven居多。但Gradle的配置也大同小异,差别只在语法上。

创建一个最简单的Maven项目,groupId设为com.example,artifactId设为gm-demo。然后我们需要引入两个核心依赖:

  • Bouncy Castle Provider —— 提供国密算法的Java实现。
  • Lombok(可选) —— 减少POJO的样板代码,我习惯加上。

下面是完整的pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>gm-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

Gradle版本(用于参考)

plugins {
    id 'java'
}
repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
}

这里有个细节:Bouncy Castle的artifactId是bcprov-jdk15on,那个"15on"不是JDK版本号,是BC自己的内部版本命名。实际上它支持JDK 8到17。嗯,别被名字误导了。

1.4 验证环境:写个简单的SM3哈希

光配环境不跑一下,心里没底。我们写一个小测试,验证Bouncy Castle能否正常加载国密算法。

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.MessageDigest;
import java.security.Security;

public class Sm3Test {
    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        MessageDigest md = MessageDigest.getInstance("SM3", "BC");
        byte[] digest = md.digest("Hello, 国密!".getBytes("UTF-8"));
        // 转十六进制字符串
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(String.format("%02x", b & 0xff));
        }
        System.out.println(sb.toString());
    }
}

如果输出一个64位的十六进制字符串,说明环境没问题。我第一次跑这个代码时,遇到了NoSuchProviderException——因为忘记加Security.addProvider。你想想看,一个异常卡了10分钟,后来发现是provider没注册。这种低级错误,犯过一次就记住了。

1.5 本章知识结构总览

我把这一章的核心逻辑画成了一张图,帮你快速建立全局感。

第1章知识体系图 第1章 国密算法与Java开发环境 SM2 非对称 SM3 哈希 SM4 对称 JDK 11 / 17 选择 Maven / Gradle 项目创建 编写第一个 SM3 哈希测试程序

一句话总结: 选JDK 11,加Bouncy Castle依赖,然后验证SM3可跑——这就是我们第1章的全部任务。后面的章节会在这个基础上,逐步封装SM2签名验签、SM4加解密,最终拼成一个生产级的国密SDK。