国密算法—SM2介绍及基于BC的实现

文章目录

  • 国密算法—SM2介绍及基于BC的实现
    • 简介
    • 私钥
    • 公钥
    • 数据格式
      • 密钥数据格式
        • 私钥数据格式
        • 公钥数据格式
      • 加密数据格式
      • 签名数据格式
    • 计算过程
      • 生成密钥
      • 加密
      • 解密
      • 预处理
        • 预处理1
        • 预处理2
      • 数字签名
      • 签名验证
    • Java基于BC实现SM2加解密

简介

SM2密码算法是一种椭圆(非对称)密码算法

  • 加密强度:256位(私钥长度);
  • 公私钥长度:公钥长度为64字节(512位),私钥32字节(256位);
  • 支持签名最大数据量及签名结果长度:最大签名数据量长度无限制;签名结果为64字节(但由于签名后会做ASN.1编码,实际输出长度为70-72字节);
  • 支持加密最大数据量及加密后结果长度:支持最大近128G字节数据长度;加密结果(C=C1C3C2)增加96字节【C1(64字节) + C3(32字节)】(如果首个字节为0x04则增加97字节,实际有效96字节)

私钥

SM2私钥是一个大于1且小于n-1的整数(n为SM2算法的阶,其值参见GM/T 0003),简记为k,长度为256位(32字节)。

公钥

SM2公钥是SM2曲线上的一个点,由横坐标和纵坐标两个分量来表示,记为(x,y),简记为Q,每个分量的长度为256位,总长度为512位(64字节,不包含公钥标识)。

数据格式

密钥数据格式
私钥数据格式

SM2算法私钥数据格式的ASN.1定义为:

SM2PrivateKey::=INTEGER
公钥数据格式

SM2算法公钥数据格式的ASN.1定义为:

SM2PublicKey::=BIT STRING

SM2PublicKey为BIT STRING类型,内容为04||X|||Y,其中X和Y分别标示公钥的x分量和y的分量,其长度各位256位。04用来标示公钥为非压缩格式(压缩格式用02标识)。

加密数据格式

SM2算法加密后的数据格式的ASN.1定义为:

SM2Cipher::= SEQENCE{XCoordinate        INTEGER,                 --x 分量 32字节(256位)YCoordinate      INTEGER,                 --y 分量 32字节(256位)HASH             OCTET STRING SIZE(32),      --杂凑值 32字节(256位)CipherText        OCTET STRING                --密文   等于明文长度
}

其中,HASH为使用SM3算法对明文数据运算得到的杂凑值,其长度固定为256位。CipherText是与明文等长的密文。因此SM2加密后的密文长度比明文长度增加了97字节(1字节04标识 + 32字节x分量 + 32字节y分量 + 32字节Hash

签名数据格式

SM2算法签名数据格式的ASN.1定义为:

SM2Signature::={R       INTEGER,    --签名值的第一部分 32字节(256位)S        INTEGER     --签名值的第二部分  32字节(256位)
}

R和S的长度各位32字节。因此签名后的数据长度为固定的64字节

计算过程

生成密钥

SM2密钥生成是指生成SM2算法的密钥对的过程,该密钥对包括私钥与之对应的公钥。其中,私钥长度为256位,公钥长度为512位。

  • 输入

  • 输出

    k SM2PrivateKey SM2私钥 256位

    Q SM2PublicKey SM2公钥 512位

详细计算过程参见GM/T 0003

加密

SM2加密是指使用指定的公开密钥对明文进行特定的加密计算,生成相应密文的过程。该密文只能由该指定公开密钥对应的私钥解密。

  • 输入

    Q SM2PublicKey SM2公钥

    m 字节串 待加密的明文数据

  • 输出

    c SM2Cipher 密文

其中:

  1. 输出参数c的格式由本规范7.2中定义;

  2. 输出参数c中的XCoordinateYCoordinate俗称C1)为随机产生的公钥的x分量和y分量256位;

  3. 输出参数c中的HASH俗称C3)的计算公式为HASH = SM3(x||m||y),其中,x,y为公钥Q的x分量和y分量;

  4. 输出参数c中的CipherText俗称C2)为加密密文,其长度等于明文长度。

详细的计算过程参见GM/T 0003和GM/T 0004。

解密

SM2解密是指使用指定的私钥对密文进行解密计算,还原对应明文的过程。

  • 输入

    d SM2PrivateKey SM2私钥

    c SM2Cipher 密文

  • 输出

    m 字节串 与密文对应的明文

m为SM2Cipher经过解密运算得到明文,该明文的长度与输入参数c中的CipherText(俗称C2)的长度相同。

详细的计算过程参见GM/T 0003。

预处理
预处理1

预处理1是指使用签名方的用户身份标识和签名方公钥,通过运算得到Z指的过程。Z值用于预处理2,也用于SM2密钥协商协议。

  • 输入

    ID 字节串 用户身份标识

    Q SM2PublicKey 用户的公钥

  • 输出

    Z 字节串 预处理1的输出

计算公式为:

Z = SM3(ENTL||ID||a||b||XG||yG||XA||yA)

其中:

  • ENTL 为由2个字节标示的ID的比特长度;
  • ID 为用户身份标识;
  • a、b 为系统曲线参考;
  • XG、yG 为基点;
  • XA、yA 为用户公钥;

详细计算过程参见GM/T 0003 和 GM/T 0004。

预处理2

预处理2是指使用Z值和待签名消息,通过SM3运算得到的杂凑值H的过程。杂凑值H用于SM2数字签名。

  • 输入

    Z 字节串 预处理2的输入

    M 字节串 待签名消息

  • 输出

    H 字节串 杂凑值

计算公式为:

H = SM3(Z||M)

详细计算过程参见GM/T 0003 和 GM/T 0004。

数字签名

SM2签名是指使用预处理的结果和签名者的私钥,通过签名计算得到签名结果的过程。

  • 输入

    d SM2Privatekey 签名者私钥

    H 字节串 预处理2的结果

  • 输出

    sign SM2Signature 签名值

详细计算过程参见GM/T 0003

签名验证

SM2签名验证是指使用预处理2的结果、签名值和签名者的公钥。通过验签计算确定签名是否通过验证的过程。

  • 输入

    H 字节串 预处理2的结果

    sign SM2Signature 签名值

    Q PublicKey 签名者的公钥

  • 输出

    :表示验证通过;为:表示验证不通过

详细计算过程参见GM/T 0003

Java基于BC实现SM2加解密

BouncyCastle在1.57版本已经提供对SM椭圆曲线密码算法的支持,不过对SM2仅提供C1C2C3模式的加解密。在1.62+版本中已经增加了对C1C3C2模式加解密的支持。

 public enum Mode{C1C2C3, C1C3C2;}

这里使用BouncyCastle 1.68版本进行测试

@SpringBootTest
class DemoApplicationTests {@Testvoid contextLoads() {testSM2();}static void testSM2() {String publicKeyHex = null;String privateKeyHex = null;KeyPair keyPair = createECKeyPair();PublicKey publicKey = keyPair.getPublic();if (publicKey instanceof BCECPublicKey) {//获取65字节非压缩缩的十六进制公钥串(0x04)publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));System.out.println("---->SM2公钥:" + publicKeyHex);}PrivateKey privateKey = keyPair.getPrivate();if (privateKey instanceof BCECPrivateKey) {//获取32字节十六进制私钥串privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);System.out.println("---->SM2私钥:" + privateKeyHex);}//TODO...可对以上公钥进行分发传输/*** 公钥加密*/String data = "=========待加密数据=========";//将十六进制公钥串转换为 BCECPublicKey 公钥对象BCECPublicKey bcecPublicKey = getECPublicKeyByPublicKeyHex(publicKeyHex);String encryptData = encrypt(bcecPublicKey, data, 1);System.out.println("---->加密结果:" + encryptData);/*** 私钥解密*///将十六进制私钥串转换为 BCECPrivateKey 私钥对象BCECPrivateKey bcecPrivateKey = getBCECPrivateKeyByPrivateKeyHex(privateKeyHex);data = decrypt(bcecPrivateKey, encryptData, 1);System.out.println("---->解密结果:" + data);} /*** 生成密钥对*/static KeyPair createECKeyPair() {//使用标准名称创建EC参数生成的参数规范final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");// 获取一个椭圆曲线类型的密钥对生成器final KeyPairGenerator kpg;try {kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());// 使用SM2算法域参数集初始化密钥生成器(默认使用以最高优先级安装的提供者的 SecureRandom 的实现作为随机源)// kpg.initialize(sm2Spec);// 使用SM2的算法域参数集和指定的随机源初始化密钥生成器kpg.initialize(sm2Spec, new SecureRandom());// 通过密钥生成器生成密钥对return kpg.generateKeyPair();} catch (Exception e) {e.printStackTrace();return null;}}  /*** 公钥加密** @param publicKey SM2公钥* @param data      明文数据* @param modeType  加密模式* @return*/public static String encrypt(BCECPublicKey publicKey, String data, int modeType) {//加密模式SM2Engine.Mode mode;if (modeType == 1) {mode = SM2Engine.Mode.C1C3C2;} else {mode = SM2Engine.Mode.C1C2C3;}//通过公钥对象获取公钥的基本域参数。ECParameterSpec ecParameterSpec = publicKey.getParameters();ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),ecParameterSpec.getG(), ecParameterSpec.getN());//通过公钥值和公钥基本参数创建公钥参数对象ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);//根据加密模式实例化SM2公钥加密引擎SM2Engine sm2Engine = new SM2Engine(mode);//初始化加密引擎sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));byte[] arrayOfBytes = null;try {//将明文字符串转换为指定编码的字节串byte[] in = data.getBytes("utf-8");//通过加密引擎对字节数串行加密arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);} catch (Exception e) {System.out.println("SM2加密时出现异常:" + e.getMessage());e.printStackTrace();}//将加密后的字节串转换为十六进制字符串return Hex.toHexString(arrayOfBytes);}/*** 私钥解密** @param privateKey SM私钥* @param cipherData 密文数据* @return*/public static String decrypt(BCECPrivateKey privateKey, String cipherData, int modeType) {//解密模式SM2Engine.Mode mode;if (modeType == 1) {mode = SM2Engine.Mode.C1C3C2;} else {mode = SM2Engine.Mode.C1C2C3;}//将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)byte[] cipherDataByte = Hex.decode(cipherData);//通过私钥对象获取私钥的基本域参数。ECParameterSpec ecParameterSpec = privateKey.getParameters();ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),ecParameterSpec.getG(), ecParameterSpec.getN());//通过私钥值和私钥钥基本参数创建私钥参数对象ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),ecDomainParameters);//通过解密模式创建解密引擎并初始化SM2Engine sm2Engine = new SM2Engine(mode);sm2Engine.init(false, ecPrivateKeyParameters);String result = null;try {//通过解密引擎对密文字节串进行解密byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);//将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)result = new String(arrayOfBytes, "utf-8");} catch (Exception e) {System.out.println("SM2解密时出现异常" + e.getMessage());}return result;}//椭圆曲线ECParameters ASN.1 结构private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");//椭圆曲线公钥或私钥的基本域参数。private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());/*** 公钥字符串转换为 BCECPublicKey 公钥对象** @param pubKeyHex 64字节十六进制公钥字符串(如果公钥字符串为65字节首个字节为0x04:表示该公钥为非压缩格式,操作时需要删除)* @return BCECPublicKey SM2公钥对象*/public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {//截取64字节有效的SM2公钥(如果公钥首个字节为0x04)if (pubKeyHex.length() > 128) {pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);}//将公钥拆分为x,y分量(各32字节)String stringX = pubKeyHex.substring(0, 64);String stringY = pubKeyHex.substring(stringX.length());//将公钥x、y分量转换为BigInteger类型BigInteger x = new BigInteger(stringX, 16);BigInteger y = new BigInteger(stringY, 16);//通过公钥x、y分量创建椭圆曲线公钥规范ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecDomainParameters);//通过椭圆曲线公钥规范,创建出椭圆曲线公钥对象(可用于SM2加密及验签)return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);} /*** 私钥字符串转换为 BCECPrivateKey 私钥对象** @param privateKeyHex: 32字节十六进制私钥字符串* @return BCECPrivateKey:SM2私钥对象*/public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {//将十六进制私钥字符串转换为BigInteger对象BigInteger d = new BigInteger(privateKeyHex, 16);//通过私钥和私钥域参数集创建椭圆曲线私钥规范ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters);//通过椭圆曲线私钥规范,创建出椭圆曲线私钥对象(可用于SM2解密和签名)return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);}
}

测试结果

---->SM2公钥:045c798b7bdedff4ace6b935269b8cc79ddeac67000f4d4f94adbfc982398ce7b72343542b035a7b9242e4f470ce495d858d5129975d7ca207ddcfae59fc1eb66d
---->SM2私钥:466a7234630258356942a47fc32b848966c557aacbe7439d4a2bb397d0cf9144
---->加密结果:04bff9cab1e38d743f87b524dae8be97d3d0fce55d9f3771a5bcf02d9a6c162e96bdefb8e91ccffddacaecfb0281bbc22ae908484c5c1ecf57a7d654efc06f9a89b0ba182264eab6e0ba1a76b21edab10f4ee221b774bba979101a9e3c57affeeffcef48ead5039f4da57d8ea4b1541ec2a290530419b61cc99938f1f885fe10bb57
---->解密结果:=========待加密数据=========

国密算法—SM2介绍及基于BC的实现相关推荐

  1. 国密算法SM2证书制作

    原文链接:http://www.jonllen.com/jonllen/work/162.aspx 国密算法SM2证书制作 分类:工作 大中小 前段时间将系统的RSA算法全部升级为SM2国密算法,密码 ...

  2. 国密算法(SM2,SM3,SM4)完善与算法辅助工具开发

    国密算法SM2,SM3和改名发布的SM4的应用好像越来越多了.首先是国密SM2证书的升级,国内CA服务商要完成SM2算法证书支持,之后是国密算法在金融领域进行推广,新近编订的PBOC标准的增强安全部分 ...

  3. 国密算法 SM2 SM3 SM4 及密钥生成

    国密算法 SM2 SM3 SM4 方式一:SM2密钥在线生成 SM2密钥在线生成工具 如果你没线下生成工具,可用下面2种线上生成方式之一: 1. sm2密钥在线生成(const.net.cn) 2.  ...

  4. c++国密算法SM2加密解密demo

    c++国密算法SM2加密解密 一.代码 一.代码 封装加密.解密接口: 加密接口: Encrpt_SM2() 解密接口:Decrypt_SM2() 加密解密结果可以和nodejs的模块sm-crypt ...

  5. 国家医保移动支付国密算法SM2签名验签、SM4加解密DLL

    国家医保移动支付国密算法SM2签名验签.SM4加解密DLL 支持医保移动支付(国家统一版), 已知省份有广西.贵州.安徽.河北.黑龙江.湖南.吉林.江苏.四川.新疆等各地方. DLL,非.net开发, ...

  6. 国密算法 SM2公钥密码 SM3杂凑算法 SM4分组密码 python代码完整实现

    包含SM2公钥密码.SM3杂凑算法和SM4分组密码的国密算法完整工具包完成了.此前分别发布过上述三个算法的代码: SM2:国密算法 SM2 公钥加密 非对称加密 数字签名 密钥协商 python实现完 ...

  7. 小程序 js 库。国密算法 sm2、sm3 和 sm4 的实现

    sm-crypto 小程序 js 库.国密算法 sm2.sm3 和 sm4 的实现. 使用此组件需要依赖小程序基础库 2.2.1 以上版本,同时依赖开发者工具的 npm 构建.具体详情可查阅官方 np ...

  8. 国密算法概述介绍(SM1(SCB2)、SM2、SM3、SM4、SM7、SM9、祖冲之密码算法(ZUC))

    ttps://blog.csdn.net/SkyChaserYu/article/details/104039272 众所周知,为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,包 ...

  9. 国密算法SM2、SM3的使用

    一.SM2.SM3介绍: 1. SM2是非对称加密算法 它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,包含数字签名.密钥交换和公钥加密,用于替换RSA/DH/ECDSA/ECDH等国 ...

最新文章

  1. c语言程序既可以编译执行也可以解释执行,2016年山东农业大学信息科学与工程学院C语言程序设计(同等学力加试)复试笔试仿真模拟题...
  2. 宝藏文章!【CCNA命令大全】
  3. oracle 树状结构一直出现不了_深入解析Oracle ASSM 段头块(PAGETABLE SEGMENT HEADER)结构...
  4. 数据仓库之 ETL漫谈
  5. 【struts2】struts2的一些常用高级应用
  6. node中的Stream-Readable和Writeable解读
  7. 内存泄露检测工具比较
  8. OpenShift 4 之Istio-Tutorial (9) 访问限流
  9. 常见浏览器的兼容问题
  10. 集团公司(嵌入ETL工具)财务报表系统解决方案
  11. oracle 操作 卡,oracle 12c 数据库上编写SQL语句时卡顿的问题解决
  12. cad字体hztxt用什么代替_为什么CAD的字体库相同但打开图纸的效果却不同?
  13. (1)python基础语法
  14. 微信小程序视频开发教程
  15. Linux下查看网络流量常用方法
  16. 创建一个urdf机器人_ROS机器人Diego 1#制作(十六)创建机器人的urdf模型描述文件详解...
  17. 敌营十八年Ⅱ虎胆雄心
  18. uniapp 上传图片到华为云obs
  19. 微信小程序网络字体安卓实机无法加载解决方案
  20. drl 规则语言 语法_C语言基本语法规则

热门文章

  1. Kotlin高仿微信-第26篇-朋友圈-选择图片、小视频对话框
  2. 花菁染料|cas146368-08-3-齐岳生物
  3. 2022-2028全球与中国企业上云市场现状及未来发展趋势
  4. IPD电源REL-70-4006
  5. cisco 无线ap ME和LAP模式切换
  6. python环境搭建及应用
  7. 【八】【vlc-android】vlc-vout视频流输出端源码分析
  8. linux子进程崩溃父进程如何得知并重启子进程,子进程崩溃父进程如何得知并重启子进程...
  9. 回放线上流量利器-GoReplay
  10. react实现echarts的疫情地图