更新记录

1.0.9(2026-06-10)

新增RSA相关 客户端,服务端(java)互通,加解密小程序端暂不支持

1.0.8(2026-06-09)

新增AES 工具

1.0.7(2026-06-08)

readme.md修改

查看更多

平台兼容性

uni-app(5.0)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - -

uni-app x(5.0)

Chrome Safari Android iOS 鸿蒙 微信小程序

其他

多语言 暗黑模式 宽屏模式

sunrains-smutil

点击体验h5端

点击查看安卓端演示安装包

本插件需要依赖 sunrains-common-type 插件 (为sunrains-*类型的插件提供各种type,便于引用与维护)

功能说明

本插件提供SM2、SM3 、 SM4、AES、RSA 客户端(ios,安卓,鸿蒙,微信小程序,h5),服务端(java)SM2、SM3 、 SM4、AES、RSA 算法的跨平台实现,支持以下功能:

服务端(Java)详见 sunrains-java

img.png

SM2 非对称加密算法

  • ✅ SM2 密钥对生成
  • ✅ SM2 数字签名
  • ✅ SM2 签名验证
  • ✅ SM2 数据加密
  • ✅ SM2 数据解密

SM3 密码杂凑算法

  • ✅ SM3 哈希计算(字符串)
  • ✅ HMAC-SM3 密钥哈希
  • ✅ 文件 SM3 哈希
  • ✅ 哈希值验证

SM4 对称加密算法

  • ✅ SM4 密钥生成(含 IV)
  • ✅ SM4 数据加密(CBC 模式)
  • ✅ SM4 数据解密(CBC 模式)

AES 对称加密算法

  • ✅ AES 密钥和 IV 生成
  • ✅ AES 数据加密(CBC 模式, PKCS7/PKCS5 填充)
  • ✅ AES 数据解密(CBC 模式, PKCS7/PKCS5 填充)
  • ✅ 跨平台兼容(Android/iOS/HarmonyOS/H5/微信小程序)

RSA 非对称加密算法

  • ✅ RSA 密钥对生成(2048位)
  • ✅ RSA-OAEP-SHA256 数据加密
  • ✅ RSA-OAEP-SHA256 数据解密
  • ✅ SHA256withRSA 数字签名(PKCS#1 v1.5)
  • ✅ SHA256withRSA 签名验证(PKCS#1 v1.5)
  • ✅ 跨平台兼容(Android/iOS/HarmonyOS/H5)
  • ⚠️ 微信小程序端暂不支持RSA-OAEP-SHA256加密解密,仅支持签名验签

调用方式

本插件同时支持回调风格异步风格两种调用方式:

方式一:回调风格(传统方式)

import { sm2Sign } from "@/uni_modules/sunrains-util";

sm2Sign({
  data: "需要签名的数据",
  privateKey: "你的私钥",
  success: (res) => {
    console.log("签名结果:", res.signature);
  },
  fail: (err) => {
    console.error("签名失败:", err);
  }
});

方式二:异步风格(推荐,更简洁)

import { sm2SignAsync } from "@/uni_modules/sunrains-util";

async function doSign() {
  try {
    const result = await sm2SignAsync("需要签名的数据", "你的私钥")
    console.log("签名结果:", result.signature)
  } catch (error) {
    console.error("签名失败:", error)
  }
}

平台支持

平台 SM2 实现方式 SM4 实现方式 AES 实现方式 RSA 实现方式 状态
Android BouncyCastle BouncyCastle Java Crypto API Java Crypto API (RSA-OAEP-SHA256) ✅ 完全支持
iOS GMObjC GMObjC CommonCrypto Security Framework (RSA-OAEP-SHA256) ✅ 完全支持
HarmonyOS cryptoFramework cryptoFramework cryptoFramework cryptoFramework (RSA-OAEP-SHA256) ✅ 完全支持
H5 sm-crypto sm-crypto Web Crypto API Web Crypto API (RSA-OAEP-SHA256) ✅ 完全支持
微信小程序 miniprogram-sm-crypto miniprogram-sm-crypto crypto-js jsrsasign (仅签名验签) ⚠️ 部分支持

使用示例

SM2 相关操作

1. 生成 SM2 密钥对

同步方式:

import { sm2GenerateKeyPair } from "@/uni_modules/sunrains-util";

const result = sm2GenerateKeyPair();
console.log("公钥:", result.publicKey);  // 04开头的130位十六进制字符串
console.log("私钥:", result.privateKey); // 64位十六进制字符串

异步方式:

import { sm2GenerateKeyPairAsync } from "@/uni_modules/sunrains-util";

async function generateKeys() {
  const result = await sm2GenerateKeyPairAsync()
  console.log("公钥:", result.publicKey)
  console.log("私钥:", result.privateKey)
}

2. SM2 签名

回调风格:

import { sm2Sign } from "@/uni_modules/sunrains-util";

sm2Sign({
  data: "需要签名的数据",
  privateKey: "你的私钥",
  success: (res) => {
    console.log("签名结果:", res.signature);
  },
  fail: (err) => {
    console.error("签名失败:", err);
  }
});

异步风格(推荐):

import { sm2SignAsync } from "@/uni_modules/sunrains-util";

async function doSign() {
  try {
    const result = await sm2SignAsync("需要签名的数据", "你的私钥")
    console.log("签名结果:", result.signature)
  } catch (error) {
    console.error("签名失败:", error)
  }
}

3. SM2 验签

回调风格:

import { sm2Verify } from "@/uni_modules/sunrains-util";

sm2Verify({
  data: "原始数据",
  signature: "签名值",
  publicKey: "你的公钥",
  success: (res) => {
    console.log("验签结果:", res.valid); // true 或 false
  },
  fail: (err) => {
    console.error("验签失败:", err);
  }
});

异步风格(推荐):

import { sm2VerifyAsync } from "@/uni_modules/sunrains-util";

async function doVerify() {
  try {
    const result = await sm2VerifyAsync("原始数据", "签名值", "你的公钥")
    console.log("验签结果:", result.valid) // true 或 false
  } catch (error) {
    console.error("验签失败:", error)
  }
}

完整实战示例

以下是一个完整的国密算法测试页面示例,及服务端(java)工具类,展示了所有功能的实际使用:

客户端 Vue 3 Composition API 示例

 <template>
  <view class="order-content">
    <view class="order-header">
      <text class="order-title">国密算法测试</text>
    </view>

    <scroll-view class="order-list" scroll-y="true">
      <view class="section">
        <view class="section-title">SM4 对称加密</view>

        <view class="input-group">
          <text class="label">待加密内容:</text>
          <input v-model="sm4Content" placeholder="请输入要加密的内容" class="input" />
        </view>

        <button @click="testSm4Encrypt" class="btn btn-primary">SM4 加密</button>

        <view class="result-box" v-if="sm4Key.key">
          <text class="result-label">密钥 (key):</text>
          <text selectable="true" class="result-value">{{ sm4Key.key }}</text>
        </view>

        <view class="result-box" v-if="sm4Key.iv">
          <text class="result-label">初始向量 (iv):</text>
          <text selectable="true" class="result-value">{{ sm4Key.iv }}</text>
        </view>

        <view class="result-box" v-if="sm4Encrypted">
          <text class="result-label">加密结果:</text>
          <text selectable="true" class="result-value">{{ sm4Encrypted }}</text>
        </view>
        <button @click="testSm4Decrypt" :disabled="sm4Encrypted.length==0" class="btn btn-secondary">SM4 解密</button>
        <button @click="testSm4Decrypt" :disabled="sm4Encrypted === ''" :class="sm4Encrypted === '' ? 'btn btn-secondary disabled' : 'btn btn-secondary'" class="btn btn-secondary">SM4 解密</button>

        <view class="result-box" v-if="sm4Decrypted">
          <text class="result-label">解密结果:</text>
          <text selectable="true" class="result-value success">{{ sm4Decrypted }}</text>
        </view>
      </view>

      <view class="section">
        <view class="section-title">SM2 数字签名</view>

        <view class="input-group">
          <text class="label">待签名内容:</text>
          <input v-model="sm2SignContent" placeholder="请输入要签名的内容" class="input" />
        </view>

        <button @click="testSm2Sign" class="btn btn-primary">生成密钥对并签名</button>

        <view class="result-box" v-if="sm2KeyPair.publicKey">
          <text class="result-label">公钥:</text>
          <text selectable="true" class="result-value small">{{ sm2KeyPair.publicKey }}</text>
        </view>

        <view class="result-box" v-if="sm2KeyPair.privateKey">
          <text class="result-label">私钥:</text>
          <text selectable="true" class="result-value small">{{ sm2KeyPair.privateKey }}</text>
        </view>

        <view class="result-box" v-if="sm2Signature">
          <text class="result-label">签名结果:</text>
          <text selectable="true" class="result-value small">{{ sm2Signature }}</text>
        </view>

        <button @click="testSm2Verify" :disabled="sm2Signature.length==0" class="btn btn-secondary">验签</button>

        <view class="result-box" v-if="sm2VerifyResult !== null">
          <text class="result-label">验签结果:</text>
          <text class="result-value" :class="sm2VerifyResult ==true? 'success' : 'error'">
            {{ sm2VerifyResult ==true ? '✅ 验签成功' : '❌ 验签失败' }}
          </text>
        </view>
      </view>

      <view class="section">
        <view class="section-title">SM2 非对称加密</view>

        <view class="input-group">
          <text class="label">待加密内容:</text>
          <input v-model="sm2EncryptContent" placeholder="请输入要加密的内容" class="input" />
        </view>

        <button @click="testSm2Encrypt" class="btn btn-primary">SM2 加密</button>

        <view class="result-box" v-if="sm2EncryptKeyPair.publicKey">
          <text class="result-label">公钥:</text>
          <text selectable="true" class="result-value small">{{ sm2EncryptKeyPair.publicKey }}</text>
        </view>

        <view class="result-box" v-if="sm2EncryptKeyPair.privateKey">
          <text class="result-label">私钥:</text>
          <text selectable="true" class="result-value small">{{ sm2EncryptKeyPair.privateKey }}</text>
        </view>

        <view class="result-box" v-if="sm2Encrypted">
          <text class="result-label">加密结果:</text>
          <text selectable="true" class="result-value small">{{ sm2Encrypted }}</text>
        </view>

        <button @click="testSm2Decrypt" :disabled="sm2Encrypted.length==0" class="btn btn-secondary">SM2 解密</button>

        <view class="result-box" v-if="sm2Decrypted">
          <text class="result-label">解密结果:</text>
          <text selectable="true" class="result-value success">{{ sm2Decrypted }}</text>
        </view>
      </view>

      <view class="section">
        <view class="section-title">SM3 哈希算法</view>

        <!-- 1. SM3 哈希 -->
        <view class="input-group">
          <text class="label">待哈希内容:</text>
          <input v-model="sm3Content" placeholder="请输入要哈希的内容" class="input" />
        </view>

        <button @click="testSm3Hash" class="btn btn-primary">SM3 哈希</button>

        <view class="result-box" v-if="sm3HashResult">
          <text class="result-label">SM3 哈希值:</text>
          <text selectable="true" class="result-value small">{{ sm3HashResult }}</text>
        </view>

        <!-- 2. SM3 哈希验证 -->
        <view class="divider"></view>
        <view class="sub-title">SM3 哈希验证</view>

        <view class="input-group">
          <text class="label">验证 - 原始内容:</text>
          <input v-model="sm3VerifyContent" placeholder="请输入原始内容" class="input" />
        </view>

        <button @click="testSm3Verify" :disabled="sm3VerifyContent.length == 0" class="btn btn-secondary">验证哈希值</button>

        <view class="result-box" v-if="sm3HashMatchResult !== null">
          <text class="result-label">哈希匹配结果:</text>
          <text class="result-value" :class="sm3HashMatchResult == true ? 'success' : 'error'">
            {{ sm3HashMatchResult == true ? '✅ 哈希值匹配' : '❌ 哈希值不匹配' }}
          </text>
        </view>

        <!-- 3. HMAC-SM3 哈希 -->
        <view class="divider"></view>
        <view class="sub-title">HMAC-SM3 哈希</view>

        <view class="input-group">
          <text class="label">HMAC-SM3 待哈希内容:</text>
          <input v-model="sm3HmacContent" placeholder="请输入要哈希的内容" class="input" />
        </view>

        <view class="input-group">
          <text class="label">HMAC-SM3 密钥(HEX):</text>
          <input v-model="sm3HmacKey" placeholder="请输入HMAC密钥(HEX格式)" class="input" />
        </view>

        <button @click="testSm3HmacHash" :disabled="sm3HmacContent.length == 0 || sm3HmacKey.length == 0" class="btn btn-primary">HMAC-SM3 哈希</button>

        <view class="result-box" v-if="sm3HmacHashResult">
          <text class="result-label">HMAC-SM3 哈希值:</text>
          <text selectable="true" class="result-value small">{{ sm3HmacHashResult }}</text>
        </view>

        <!-- 4. HMAC-SM3 哈希验证 -->
        <view class="divider"></view>
        <view class="sub-title">HMAC-SM3 哈希验证</view>

        <view class="input-group">
          <text class="label">验证 - 原始内容:</text>
          <input v-model="sm3HmacVerifyContent" placeholder="请输入原始内容" class="input" />
        </view>

        <view class="input-group">
          <text class="label">验证 - HMAC 密钥:</text>
          <input v-model="sm3HmacVerifyKey" placeholder="请输入HMAC密钥" class="input" />
        </view>

        <view class="input-group">
          <text class="label">验证 - 期望哈希值:</text>
          <input v-model="sm3HmacExpectedHash" placeholder="请输入期望的哈希值" class="input" />
        </view>

        <button @click="testSm3HmacHashWithExpected" :disabled="sm3HmacVerifyContent.length == 0 || sm3HmacVerifyKey.length == 0 || sm3HmacExpectedHash.length == 0" class="btn btn-secondary">验证 HMAC-SM3 哈希值</button>

        <view class="result-box" v-if="sm3HmacHashMatchResult !== null">
          <text class="result-label">HMAC-SM3 哈希匹配结果:</text>
          <text class="result-value" :class="sm3HmacHashMatchResult == true ? 'success' : 'error'">
            {{ sm3HmacHashMatchResult == true ? '✅ 哈希值匹配' : '❌ 哈希值不匹配' }}
          </text>
        </view>

        <!-- 5. 文件 SM3 哈希 -->
        <view class="divider"></view>
        <view class="sub-title">文件 SM3 哈希</view>

        <button @click="chooseImageAndHash" class="btn btn-primary">选择图片并计算 SM3</button>
        <button @click="chooseFileAndHash" class="btn btn-primary">选择文件并计算 SM3</button>
        <view class="result-box" v-if="sm3FileName">
          <text class="result-label">文件名:</text>
          <text class="result-value">{{ sm3FileName }}</text>
        </view>

        <view class="result-box" v-if="sm3FileHashResult">
          <text class="result-label">文件 SM3 哈希值:</text>
          <text selectable="true" class="result-value small">{{ sm3FileHashResult }}</text>
        </view>
      </view>

      <view class="section">
        <view class="section-title">AES 对称加密</view>

        <view class="input-group">
          <text class="label">待加密内容:</text>
          <input v-model="aesContent" placeholder="请输入要加密的内容" class="input" />
        </view>

        <button @click="testAesEncrypt" class="btn btn-primary">AES 加密</button>

        <view class="result-box" v-if="aesKey.key">
          <text class="result-label">密钥 (key):</text>
          <text selectable="true" class="result-value">{{ aesKey.key }}</text>
        </view>

        <view class="result-box" v-if="aesKey.iv">
          <text class="result-label">初始向量 (iv):</text>
          <text selectable="true" class="result-value">{{ aesKey.iv }}</text>
        </view>

        <view class="result-box" v-if="aesEncrypted">
          <text class="result-label">加密结果:</text>
          <text selectable="true" class="result-value">{{ aesEncrypted }}</text>
        </view>

        <button @click="testAesDecrypt" :disabled="aesEncrypted.length==0" class="btn btn-secondary">AES 解密</button>

        <view class="result-box" v-if="aesDecrypted">
          <text class="result-label">解密结果:</text>
          <text selectable="true" class="result-value success">{{ aesDecrypted }}</text>
        </view>
      </view>

      <view class="section">
        <view class="section-title">RSA 非对称加密</view>

        <view class="input-group">
          <text class="label">待加密内容:</text>
          <input v-model="rsaContent" placeholder="请输入要加密的内容" class="input" />
        </view>

        <button @click="testRsaEncrypt" class="btn btn-primary">RSA 加密</button>

        <view class="result-box" v-if="rsaKeyPair.publicKey">
          <text class="result-label">公钥:</text>
          <text selectable="true" class="result-value small">{{ rsaKeyPair.publicKey }}</text>
        </view>

        <view class="result-box" v-if="rsaKeyPair.privateKey">
          <text class="result-label">私钥:</text>
          <text selectable="true" class="result-value small">{{ rsaKeyPair.privateKey }}</text>
        </view>

        <view class="result-box" v-if="rsaEncrypted">
          <text class="result-label">加密结果:</text>
          <text selectable="true" class="result-value small">{{ rsaEncrypted }}</text>
        </view>

        <button @click="testRsaDecrypt" :disabled="rsaEncrypted.length==0" class="btn btn-secondary">RSA 解密</button>

        <view class="result-box" v-if="rsaDecrypted">
          <text class="result-label">解密结果:</text>
          <text selectable="true" class="result-value success">{{ rsaDecrypted }}</text>
        </view>
      </view>

      <view class="section">
        <view class="section-title">RSA 数字签名</view>

        <view class="input-group">
          <text class="label">待签名内容:</text>
          <input v-model="rsaSignContent" placeholder="请输入要签名的内容" class="input" />
        </view>

        <button @click="testRsaSign" class="btn btn-primary">生成密钥对并签名</button>

        <view class="result-box" v-if="rsaSignKeyPair.publicKey">
          <text class="result-label">公钥:</text>
          <text selectable="true" class="result-value small">{{ rsaSignKeyPair.publicKey }}</text>
        </view>

        <view class="result-box" v-if="rsaSignKeyPair.privateKey">
          <text class="result-label">私钥:</text>
          <text selectable="true" class="result-value small">{{ rsaSignKeyPair.privateKey }}</text>
        </view>

        <view class="result-box" v-if="rsaSignature">
          <text class="result-label">签名结果:</text>
          <text selectable="true" class="result-value small">{{ rsaSignature }}</text>
        </view>

        <button @click="testRsaVerify" :disabled="rsaSignature.length==0" class="btn btn-secondary">验签</button>

        <view class="result-box" v-if="rsaVerifyResult !== null">
          <text class="result-label">验签结果:</text>
          <text class="result-value" :class="rsaVerifyResult ==true? 'success' : 'error'">
            {{ rsaVerifyResult ==true ? '✅ 验签成功' : '❌ 验签失败' }}
          </text>
        </view>
      </view>

      <view class="order-item" @click="goToDetail(1002, '已完成')">
        <view class="order-info">
          <text class="order-id">算法编号:1002</text>
          <text class="order-status">状态:已完成</text>
        </view>
        <view class="order-btn">
          <text class="go-detail">查看详情</text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script setup lang="uts">
  import {IdcardOcrModel}from "@/uni_modules/sunrains-idcard-ocr/utssdk/interface.uts"
  import {Response} from "@/uni_modules/sunrains-utils/index.uts"

  import * as SM from "@/uni_modules/sunrains-smutil"

  // ==================== SM4 相关数据 ====================
  const sm4Content = ref("Hello World")
  const sm4Key = ref<SM.Sm4Key>({ key: '', iv: '' })
  const sm4Encrypted = ref("")
  const sm4Decrypted = ref("")

  // ==================== SM2 签名验签相关数据 ====================
  const sm2SignContent = ref("123")
  const sm2KeyPair = ref<SM.Sm2KeyPair>({ publicKey: '', privateKey: '' })
  const sm2Signature = ref("")
  const sm2VerifyResult = ref<boolean | null>(null)

  // ==================== SM2 加密解密相关数据 ====================
  const sm2EncryptContent = ref("Hello SM2")
  const sm2EncryptKeyPair = ref<SM.Sm2KeyPair>({ publicKey: '', privateKey: '' })
  const sm2Encrypted = ref("")
  const sm2Decrypted = ref("")

  // ==================== SM3 相关数据 ====================
  const sm3Content = ref("Hello SM3")
  const sm3HashResult = ref("")
  const sm3VerifyContent = ref("")
  const sm3HashMatchResult = ref<boolean | null>(null)

  const sm3HmacContent = ref("Hello HMAC-SM3")
  const sm3HmacKey = ref("0123456789abcdef0123456789abcdef")  // 默认 HMAC 密钥(32字节HEX)
  const sm3HmacHashResult = ref("")
  const sm3HmacVerifyContent = ref("")
  const sm3HmacVerifyKey = ref("")
  const sm3HmacExpectedHash = ref("")
  const sm3HmacHashMatchResult = ref<boolean | null>(null)
  const sm3VerifyResult = ref<boolean|null>(null)

  const sm3FileName = ref("")
  const sm3FileHashResult = ref("")

  // ==================== AES 相关数据 ====================
  const aesContent = ref("Hello AES")
  const aesKey = ref<SM.AesKey>({ key: '', iv: '' })
  const aesEncrypted = ref("")
  const aesDecrypted = ref("")

  // ==================== RSA 加密解密相关数据 ====================
  const rsaContent = ref("Hello RSA")
  const rsaKeyPair = ref<SM.RsaKeyPair>({ publicKey: '', privateKey: '' })
  const rsaEncrypted = ref("")
  const rsaDecrypted = ref("")

  // ==================== RSA 签名验签相关数据 ====================
  const rsaSignContent = ref("需要签名的数据")
  const rsaSignKeyPair = ref<SM.RsaKeyPair>({ publicKey: '', privateKey: '' })
  const rsaSignature = ref("")
  const rsaVerifyResult = ref<boolean | null>(null)

  // ==================== SM4 加密 ====================
  async function testSm4Encrypt() {
      if (sm4Content.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          // 生成密钥
          const key = await SM.sm4GenerateKeyAsync()
          sm4Key.value = { key: key.key, iv: key.iv }
          console.log("SM4 密钥:", key)

          // 加密
          const encryptResult = await SM.sm4EncryptAsync(sm4Content.value, key.key, key.iv)
          sm4Encrypted.value = encryptResult.result
          console.log("SM4 加密成功:", encryptResult.result)
          uni.showToast({ title: '加密成功', icon: 'success' })

      } catch (e) {
          console.error("SM4 加密失败:", e)
          uni.showToast({ title: '加密失败', icon: 'error' })
      }
  }

  // ==================== SM4 解密 ====================
  async function testSm4Decrypt() {
      if (sm4Encrypted.value.length==0 || sm4Key.value.key.length==0) {
          uni.showToast({ title: '请先加密', icon: 'none' })
          return
      }

      try {
          const decryptResult = await SM.sm4DecryptAsync(sm4Encrypted.value, sm4Key.value.key, sm4Key.value.iv)
          sm4Decrypted.value = decryptResult.result
          console.log("SM4 解密成功:", decryptResult.result)
          if (decryptResult.result == sm4Content.value) {
              uni.showToast({ title: '解密成功且内容一致', icon: 'success' })
          } else {
              uni.showToast({ title: '解密成功但内容不一致', icon: 'error' })
          }
      } catch (e) {
          console.error("SM4 解密失败:", e)
          uni.showToast({ title: '解密失败', icon: 'error' })
      }
  }

  // ==================== AES 加密 ====================
  async function testAesEncrypt() {
      if (aesContent.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          // 生成密钥和 IV
          const key = await SM.aesGenerateKeyAsync()
          aesKey.value = { key: key.key, iv: key.iv }
          console.log("AES 密钥:", key)

          // 加密
          const encryptResult = await SM.aesEncryptAsync(aesContent.value, key.key, key.iv)
          aesEncrypted.value = encryptResult.result
          console.log("AES 加密成功:", encryptResult.result)
          uni.showToast({ title: '加密成功', icon: 'success' })

      } catch (e) {
          console.error("AES 加密失败:", e)
          uni.showToast({ title: '加密失败', icon: 'error' })
      }
  }

  // ==================== AES 解密 ====================
  async function testAesDecrypt() {
      if (aesEncrypted.value.length==0 || aesKey.value.key.length==0) {
          uni.showToast({ title: '请先加密', icon: 'none' })
          return
      }

      try {
          const decryptResult = await SM.aesDecryptAsync(aesEncrypted.value, aesKey.value.key, aesKey.value.iv)
          aesDecrypted.value = decryptResult.result
          console.log("AES 解密成功:", decryptResult.result)
          if (decryptResult.result == aesContent.value) {
              uni.showToast({ title: '解密成功且内容一致', icon: 'success' })
          } else {
              uni.showToast({ title: '解密成功但内容不一致', icon: 'error' })
          }
      } catch (e) {
          console.error("AES 解密失败:", e)
          uni.showToast({ title: '解密失败', icon: 'error' })
      }
  }

  // ==================== RSA 加密 ====================
  async function testRsaEncrypt() {
      if (rsaContent.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          // 生成密钥对
          const keyPair = await SM.rsaGenerateKeyPairAsync()
          rsaKeyPair.value = { 
              publicKey: keyPair.publicKey, 
              privateKey: keyPair.privateKey 
          }
          console.log("RSA 公钥:", keyPair.publicKey)
          console.log("RSA 私钥:", keyPair.privateKey)

          // 加密
          const encryptResult = await SM.rsaEncryptAsync(rsaContent.value, keyPair.publicKey)
          rsaEncrypted.value = encryptResult.result
          console.log("RSA 加密结果:", encryptResult.result)
          uni.showToast({ title: '加密成功', icon: 'success' })
      } catch (e) {
          console.error("RSA 加密失败:", e)
          uni.showToast({ title: '加密失败', icon: 'error' })
      }
  }

  // ==================== RSA 解密 ====================
  async function testRsaDecrypt() {
      if (rsaEncrypted.value.length==0 || rsaKeyPair.value.privateKey.length==0) {
          uni.showToast({ title: '请先加密', icon: 'none' })
          return
      }

      try {
          const decryptResult = await SM.rsaDecryptAsync(rsaEncrypted.value, rsaKeyPair.value.privateKey)
          rsaDecrypted.value = decryptResult.result
          console.log("RSA 解密结果:", decryptResult.result)

          if (decryptResult.result == rsaContent.value) {
              uni.showToast({ title: '解密成功且内容一致', icon: 'success' })
          } else {
              uni.showToast({ title: '解密成功但内容不一致', icon: 'error' })
          }
      } catch (e) {
          console.error("RSA 解密异常:", e)
          uni.showToast({ title: '解密异常', icon: 'error' })
      }
  }

  // ==================== RSA 签名 ====================
  async function testRsaSign() {
      if (rsaSignContent.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          // 生成密钥对
          const keyPair = await SM.rsaGenerateKeyPairAsync()
          rsaSignKeyPair.value = { 
              publicKey: keyPair.publicKey, 
              privateKey: keyPair.privateKey 
          }
          console.log("RSA 公钥:", keyPair.publicKey)
          console.log("RSA 私钥:", keyPair.privateKey)

          // 签名
          const signResult = await SM.rsaSignAsync(rsaSignContent.value, keyPair.privateKey)
          rsaSignature.value = signResult.signature
          console.log("RSA 签名成功:", signResult.signature)
          uni.showToast({ title: '签名成功', icon: 'success' })
      } catch (e) {
          console.error("RSA 签名失败:", e)
          uni.showToast({ title: '签名失败', icon: 'error' })
      }
  }

  // ==================== RSA 验签 ====================
  async function testRsaVerify() {
      if (rsaSignature.value.length==0 || rsaSignKeyPair.value.publicKey.length==0) {
          uni.showToast({ title: '请先签名', icon: 'none' })
          return
      }

      try {
          // 验签
          const verifyResult = await SM.rsaVerifyAsync(rsaSignContent.value, rsaSignature.value, rsaSignKeyPair.value.publicKey)
          rsaVerifyResult.value = verifyResult.valid
          console.log("RSA 验签结果:", verifyResult.valid)
          if (verifyResult.valid) {
              uni.showToast({ title: '验签成功', icon: 'success' })
          } else {
              uni.showToast({ title: '验签失败', icon: 'error' })
          }
      } catch (e) {
          console.error("RSA 验签异常:", e)
          uni.showToast({ title: '验签异常', icon: 'error' })
      }
  }

  // ==================== SM2 签名 ====================
  async function testSm2Sign() {
      if (sm2SignContent.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          // 生成密钥对
          const keyPair = await SM.sm2GenerateKeyPairAsync()
          sm2KeyPair.value = { 
              publicKey: keyPair.publicKey, 
              privateKey: keyPair.privateKey 
          }
          console.log("SM2 公钥:", keyPair.publicKey)
          console.log("SM2 私钥:", keyPair.privateKey)

          // 签名
          const signResult = await SM.sm2SignAsync(sm2SignContent.value, keyPair.privateKey)
          sm2Signature.value = signResult.signature
          console.log("SM2 签名成功:", signResult.signature)
          uni.showToast({ title: '签名成功', icon: 'success' })
      } catch (e) {
          console.error("SM2 生成密钥对失败:", e)
          uni.showToast({ title: '生成密钥对失败', icon: 'error' })
      }
  }

  // ==================== SM2 验签 ====================
  async function testSm2Verify() {
      if (sm2Signature.value.length==0 || sm2KeyPair.value.publicKey.length==0) {
          uni.showToast({ title: '请先签名', icon: 'none' })
          return
      }

      try {
          // 验签
          const verifyResult = await SM.sm2VerifyAsync(sm2SignContent.value, sm2Signature.value, sm2KeyPair.value.publicKey)
          sm2VerifyResult.value = verifyResult.valid
          console.log("SM2 验签结果:", verifyResult.valid)
          if (verifyResult.valid) {
              uni.showToast({ title: '验签成功', icon: 'success' })
          } else {
              uni.showToast({ title: '验签失败', icon: 'error' })
          }
      } catch (e) {
          console.error("SM2 验签异常:", e)
          uni.showToast({ title: '验签异常', icon: 'error' })
      }
  }

  // ==================== SM2 加密 ====================
  async function testSm2Encrypt() {
      if (sm2EncryptContent.value.length==0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          // 生成密钥对
          const keyPair = await SM.sm2GenerateKeyPairAsync()
          sm2EncryptKeyPair.value = { 
              publicKey: keyPair.publicKey, 
              privateKey: keyPair.privateKey 
          }
          console.log("SM2 加密公钥:", keyPair.publicKey)
          console.log("SM2 加密私钥:", keyPair.privateKey)

          // 加密
          const encryptResult = await SM.sm2EncryptAsync(sm2EncryptContent.value, keyPair.publicKey)
          sm2Encrypted.value = encryptResult.encrypted
          console.log("SM2 加密结果:", encryptResult.encrypted)
          uni.showToast({ title: '加密成功', icon: 'success' })
      } catch (e) {
          console.error("SM2 生成密钥对失败:", e)
          uni.showToast({ title: '生成密钥对失败', icon: 'error' })
      }
  }

  // ==================== SM2 解密 ====================
  async function testSm2Decrypt() {
      if (sm2Encrypted.value.length==0 || sm2EncryptKeyPair.value.privateKey.length==0) {
          uni.showToast({ title: '请先加密', icon: 'none' })
          return
      }

      try {
          const decryptResult = await SM.sm2DecryptAsync(sm2Encrypted.value, sm2EncryptKeyPair.value.privateKey)
          sm2Decrypted.value = decryptResult.decrypted
          console.log("SM2 解密结果:", decryptResult.decrypted)

          if (decryptResult.decrypted == sm2EncryptContent.value) {
              uni.showToast({ title: '解密成功且内容一致', icon: 'success' })
          } else {
              uni.showToast({ title: '解密成功但内容不一致', icon: 'error' })
          }
      } catch (e) {
          console.error("SM2 解密异常:", e)
          uni.showToast({ title: '解密异常', icon: 'error' })
      }
  }

  // ==================== SM3 哈希 ====================
  async function testSm3Hash() {
      if (sm3Content.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      try {
          const hashResult = await SM.sm3HashAsync(sm3Content.value)
          sm3HashResult.value = hashResult.resultHash

          console.log("SM3 哈希成功:", hashResult.resultHash)
          uni.showToast({ title: '哈希成功', icon: 'success' })
      } catch (e) {
          console.error("SM3 哈希失败:", e)
          uni.showToast({ title: '哈希失败', icon: 'error' })
      }
  }

  // ==================== HMAC-SM3 哈希 ====================
  async function testSm3HmacHash() {
      if (sm3HmacContent.value.length == 0) {
          uni.showToast({ title: '请输入内容', icon: 'none' })
          return
      }

      if (sm3HmacKey.value.length == 0) {
          uni.showToast({ title: '请输入HMAC密钥', icon: 'none' })
          return
      }

      try {
          const hmacHashResult = await SM.sm3HmacHashAsync(sm3HmacContent.value, sm3HmacKey.value)
          sm3HmacHashResult.value = hmacHashResult.resultHash
          sm3HmacExpectedHash.value  =  hmacHashResult.resultHash
          sm3HmacVerifyKey.value = sm3HmacKey.value
          sm3HmacVerifyContent.value = sm3HmacContent.value
          console.log("HMAC-SM3 哈希成功:", hmacHashResult.resultHash)
          uni.showToast({ title: 'HMAC-SM3 哈希成功', icon: 'success' })
      } catch (e) {
          console.error("HMAC-SM3 哈希失败:", e)
          uni.showToast({ title: 'HMAC-SM3 哈希失败', icon: 'error' })
      }
  }

  // ==================== HMAC-SM3 验证(输入期望哈希值) ====================
  async function testSm3HmacHashWithExpected() {
      if (sm3HmacVerifyContent.value.length == 0) {
          uni.showToast({ title: '请输入原始内容', icon: 'none' })
          return
      }

      try {
          // 计算实际 HMAC-SM3 哈希值
          const res = await SM.sm3HmacVerifyAsync(sm3HmacVerifyContent.value, sm3HmacVerifyKey.value,sm3HmacHashResult.value)
          if (res) {
              uni.showToast({ title: 'HMAC-SM3 哈希值匹配', icon: 'success' })
          } else {
              uni.showToast({ title: 'HMAC-SM3 哈希值不匹配', icon: 'error' })
          }
      } catch (e) {
          console.error("HMAC-SM3 哈希验证异常:", e)
          uni.showToast({ title: '验证异常', icon: 'error' })
      }
  }

  // ==================== SM3 验证 ====================
  async function testSm3Verify() {
      if (sm3HashResult.value.length == 0 || sm3Content.value.length == 0) {
          uni.showToast({ title: '请先计算哈希', icon: 'none' })
          return
      }

      try {
          // 重新计算哈希并验证
          const res = await SM.sm3VerifyAsync(sm3VerifyContent.value,sm3HashResult.value)
          sm3VerifyResult.value = res
          console.log("SM3 验证结果:", sm3VerifyResult.value)

          if (sm3VerifyResult.value!) {
              uni.showToast({ title: '验证成功', icon: 'success' })
          } else {
              uni.showToast({ title: '验证失败', icon: 'error' })
          }
      } catch (e) {
          console.error("SM3 验证异常:", e)
          uni.showToast({ title: '验证异常', icon: 'error' })
      }
  }

  // ==================== 选择文件并计算 SM3 哈希 ====================
  async function processFileHash(filePath: string, fileSize?: number) {
      // 计算文件 SM3 哈希
      uni.showLoading({ title: '计算中...' })

      try {
          console.log("开始计算文件 SM3 哈希")
          console.log("文件路径:", filePath)

          const result = await SM.sm3FileHashAsync(filePath)
          sm3FileHashResult.value = result.resultHash
          console.log("文件 SM3 哈希成功:", result.resultHash)
          uni.hideLoading()
          uni.showToast({ title: '文件哈希成功', icon: 'success' })
      } catch (e) {
          console.error("文件 SM3 哈希失败:", e)
          uni.hideLoading()
          uni.showToast({ title: '文件哈希失败', icon: 'error' })
      }
  }

  function chooseFileAndHash() {
      uni.chooseFile({
          count: 1,
          success: (res) => {
              const tempFiles = res.tempFiles
              if (tempFiles != null && tempFiles.length > 0) {
                  const file = tempFiles[0]
                  const fileName = file.name
                  const filePath = file.path
                  const fileSize = file.size
                  sm3FileName.value = fileName != null ? fileName : "未知文件"

                  if (filePath != null) {
                      processFileHash(filePath, fileSize)
                  }
              }
          },
          fail: (err) => {
              console.error("选择文件失败:", err)
              if (err.errMsg != null && !err.errMsg.includes('cancel')) {
                  uni.showToast({ title: '选择文件失败', icon: 'error' })
              }
          }
      })
  }

  // ==================== 选择相册图片并计算 SM3 哈希 ====================
  function chooseImageAndHash() {
      uni.chooseImage({
          count: 1,
          sizeType: ['original'],  // 只选择原图,不压缩
          sourceType: ['album', 'camera'],
          success: (res) => {
              const tempFilePaths = res.tempFilePaths
              if (tempFilePaths != null && tempFilePaths.length > 0) {
                  const filePath = tempFilePaths[0]
                  const fileName = "图片文件"
                  sm3FileName.value = fileName
                  console.log("选择的图片(原图):", filePath)

                  if (filePath != null) {
                      // 调用异步函数处理文件哈希
                      processFileHash(filePath)
                  }
              }
          },
          fail: (err) => {
              console.error("选择图片失败:", err)
              if (err.errMsg != null && !err.errMsg.includes('cancel')) {
                  uni.showToast({ title: '选择图片失败', icon: 'error' })
              }
          }
      })
  }

  const goToDetail = (orderId: number, status: string) => {
    uni.setStorageSync('fromTabBar', true)
    uni.navigateTo({
      url: `/pages/detail/detail?orderId=${orderId}&status=${status}`,
      success: () => {
        console.log(`跳转到order${orderId}的详情页`)
      }
    })
  }

  function btn (){

  }

</script>

<style scoped>
  .order-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    background-color: #f5f5f5;
  }

  .order-header {
    height: 48px;
    background-color: #fff;
    align-items: center;
    justify-content: center;
    border-bottom-width: 1px;
    border-bottom-style: solid;
    border-bottom-color: #eee;
  }

  .order-title {
    font-size: 18px;
    font-weight: bold;
    color: #333;
  }

  .order-list {
    flex: 1;
    padding: 10px;
  }

  /* 区块样式 */
  .section {
    background-color: #fff;
    border-radius: 8px;
    padding: 15px;
    margin-bottom: 15px;
  }

  .section-title {
    font-size: 16px;
    font-weight: bold;
    color: #333;
    margin-bottom: 15px;
    padding-bottom: 10px;
    border-bottom-width: 1px;
    border-bottom-style: solid;
    border-bottom-color: #eee;
  }

  .input-group {
    margin-bottom: 15px;
  }

  .label {
    font-size: 14px;
    color: #666;
    margin-bottom: 8px;
  }

  .input {
    height: 40px;
    border-width: 1px;
    border-style: solid;
    border-color: #ddd;
    border-radius: 4px;
    padding: 0 10px;
    font-size: 14px;
    background-color: #fafafa;
  }

  /* 按钮样式 */
  .btn {
    height: 44px;
    border-radius: 4px;
    font-size: 15px;
    margin-bottom: 10px;
  }

  .btn-primary {
    background-color: #007aff;
    color: #fff;
  }

  .btn-secondary {
    background-color: #34c759;
    color: #fff;
  }

  .btn:disabled {
    opacity: 0.5;
  }

  /* 结果展示框 */
  .result-box {
    margin-top: 10px;
    padding: 10px;
    background-color: #f9f9f9;
    border-radius: 4px;
    border-left-width: 3px;
    border-left-style: solid;
    border-left-color: #007aff;
  }

  .result-label {
    font-size: 12px;
    color: #999;
    margin-bottom: 5px;
  }

  .result-value {
    font-size: 13px;
    color: #333;
    line-height: 1.5;
  }

  .result-value.small {
    font-size: 11px;
  }

  .result-value.success {
    color: #34c759;
    font-weight: bold;
  }

  .result-value.error {
    color: #ff3b30;
    font-weight: bold;
  }

  .divider {
    height: 1px;
    background-color: #eee;
    margin: 15px 0;
  }

  .sub-title {
    font-size: 14px;
    font-weight: bold;
    color: #666;
    margin-bottom: 10px;
  }
</style>

服务端(java工具类)

package top.sunrains;

import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
 *         <dependency>
 *             <groupId>org.bouncycastle</groupId>
 *             <artifactId>bcprov-jdk15to18</artifactId>
 *             <version>1.69</version>
 *         </dependency>
 */
public class SM2Util {

    private static final ECParameterSpec CURVE = ECNamedCurveTable.getParameterSpec("sm2p256v1");
    private static final ECDomainParameters DOMAIN = new ECDomainParameters(
            CURVE.getCurve(),
            CURVE.getG(),
            CURVE.getN(),
            CURVE.getH()
    );

    /**
     * 生成SM2密钥对
     *
     * @return Map包含publicKey和privateKey,均为小写十六进制字符串
     */
    public static java.util.Map<String, String> generateKeyPair() {
        try {
            BigInteger d = new BigInteger(256, new SecureRandom()).mod(DOMAIN.getN());
            org.bouncycastle.math.ec.ECPoint q = DOMAIN.getG().multiply(d).normalize();

            String x = String.format("4x", q.getAffineXCoord().toBigInteger());
            String y = String.format("4x", q.getAffineYCoord().toBigInteger());
            String publicKey = "04" + x + y;
            String privateKey = String.format("4x", d);
            java.util.Map<String, String> keyMap = new java.util.HashMap<>();
            keyMap.put("publicKey", publicKey.toLowerCase());
            keyMap.put("privateKey", privateKey.toLowerCase());
            return keyMap;
        } catch (Exception e) {
            System.err.println("密钥生成失败: " + e.getMessage());
            e.printStackTrace();
            java.util.Map<String, String> keyMap = new java.util.HashMap<>();
            keyMap.put("publicKey", "");
            keyMap.put("privateKey", "");
            return keyMap;
        }
    }

    /**
     * SM2签名
     *
     * @param data 待签名的原始数据
     * @param privateKeyHex 私钥(64字符十六进制)
     * @return 签名结果(DER格式,小写十六进制)
     */
    public static String sign(String data, String privateKeyHex) {
        try {
            byte[] msgBytes = data.getBytes(java.nio.charset.StandardCharsets.UTF_8);

            BigInteger d = new BigInteger(privateKeyHex, 16);
            ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(d, DOMAIN);

            SM2Signer signer = new SM2Signer(new SM3Digest());
            signer.init(true, priKey);
            signer.update(msgBytes, 0, msgBytes.length);
            byte[] sig = signer.generateSignature();

            return Hex.toHexString(sig).toLowerCase();
        } catch (Exception e) {
            System.err.println("签名失败: " + e.getMessage());
            e.printStackTrace();
            return "";
        }
    }

    /**
     * SM2验签
     *
     * @param data 原始数据
     * @param signatureHex 签名(DER格式,十六进制)
     * @param publicKeyHex 公钥(04开头或不含04前缀的十六进制)
     * @return 验签是否成功
     */
    public static boolean verify(String data, String signatureHex, String publicKeyHex) {
        try {
            byte[] msgBytes = data.getBytes(java.nio.charset.StandardCharsets.UTF_8);

            String xy = publicKeyHex.startsWith("04") ? publicKeyHex.substring(2) : publicKeyHex;
            BigInteger x = new BigInteger(xy.substring(0, 64), 16);
            BigInteger y = new BigInteger(xy.substring(64, 128), 16);

            org.bouncycastle.math.ec.ECPoint pubPoint = DOMAIN.getCurve().createPoint(x, y).normalize();
            ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubPoint, DOMAIN);

            SM2Signer signer = new SM2Signer(new SM3Digest());
            signer.init(false, pubKey);
            signer.update(msgBytes, 0, msgBytes.length);

            byte[] sigBytes = Hex.decode(signatureHex);
            return signer.verifySignature(sigBytes);
        } catch (Exception e) {
            System.err.println("验签失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * SM2加密
     *
     * @param data 待加密的原始数据(UTF-8字符串)
     * @param publicKeyHex 公钥(04开头或不含04前缀的十六进制)
     * @return 密文(HEX字符串,小写)
     */
    public static String encrypt(String data, String publicKeyHex) {
        try {
            byte[] dataBytes = data.getBytes(java.nio.charset.StandardCharsets.UTF_8);

            String xy = publicKeyHex.startsWith("04") ? publicKeyHex.substring(2) : publicKeyHex;
            BigInteger x = new BigInteger(xy.substring(0, 64), 16);
            BigInteger y = new BigInteger(xy.substring(64, 128), 16);

            org.bouncycastle.math.ec.ECPoint pubPoint = DOMAIN.getCurve().createPoint(x, y).normalize();
            ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubPoint, DOMAIN);

            SM2Engine engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);
            engine.init(true, new ParametersWithRandom(pubKey, new SecureRandom()));

            byte[] encrypted = engine.processBlock(dataBytes, 0, dataBytes.length);
            return Hex.toHexString(encrypted).toLowerCase();
        } catch (Exception e) {
            System.err.println("加密失败: " + e.getMessage());
            e.printStackTrace();
            return "";
        }
    }

    /**
     * SM2解密
     *
     * @param encryptedHex 密文(HEX字符串)
     * @param privateKeyHex 私钥(64字符十六进制)
     * @return 解密后的原始数据(UTF-8字符串)
     */
    public static String decrypt(String encryptedHex, String privateKeyHex) {
        try {
            byte[] encryptedBytes = Hex.decode(encryptedHex);

            BigInteger d = new BigInteger(privateKeyHex, 16);
            ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(d, DOMAIN);

            SM2Engine engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);
            engine.init(false, priKey);

            byte[] decrypted = engine.processBlock(encryptedBytes, 0, encryptedBytes.length);
            return new String(decrypted, java.nio.charset.StandardCharsets.UTF_8);
        } catch (Exception e) {
            System.err.println("解密失败: " + e.getMessage());
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        System.out.println("=== SM2 工具类测试 ===\n");

        java.util.Map<String, String> keyPair = generateKeyPair();
        String publicKey = keyPair.get("publicKey");
        String privateKey = keyPair.get("privateKey");

        System.out.println("公钥: " + publicKey);
        System.out.println("私钥: " + privateKey);
        System.out.println();

        String testData = "123";
        System.out.println("原始数据: " + testData);

        String signature = sign(testData, privateKey);
        System.out.println("签名: " + signature);
        System.out.println();

        boolean isValid = verify(testData, signature, publicKey);
        System.out.println("验签结果: " + isValid);
        System.out.println();

        if (!isValid) {
            System.err.println("验签失败!");
        } else {
            System.out.println("验签成功!");
        }

        System.out.println("\n=== SM2 加密解密测试 ===\n");

        // 加密解密测试
        String originalText = "Hello SM2";
        System.out.println("原始数据: " + originalText);

        String encrypted = encrypt(originalText, publicKey);
        System.out.println("原始数据: " + originalText);

        String decrypted = decrypt(encrypted, privateKey);
        System.out.println("解密结果: " + decrypted);
        System.out.println();

        if (originalText.equals(decrypted)) {
            System.out.println("加密解密成功!");
        } else {
            System.err.println("加密解密失败!");
        }
    }
}

package top.sunrains;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.Security;

/**
 *         <dependency>
 *             <groupId>org.bouncycastle</groupId>
 *             <artifactId>bcprov-jdk15to18</artifactId>
 *             <version>1.69</version>
 *         </dependency>
 *
 * SM4国密算法工具类
 * 使用Bouncy Castle原生API
 * 配置信息:
 * - 算法:SM4
 * - 模式:CBC(密码分组链接)
 * - 填充:PKCS5Padding
 * - 密钥长度:128位(16字节)
 * - IV长度:128位(16字节)
 */
public class Sm4Util {

    static {
        try {
            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
//        String key = generateKeyHex();
//        String iv = generateIvHex();
        //String s = encryptHex("123", key, iv);
        String key = "7f9a942a1aa85c331d3697a5369a37fe";
        String iv = "cbc07750d33d6a3ef6d39fc7e7c4a876";

        String s = "09187a80e9f52e7e962a3465ed70669e";
        System.out.println(s);
        String decryptedHex = decryptHex(s, key, iv);
        System.out.println(decryptedHex);
    }

    /**
     * 生成SM4密钥(16字节,128位)
     * @return 十六进制编码的密钥字符串(32个字符)
     */
    public static String generateKeyHex() {
        byte[] keyBytes = new byte[16];
        new SecureRandom().nextBytes(keyBytes);
        return byteArrayToHex(keyBytes);
    }

    /**
     * 生成IV向量(16字节,128位)
     * @return 十六进制编码的IV字符串(32个字符)
     */
    public static String generateIvHex() {
        byte[] ivBytes = new byte[16];
        new SecureRandom().nextBytes(ivBytes);
        return byteArrayToHex(ivBytes);
    }

    /**
     * SM4-CBC加密(字符串到HEX)
     * 标准加密方式
     *
     * @param plaintext 明文字符串(UTF-8)
     * @param keyHex 十六进制密钥字符串
     * @param ivHex 十六进制IV字符串
     * @return 加密后的HEX字符串
     * @throws Exception 加密异常
     */
    public static String encryptHex(String plaintext, String keyHex, String ivHex) throws Exception {
        byte[] key = decodeKeyHex(keyHex);
        byte[] iv = decodeIvHex(ivHex);
        byte[] data = plaintext.getBytes("UTF-8");

        byte[] encrypted = encrypt(data, key, iv);
        return byteArrayToHex(encrypted);
    }

    /**
     * SM4-CBC加密
     *
     * @param data 原始数据字节数组
     * @param key 密钥字节数组(16字节)
     * @param iv IV字节数组(16字节)
     * @return 加密后的字节数组
     * @throws Exception 加密异常
     */
    public static byte[] encrypt(byte[] data, byte[] key, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(key, "SM4"),
                new IvParameterSpec(iv));
        return cipher.doFinal(data);
    }

    /**
     * byte[] 转 Hex
     *
     * @param bytes 字节数组
     * @return HEX字符串(小写)
     */
    public static String byteArrayToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString().toLowerCase();
    }

    public static String decryptHex(String encryptedHex, String keyHex, String ivHex) throws Exception {
        byte[] key = decodeKeyHex(keyHex);
        byte[] iv = decodeIvHex(ivHex);
        byte[] encryptedData = hexToByteArray(encryptedHex);

        byte[] decrypted = decrypt(encryptedData, key, iv);
        return new String(decrypted, "UTF-8");
    }

    /**
     * SM4-CBC解密
     *
     * @param encryptedData 加密数据字节数组
     * @param key 密钥字节数组(16字节)
     * @param iv IV字节数组(16字节)
     * @return 解密后的原始字节数组
     * @throws Exception 解密异常
     */
    public static byte[] decrypt(byte[] encryptedData, byte[] key, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "BC");
        cipher.init(Cipher.DECRYPT_MODE,
                new SecretKeySpec(key, "SM4"),
                new IvParameterSpec(iv));
        return cipher.doFinal(encryptedData);
    }

    /**
     * 从十六进制字符串还原密钥字节数组
     * @param keyHex 十六进制编码的密钥字符串
     * @return 密钥字节数组(16字节)
     */
    public static byte[] decodeKeyHex(String keyHex) {
        return hexToByteArray(keyHex);
    }

    /**
     * 从十六进制字符串还原IV字节数组
     * @param ivHex 十六进制编码的IV字符串
     * @return IV字节数组(16字节)
     */
    public static byte[] decodeIvHex(String ivHex) {
        return hexToByteArray(ivHex);
    }

    /**
     * HEX 转 byte[]
     *
     * @param hex HEX字符串
     * @return 字节数组
     */
    public static byte[] hexToByteArray(String hex) {
        if (hex == null || hex.length() == 0) {
            return new byte[0];
        }

        int len = hex.length() / 2;
        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            int high = Character.digit(hex.charAt(i * 2), 16);
            int low = Character.digit(hex.charAt(i * 2 + 1), 16);
            bytes[i] = (byte) ((high << 4) | low);
        }
        return bytes;
    }

    public static String stringToHex(String str) {
        if (str == null || str.isEmpty()) {
            return "";
        }
        byte[] bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_8);
        return byteArrayToHex(bytes);
    }
}

package top.sunrains;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * AES对称加密算法工具类
 * 使用Java原生Crypto API
 * 配置信息:
 * - 算法:AES-256
 * - 模式:CBC(密码分组链接)
 * - 填充:PKCS5Padding
 * - 密钥长度:256位(32字节)
 * - IV长度:128位(16字节)
 */
public class AesUtil {

    /**
     * 生成AES密钥和IV
     * @return Map包含key (32字符) 和 iv (16字符)
     */
    public static Map<String, String> generateKey() {
        try {
            // 生成32字节的随机字符串作为密钥
            String key = generateRandomString(32);

            // 生成16字节的随机字符串作为IV
            String iv = generateRandomString(16);

            System.out.println("AES Key: " + key + " (长度: " + key.length() + ")");
            System.out.println("AES IV: " + iv + " (长度: " + iv.length() + ")");

            Map<String, String> result = new HashMap<>();
            result.put("key", key);
            result.put("iv", iv);

            return result;
        } catch (Exception e) {
            System.err.println("AES 密钥生成失败: " + e.getMessage());
            e.printStackTrace();
            return new HashMap<>();
        }
    }

    /**
     * 生成指定长度的随机字符串
     */
    private static String generateRandomString(int length) {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        SecureRandom random = new SecureRandom();
        StringBuilder result = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            result.append(chars.charAt(random.nextInt(chars.length())));
        }
        return result.toString();
    }

    /**
     * AES-CBC加密
     *
     * @param plaintext 明文字符串(UTF-8)
     * @param keyStr 密钥字符串(32字符)
     * @param ivStr IV字符串(16字符)
     * @return Base64编码的密文
     * @throws Exception 加密异常
     */
    public static String encrypt(String plaintext, String keyStr, String ivStr) throws Exception {
        // 将字符串Key和IV转换为字节数组(UTF-8编码)
        byte[] keyBytes = keyStr.getBytes("UTF-8");
        byte[] ivBytes = ivStr.getBytes("UTF-8");

        // 创建密钥和IV
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

        // 创建Cipher对象(AES/CBC/PKCS5Padding)
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        // 执行加密
        byte[] encrypted = cipher.doFinal(plaintext.getBytes("UTF-8"));

        // 返回Base64编码的密文
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * AES-CBC解密
     *
     * @param encryptedBase64 Base64编码的密文
     * @param keyStr 密钥字符串(32字符)
     * @param ivStr IV字符串(16字符)
     * @return 解密后的明文字符串
     * @throws Exception 解密异常
     */
    public static String decrypt(String encryptedBase64, String keyStr, String ivStr) throws Exception {
        // 将字符串Key和IV转换为字节数组(UTF-8编码)
        byte[] keyBytes = keyStr.getBytes("UTF-8");
        byte[] ivBytes = ivStr.getBytes("UTF-8");

        // 创建密钥和IV
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

        // 创建Cipher对象(AES/CBC/PKCS5Padding)
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

        // 将Base64密文解码为字节
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64);

        // 执行解密
        byte[] decrypted = cipher.doFinal(encryptedBytes);

        // 返回明文字符串
        return new String(decrypted, "UTF-8");
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) throws Exception {
        System.out.println("=== AES 工具类测试 ===\n");

        // 生成密钥
        Map<String, String> keyMap = generateKey();
        String key = keyMap.get("key");
        String iv = keyMap.get("iv");

        System.out.println("密钥: " + key);
        System.out.println("IV: " + iv);
        System.out.println();

        // 加密测试
        String originalText = "Hello AES";
        System.out.println("原始数据: " + originalText);

        String encrypted = encrypt(originalText, key, iv);
        System.out.println("加密结果: " + encrypted);
        System.out.println();

        // 解密测试
        String decrypted = decrypt(encrypted, key, iv);
        System.out.println("解密结果: " + decrypted);
        System.out.println();

        if (originalText.equals(decrypted)) {
            System.out.println("✅ 加密解密成功!");
        } else {
            System.err.println("❌ 加密解密失败!");
        }
    }
}

package top.sunrains;

import cn.hutool.core.text.CharSequenceUtil;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Security;
/**
 * SM3国密哈希算法工具类
 * 使用Bouncy Castle原生API
 * 功能:
 * - 字符串哈希计算
 * - 字节数组哈希计算
 * - 文件哈希计算
 * - HMAC-SM3计算
 */
public class SM3Util {

    static {
        try {
            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static final int BUFFER_SIZE = 8192;

    /**
     * 计算字符串的SM3哈希值
     *
     * @param data 待哈希的字符串
     * @return 哈希值(小写十六进制字符串,64个字符)
     */
    public static String hash(String data) {
        if (data == null) {
            throw new IllegalArgumentException("Data cannot be null");
        }
        return hash(data.getBytes(java.nio.charset.StandardCharsets.UTF_8));
    }

    /**
     * 验证字符串的SM3哈希值
     *
     * @param data 原始数据
     * @param expectedHash 期望的哈希值
     * @return 是否匹配
     */
    public static boolean verify(String data, String expectedHash) {
        String actualHash = hash(data);
        return actualHash.equalsIgnoreCase(expectedHash);
    }

    /**
     * 计算字节数组的SM3哈希值
     *
     * @param data 待哈希的字节数组
     * @return 哈希值(小写十六进制字符串,64个字符)
     */
    public static String hash(byte[] data) {
        SM3Digest digest = new SM3Digest();
        digest.update(data, 0, data.length);
        byte[] result = new byte[digest.getDigestSize()];
        digest.doFinal(result, 0);
        return byteArrayToHex(result);
    }

    /**
     * 计算文件的SM3哈希值(支持大文件)
     *
     * @param filePath 文件路径
     * @return 哈希值(小写十六进制字符串,64个字符)
     * @throws IOException 文件读取异常
     */
    public static String hashFile(String filePath) throws IOException {
        return hashFile(new File(filePath));
    }

    /**
     * 计算文件的SM3哈希值(支持大文件)
     *
     * @param file 文件对象
     * @return 哈希值(小写十六进制字符串,64个字符)
     * @throws IOException 文件读取异常
     */
    public static String hashFile(File file) throws IOException {
        if (!file.exists()) {
            throw new IOException("File not found: " + file.getAbsolutePath());
        }

        SM3Digest digest = new SM3Digest();
        byte[] buffer = new byte[BUFFER_SIZE];

        try (InputStream is = new FileInputStream(file)) {
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
            }
        }

        byte[] result = new byte[digest.getDigestSize()];
        digest.doFinal(result, 0);
        return byteArrayToHex(result);
    }

    /**
     * 计算HMAC-SM3(基于密钥的哈希消息认证码)
     *
     * @param data 待计算的数据
     * @param hexKey 密钥
     * @return HMAC-SM3结果(小写十六进制字符串,64个字符)
     */
    public static String hmac(String data, String hexKey) {
        if (CharSequenceUtil.isBlank(hexKey)) {
            throw new IllegalArgumentException("密钥不能为空");
        }
        return hmac(data.getBytes(java.nio.charset.StandardCharsets.UTF_8),
                hexToByteArray(hexKey));
    }

    /**
     * 计算HMAC-SM3(基于密钥的哈希消息认证码)
     *
     * @param data 待计算的字节数组
     * @param hexKey hex密钥字节数组
     * @return HMAC-SM3结果(小写十六进制字符串,64个字符)
     */
    public static String hmac(byte[] data, byte[] hexKey) {
        HMac hmac = new HMac(new SM3Digest());
        hmac.init(new KeyParameter(hexKey));
        hmac.update(data, 0, data.length);
        byte[] result = new byte[hmac.getMacSize()];
        hmac.doFinal(result, 0);
        return byteArrayToHex(result);
    }

    /**
     * byte[] 转 Hex
     *
     * @param bytes 字节数组
     * @return HEX字符串(小写)
     */
    private static String byteArrayToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString().toLowerCase();
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) throws IOException {
        String testData = "12";
        System.out.println("SM3: " + hash(testData));
        String key = "1";
        key = stringToHex(key);
        System.out.println("HMAC-SM3 key: " + key);
        String hmacResult = hmac(testData, key);
        System.out.println("HMAC-SM3: " + hmacResult);
        System.out.println("HMAC长度: " + hmacResult.length() + " 字符");
       System.out.println("文件哈希值: " +  hashFile("/Users/sun/1/t.jpg"));
    }

    public static String stringToHex(String str) {
        if (str == null || str.isEmpty()) {
            return "";
        }
        byte[] bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_8);
        return byteArrayToHex(bytes);
    }

    /**
     * HEX 转 byte[]
     *
     * @param hex HEX字符串
     * @return 字节数组
     */
    public static byte[] hexToByteArray(String hex) {
        if (hex == null || hex.length() == 0) {
            return new byte[0];
        }

        int len = hex.length() / 2;
        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            int high = Character.digit(hex.charAt(i * 2), 16);
            int low = Character.digit(hex.charAt(i * 2 + 1), 16);
            bytes[i] = (byte) ((high << 4) | low);
        }
        return bytes;
    }
}

各平台实现说明

Android 平台

SM2 实现:

  • 依赖库:BouncyCastle (org.bouncycastle:bcprov-jdk15on:1.70)
  • 特点
    • 使用 ECKeyPairGenerator 生成密钥对
    • 使用 SM2Signer 进行签名和验签(DER 格式)
    • 使用 SM2Engine 进行加密和解密(C1C3C2 格式)
    • 不依赖 JCA Provider,避免兼容性问题

SM4 实现:

  • 依赖库:BouncyCastle
  • 特点
    • 使用 SM4Engine 进行加密和解密
    • CBC 模式 + PKCS7 填充
    • 支持自定义密钥和 IV

iOS 平台

实现方式:GMObjC 原生库

SM2 特点:

  • 使用 GMSm2Utils 进行所有操作
  • 签名自动转换为 DER 格式以兼容其他平台
  • 加密输出 C1C3C2 格式(04 开头)
  • 支持跨平台互通(Android/HarmonyOS)

SM4 特点:

  • 使用 GMSm4Utils 进行加密解密
  • CBC 模式 + PKCS7 填充
  • 密钥和 IV 为 32 位十六进制字符串

HarmonyOS 平台

实现方式: @ohos.security.cryptoFramework

SM2 特点:

  • 使用 HarmonyOS 原生 cryptoFramework API
  • 签名使用 DER 格式
  • 加密使用 C1C3C2 格式
  • 完全符合国密标准

SM3 特点:

  • 使用 createMd (MessageDigest) 进行普通哈希
  • 使用手动实现 HMAC-SM3 (RFC 2104)
  • 支持流式文件哈希处理 (4096 字节缓冲区)
  • 不依赖 createMac,避免密钥参数问题

SM4 特点:

  • 使用 createSymKeyGenerator 生成密钥
  • 使用 createCipher 进行加密解密
  • CBC 模式 + PKCS7 填充
  • 支持 SM4_128 算法

AES 特点:

  • 使用 crypto.createSymKeyGenerator('AES256') 创建密钥生成器
  • 使用 crypto.createCipher('AES|CBC|PKCS7') 进行加解密
  • CBC 模式 + PKCS7 填充
  • 密钥和 IV 为随机字符串(UTF-8 编码)

H5 平台

实现方式:sm-crypto + Web Crypto API

特点:

  • SM2/SM3/SM4:通过 npm 包 sm-crypto 实现
  • AES:使用浏览器原生 Web Crypto API 实现
  • 所有功能均在浏览器环境中运行
  • SM2 签名使用 DER 格式(der: true
  • SM2 默认 userId 为 '1234567812345678'

依赖安装:

npm install --save sm-crypto

微信小程序平台

实现方式:miniprogram-sm-crypto + crypto-js + jsrsasign

特点:

  • 专为小程序优化的国密算法库
  • 完全支持 SM2、SM4 和 AES
  • SM2:密钥生成、签名、验签、加密、解密(使用 miniprogram-sm-crypto)
  • SM4:密钥生成、加密、解密(CBC 模式,使用 miniprogram-sm-crypto)
  • AES:密钥生成、加密、解密(CBC 模式,使用 crypto-js)
  • 需要通过微信开发者工具构建 npm

依赖安装:

npm install --save miniprogram-sm-crypto crypto-js jsrsasign

重要:微信小程序 npm 构建步骤

  1. 确保项目根目录已安装上述依赖

  2. 打开微信开发者工具,勾选「详情」→「本地设置」→「使用 npm 模块」

  3. 点击菜单栏「工具」→「构建 npm」,生成 miniprogram_npm 目录

  4. 重新编译运行小程序

注意事项

1. 密钥格式

SM2 密钥:

  • 公钥:以 04 开头的 130 位十六进制字符串(非压缩格式)
  • 私钥:64 位十六进制字符串

SM4 密钥:

  • 密钥 (key):32 位十六进制字符串(128 位)
  • 初始向量 (IV):32 位十六进制字符串(128 位)

AES 密钥:

  • 密钥 (key):32 字符随机字符串(字母+数字,UTF-8 编码后为 256 位)
  • 初始向量 (IV):16 字符随机字符串(字母+数字,UTF-8 编码后为 128 位)

2. 签名格式

  • 所有平台的签名输出均为 DER 编码的十六进制字符串
  • DER 格式确保跨平台兼容性

3. 哈希格式

SM3 哈希:

  • 输出长度:64 位十六进制字符串 (256 位)
  • 格式:小写十六进制
  • 示例:a1b2c3d4e5f6... (64 个字符)

HMAC-SM3 哈希:

  • 输出长度:64 位十六进制字符串 (256 位)
  • 密钥格式:十六进制字符串
  • 格式:小写十六进制

4. 加密格式

SM2 加密:

  • 输出格式:C1C3C2(以 04 开头)
  • 兼容 Android BouncyCastle、iOS GMObjC、HarmonyOS cryptoFramework

SM4 加密:

  • 模式:CBC
  • 填充:PKCS7
  • 输出:十六进制字符串

AES 加密:

  • 模式:CBC
  • 填充:PKCS5/PKCS7
  • 密钥:32 字符随机字符串(UTF-8 编码后为 256 位)
  • IV:16 字符随机字符串(UTF-8 编码后为 128 位)
  • 输出:Base64 编码字符串

RSA 加密:

  • 算法:RSA-OAEP with SHA-256 (对应 Java 的 RSA/ECB/OAEPWithSHA-256AndMGF1Padding)
  • 密钥长度:2048 位
  • 私钥格式:PKCS#8 (Base64 编码)
  • 公钥格式:X.509/SPKI (Base64 编码)
  • 输出:Base64 编码字符串
  • 跨平台兼容:Android/iOS/HarmonyOS/H5 完全互通

RSA 签名:

  • 算法:SHA256withRSA (PKCS#1 v1.5)
  • 私钥格式:PKCS#8 (Base64 编码)
  • 公钥格式:X.509/SPKI (Base64 编码)
  • 签名输出:Base64 编码字符串
  • 跨平台兼容:Android/iOS/HarmonyOS/H5/微信小程序 完全互通

5. 跨平台兼容性

完全兼容:

  • 各平台生成的 SM2 密钥对可以互相使用
  • Android 平台签名的数据可以在 iOS/Harmony/H5 平台验证
  • SM2 加密的数据可以在不同平台间解密
  • SM4 加密的数据可以在 Android/iOS/Harmony 平台间加解密
  • AES 加密的数据可以在所有平台间加解密(Android/iOS/HarmonyOS/H5/微信小程序)
  • SM3 哈希值在所有平台保持一致(相同输入产生相同输出)
  • HMAC-SM3 支持跨平台验证
  • RSA-OAEP-SHA256 加密的数据可以在 Android/iOS/HarmonyOS/H5 平台间加解密
  • SHA256withRSA 签名可以在所有平台间验签(包括微信小程序)

⚠️ 注意事项:

  • 确保使用相同的哈希选项(默认启用)
  • SM4/AES 加密和解密必须使用相同的密钥和 IV
  • 微信小程序需要先构建 npm 才能使用
  • SM3 文件哈希需要确保文件内容完全一致
  • AES 密钥为 32 字符随机字符串,IV 为 16 字符随机字符串
  • RSA 密钥长度为 2048 位,私钥为 PKCS#8 格式,公钥为 X.509/SPKI 格式
  • RSA 加密使用 OAEP with SHA-256 填充,与其他平台互通
  • RSA 签名使用 SHA256withRSA (PKCS#1 v1.5),所有平台兼容
  • 微信小程序端暂不支持 RSA-OAEP-SHA256 加密解密,仅支持签名验签

开发文档

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。