Skip to content

SPI SM Crypto Extension 说明文档

功能概述

spi-sm-crypto-extension 是一个 Keycloak 扩展,实现了 Keycloak 的加密 SPI,集成了中国国家标准密码算法(国密算法),包括 SM2(椭圆曲线公钥密码算法)、SM3(密码杂凑算法)和 SM4(分组密码算法)。

该扩展为 Keycloak 提供了符合中国密码标准的加密能力,使 Keycloak 能够在需要使用国密算法的场景中正常工作,如政府、金融等对密码算法有特殊要求的领域。

界面预览

SPI SM Crypto Extension

SPI SM Crypto Extension 信息

技术支持

核心组件

1. 国密算法工具类

SM2Util

功能:提供 SM2 椭圆曲线密码算法的实现,用于数字签名和密钥交换。

主要方法

  • generateKeyPair():生成 SM2 密钥对
  • sign(byte[] privateKey, byte[] data):使用 SM2 私钥对数据进行签名
  • verify(byte[] publicKey, byte[] data, byte[] signature):使用 SM2 公钥验证签名
  • encrypt(byte[] publicKey, byte[] data):使用 SM2 公钥加密数据
  • decrypt(byte[] privateKey, byte[] encryptedData):使用 SM2 私钥解密数据

SM3Util

功能:提供 SM3 密码杂凑算法的实现,用于数据完整性校验和消息认证。

主要方法

  • digest(byte[] data):对数据进行 SM3 哈希计算
    • 参数data - 要哈希的数据字节数组
    • 返回值:SM3 哈希值字节数组(32字节)
    • 功能:使用 SM3 算法对输入数据进行哈希计算

SM4Util

功能:提供 SM4 分组密码算法的实现,用于数据加密和解密。

主要方法

  • encrypt(byte[] key, byte[] data):使用 SM4 密钥加密数据
  • decrypt(byte[] key, byte[] encryptedData):使用 SM4 密钥解密数据
  • generateKey():生成 SM4 密钥

2. 加密提供者实现

SMContentEncryptionProvider

功能:实现了 Keycloak 的 ContentEncryptionProvider 接口,用于内容加密,支持 JWE(JSON Web Encryption)。

主要方法

  • jweEncryptionProvider():返回 JWE 加密提供者
    • 返回值JWEEncryptionProvider 实例,当前返回 null,需要实现完整的 SM4 JWE 加密提供者

SMContentEncryptionProviderFactory

功能:实现了 Keycloak 的 ContentEncryptionProviderFactory 接口,负责创建 SMContentEncryptionProvider 实例。

主要方法

  • getId():返回提供者工厂的ID
    • 返回值:字符串,固定为 "bima-sm-content-encryption"
  • create(KeycloakSession session):创建内容加密提供者实例
    • 参数session - Keycloak 会话对象
    • 返回值SMContentEncryptionProvider 实例

SMHashProvider

功能:实现了 Keycloak 的 HashProvider 接口,用于哈希计算,使用 SM3 算法。

主要方法

  • hash(byte[] data):对数据进行哈希计算,使用 SM3 算法
    • 参数data - 要哈希的数据字节数组
    • 返回值:SM3 哈希值字节数组

SMHashProviderFactory

功能:实现了 Keycloak 的 HashProviderFactory 接口,负责创建 SMHashProvider 实例。

主要方法

  • getId():返回提供者工厂的ID
    • 返回值:字符串,固定为 "bima-sm-hash"
  • create(KeycloakSession session):创建哈希提供者实例
    • 参数session - Keycloak 会话对象
    • 返回值SMHashProvider 实例

SMKeyProvider

功能:实现了 Keycloak 的 KeyProvider 接口,用于 SM 密钥管理。

主要方法

  • getKey(String kid):根据密钥ID获取密钥
  • getKeys():获取所有密钥
  • createKey(RealmModel realm, Map<String, String> config):创建新密钥

SMKeyProviderFactory

功能:实现了 Keycloak 的 KeyProviderFactory 接口,负责创建 SMKeyProvider 实例。

主要方法

  • getId():返回提供者工厂的ID
    • 返回值:字符串,固定为 "bima-sm-key"
  • create(KeycloakSession session, ComponentModel model):创建密钥提供者实例
    • 参数
      • session - Keycloak 会话对象
      • model - 组件模型对象
    • 返回值SMKeyProvider 实例

SMSignatureProvider

功能:实现了 Keycloak 的 SignatureProvider 接口,用于 SM2 数字签名。

主要方法

  • signer(String algorithm, Key key):获取签名器上下文
  • verifier(String algorithm, Key key):获取验证器上下文

SMSignatureProviderFactory

功能:实现了 Keycloak 的 SignatureProviderFactory 接口,负责创建 SMSignatureProvider 实例。

主要方法

  • getId():返回提供者工厂的ID
    • 返回值:字符串,固定为 "bima-sm-signature"
  • create(KeycloakSession session):创建签名提供者实例
    • 参数session - Keycloak 会话对象
    • 返回值SMSignatureProvider 实例

SMSignatureSignerContext

功能:实现了 Keycloak 的 SignatureSignerContext 接口,用于 SM2 签名上下文。

主要方法

  • sign(byte[] data):对数据进行签名
  • sign(InputStream data):对输入流数据进行签名

SMSignatureVerifierContext

功能:实现了 Keycloak 的 SignatureVerifierContext 接口,用于 SM2 签名验证上下文。

主要方法

  • verify(byte[] data, byte[] signature):验证签名
  • verify(InputStream data, byte[] signature):验证输入流数据的签名

配置与使用

1. 开发环境搭建

前提条件

  • JDK 11 或更高版本
  • Maven 3.6 或更高版本
  • Keycloak 17.0.0 或更高版本
  • Bouncy Castle 库 1.68 或更高版本

编译构建

bash
cd spi-sm-crypto-extension
mvn clean package

编译完成后,在 target 目录下会生成 spi-sm-crypto-extension-1.0.0.jar 文件。

2. 部署

将编译好的 JAR 文件放入 Keycloak 的 standalone/deployments 目录。

3. 依赖配置

该扩展依赖于 Bouncy Castle 库来实现国密算法,因此需要确保 Keycloak 环境中包含 Bouncy Castle 依赖。

方法一:将 Bouncy Castle JAR 文件放入 Keycloak 的 standalone/lib 目录

  1. 下载 Bouncy Castle 库(bcprov-jdk15on-1.68.jar 和 bcpkix-jdk15on-1.68.jar)
  2. 将这两个 JAR 文件放入 Keycloak 的 standalone/lib 目录
  3. 重启 Keycloak 服务器

方法二:在 Maven 依赖中添加 Bouncy Castle

pom.xml 文件中添加 Bouncy Castle 依赖:

xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.68</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.68</version>
</dependency>

4. 使用

Keycloak 会自动检测并加载该扩展,之后可以在 Keycloak 的配置中使用国密算法。

4.1 使用 SM3 哈希算法

在 Keycloak 中,可以通过以下方式使用 SM3 哈希算法:

  1. 登录 Keycloak 管理控制台
  2. 进入 Realm 设置 → 认证 → 密码策略
  3. 在哈希算法中选择 "bima-sm-hash"
  4. 保存配置

4.2 使用 SM2 数字签名

在 Keycloak 中,可以通过以下方式使用 SM2 数字签名:

  1. 登录 Keycloak 管理控制台
  2. 进入 Realm 设置 → 密钥
  3. 添加新的密钥,选择 "bima-sm-key" 类型
  4. 配置密钥参数并保存
  5. 在需要使用签名的地方选择该密钥

4.3 使用 SM4 加密

当前实现中,SM4 加密功能尚未完全实现,需要完成 SMContentEncryptionProvider.jweEncryptionProvider() 方法的实现。

扩展与定制

完善 SM4 加密实现

当前 SMContentEncryptionProvider.jweEncryptionProvider() 方法返回 null,需要实现完整的 SM4 JWE 加密提供者。

实现示例

java
@Override
public JWEEncryptionProvider jweEncryptionProvider() {
    return new JWEEncryptionProvider() {
        @Override
        public String getAlgorithm() {
            return "A256GCM"; // 暂时使用 A256GCM,后续可实现 SM4 特定算法
        }
        
        @Override
        public byte[] encrypt(byte[] data, byte[] key, byte[] iv, byte[] aad) throws Exception {
            // 使用 SM4 算法加密数据
            return SM4Util.encrypt(key, data);
        }
        
        @Override
        public byte[] decrypt(byte[] encryptedData, byte[] key, byte[] iv, byte[] aad) throws Exception {
            // 使用 SM4 算法解密数据
            return SM4Util.decrypt(key, encryptedData);
        }
        
        @Override
        public int getKeyLength() {
            return 16; // SM4 密钥长度为 16 字节
        }
        
        @Override
        public int getIVLength() {
            return 16; // SM4 IV 长度为 16 字节
        }
    };
}

扩展其他国密算法

可以根据需要扩展其他国密算法,如 SM9(标识密码算法)等。

实现示例

  1. 创建 SM9 工具类
java
public class SM9Util {
    // SM9 算法实现
    public static byte[] sign(byte[] privateKey, byte[] data) {
        // 实现 SM9 签名
        return new byte[0];
    }
    
    public static boolean verify(byte[] publicKey, byte[] data, byte[] signature) {
        // 实现 SM9 签名验证
        return false;
    }
}
  1. 创建 SM9 签名提供者
java
public class SM9SignatureProvider implements SignatureProvider {
    @Override
    public SignatureSignerContext signer(String algorithm, Key key) {
        // 实现 SM9 签名器
        return null;
    }
    
    @Override
    public SignatureVerifierContext verifier(String algorithm, Key key) {
        // 实现 SM9 验证器
        return null;
    }
    
    @Override
    public void close() {
        // 清理资源
    }
}
  1. 创建 SM9 签名提供者工厂
java
public class SM9SignatureProviderFactory implements SignatureProviderFactory {
    public static final String ID = "bima-sm9-signature";
    
    @Override
    public String getId() {
        return ID;
    }
    
    @Override
    public SignatureProvider create(KeycloakSession session) {
        return new SM9SignatureProvider();
    }
    
    @Override
    public void init(Config.Scope config) {
        // 初始化配置
    }
    
    @Override
    public void postInit(KeycloakSessionFactory factory) {
        // 后初始化
    }
    
    @Override
    public void close() {
        // 清理资源
    }
}
  1. 注册服务

META-INF/services/org.keycloak.crypto.SignatureProviderFactory 文件中添加:

cc.bima.keycloak.extension.sm.SM9SignatureProviderFactory

优化现有实现

1. 性能优化

SM3Util 优化示例

java
public class SM3Util {
    static {
        // 注册 Bouncy Castle 提供者
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }
    
    // 线程本地变量,避免重复创建 SM3Digest 对象
    private static final ThreadLocal<SM3Digest> digestThreadLocal = ThreadLocal.withInitial(SM3Digest::new);
    
    public static byte[] digest(byte[] data) {
        try {
            SM3Digest digest = digestThreadLocal.get();
            digest.reset(); // 重置摘要对象
            digest.update(data, 0, data.length);
            byte[] result = new byte[digest.getDigestSize()];
            digest.doFinal(result, 0);
            return result;
        } catch (Exception e) {
            throw new RuntimeException("SM3 digest failed", e);
        }
    }
}

2. 安全性增强

SM4Util 安全性增强示例

java
public class SM4Util {
    // 使用安全的随机数生成器
    private static final SecureRandom secureRandom = new SecureRandom();
    
    public static byte[] generateKey() {
        byte[] key = new byte[16];
        secureRandom.nextBytes(key);
        return key;
    }
    
    public static byte[] encrypt(byte[] key, byte[] data) {
        try {
            // 使用 CCM 模式增强安全性
            SM4Engine engine = new SM4Engine();
            CCMParameters params = new CCMParameters(new KeyParameter(key), 128);
            engine.init(true, params);
            
            byte[] encrypted = new byte[engine.getOutputSize(data.length)];
            int len = engine.processBytes(data, 0, data.length, encrypted, 0);
            engine.doFinal(encrypted, len);
            
            return encrypted;
        } catch (Exception e) {
            throw new RuntimeException("SM4 encryption failed", e);
        }
    }
    
    public static byte[] decrypt(byte[] key, byte[] encryptedData) {
        try {
            SM4Engine engine = new SM4Engine();
            CCMParameters params = new CCMParameters(new KeyParameter(key), 128);
            engine.init(false, params);
            
            byte[] decrypted = new byte[engine.getOutputSize(encryptedData.length)];
            int len = engine.processBytes(encryptedData, 0, encryptedData.length, decrypted, 0);
            engine.doFinal(decrypted, len);
            
            return decrypted;
        } catch (Exception e) {
            throw new RuntimeException("SM4 decryption failed", e);
        }
    }
}

代码结构

spi-sm-crypto-extension/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── cc/bima/keycloak/extension/sm/
│   │   │       ├── SM2Util.java                           # SM2 算法工具类
│   │   │       ├── SM3Util.java                           # SM3 算法工具类
│   │   │       ├── SM4Util.java                           # SM4 算法工具类
│   │   │       ├── SMContentEncryptionProvider.java       # 内容加密提供者
│   │   │       ├── SMContentEncryptionProviderFactory.java # 内容加密提供者工厂
│   │   │       ├── SMHashProvider.java                    # 哈希提供者
│   │   │       ├── SMHashProviderFactory.java             # 哈希提供者工厂
│   │   │       ├── SMKeyProvider.java                     # 密钥提供者
│   │   │       ├── SMKeyProviderFactory.java              # 密钥提供者工厂
│   │   │       ├── SMSignatureProvider.java               # 签名提供者
│   │   │       ├── SMSignatureProviderFactory.java        # 签名提供者工厂
│   │   │       ├── SMSignatureSignerContext.java          # 签名上下文
│   │   │       └── SMSignatureVerifierContext.java        # 签名验证上下文
│   │   └── resources/
│   │       └── META-INF/
│   │           └── services/
│   │               ├── org.keycloak.crypto.ContentEncryptionProviderFactory # 内容加密提供者服务配置
│   │               ├── org.keycloak.crypto.HashProviderFactory              # 哈希提供者服务配置
│   │               ├── org.keycloak.crypto.SignatureProviderFactory          # 签名提供者服务配置
│   │               └── org.keycloak.keys.KeyProviderFactory                 # 密钥提供者服务配置
│   └── test/
│       └── java/
│           └── cc/bima/keycloak/extension/sm/            # 测试代码
└── pom.xml                                               # Maven 配置文件

部署与维护

1. 部署方式

1.1 标准部署

将编译好的 JAR 文件放入 Keycloak 的 standalone/deployments 目录,Keycloak 会自动部署该扩展。

1.2 Docker 部署

如果使用 Docker 运行 Keycloak,可以将扩展 JAR 文件和 Bouncy Castle JAR 文件复制到容器中。

Dockerfile 示例

dockerfile
FROM quay.io/keycloak/keycloak:17.0.0

# 复制 Bouncy Castle JAR 文件
COPY bcprov-jdk15on-1.68.jar /opt/keycloak/standalone/lib/
COPY bcpkix-jdk15on-1.68.jar /opt/keycloak/standalone/lib/

# 复制国密算法扩展 JAR 文件
COPY spi-sm-crypto-extension-1.0.0.jar /opt/keycloak/standalone/deployments/

ENV KEYCLOAK_ADMIN=admin
ENV KEYCLOAK_ADMIN_PASSWORD=admin

ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start-dev"]

2. 监控与日志

2.1 日志配置

在 Keycloak 的 standalone/configuration/standalone.xml 文件中,可以配置日志级别:

xml
<logger category="cc.bima.keycloak.extension.sm" level="info"/>

2.2 监控指标

可以通过 Keycloak 的管理 API 或 JMX 监控扩展的运行状态。

3. 故障排除

3.1 常见问题

问题原因解决方案
扩展未加载Bouncy Castle 库未正确部署确保 Bouncy Castle JAR 文件已放入 Keycloak 的 standalone/lib 目录
SM3 哈希算法未生效未在 Keycloak 管理控制台中配置登录管理控制台,在密码策略中选择 "bima-sm-hash"
SM2 签名失败密钥配置错误检查密钥配置是否正确,确保使用的是 SM2 密钥
SM4 加密未生效未实现完整的 JWE 加密提供者实现 SMContentEncryptionProvider.jweEncryptionProvider() 方法

性能优化

  1. 缓存机制:对常用的加密对象(如 SM3Digest)使用线程本地变量或对象池,避免重复创建
  2. 批处理:对批量数据进行处理时,使用单次哈希计算而非多次调用
  3. 并行处理:对大量数据的加密或哈希计算,考虑使用并行处理
  4. 密钥管理:使用密钥缓存,避免频繁加载密钥
  5. 算法优化:根据具体使用场景选择合适的加密模式和参数

注意事项

  1. Bouncy Castle 库版本:确保使用与国密算法实现兼容的 Bouncy Castle 版本,建议使用 1.68 或更高版本
  2. 密钥长度和加密模式:生产环境中应根据安全要求配置适当的密钥长度和加密模式
  3. 性能测试:建议对国密算法的使用进行性能测试,确保满足业务需求
  4. 合规性:遵循国家密码管理相关规定,确保算法使用符合要求
  5. 安全性:定期更新密钥,使用安全的随机数生成器,避免密钥泄露
  6. 兼容性:注意 Keycloak 版本与扩展版本的兼容性,避免因版本不匹配导致的问题
  7. 备份:定期备份密钥材料,防止密钥丢失
  8. 审计:对加密操作进行审计,记录关键的加密/解密操作
  9. 测试:在生产环境部署前,进行充分的测试,确保国密算法的正确实现
  10. 文档:维护详细的文档,记录密钥管理流程和算法使用情况

免责声明

本项目基于 GitHub 开源软件进行定制化开发,旨在为企业和开发者提供更便捷的项目基座解决方案。使用本项目时,请务必了解以下免责声明:

  1. 开源基础:本项目基于 GitHub 开源软件构建,遵循原开源协议的相关规定。
  2. 定制开发:我们对原开源软件进行了定制和扩展,以提供更优质的开发体验和功能支持。
  3. 责任限制:对于使用本项目可能产生的任何直接或间接的经济损失、数据丢失或其他问题,北京必码科技工作室不承担任何责任。
  4. 使用建议:在生产环境中使用本项目前,请进行充分的测试和验证,确保其符合您的业务需求和安全要求。
  5. 技术支持:我们提供技术支持服务,但不保证解决所有可能出现的问题。
  6. 合规使用:用户应确保在使用本项目时遵守相关法律法规和行业规范,不得用于任何违法或违规用途。