SM2密码算法是一种椭圆(非对称)密码算法
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 密文
其中:
输出参数c的格式由本规范7.2中定义;
输出参数c中的XCoordinate
、YCoordinate
(俗称C1
)为随机产生的公钥的x分量和y分量256位;
输出参数c中的HASH
(俗称C3
)的计算公式为HASH = SM3(x||m||y)
,其中,x,y为公钥Q的x分量和y分量;
输出参数c中的CipherText
(俗称C2
)为加密密文,其长度等于明文长度。
SM2解密是指使用指定的私钥对密文进行解密计算,还原对应明文的过程。
输入
d SM2PrivateKey SM2私钥
c SM2Cipher 密文
输出
m 字节串 与密文对应的明文
m为SM2Cipher经过解密运算得到明文,该明文的长度与输入参数c中的CipherText(俗称C2
)的长度相同。
详细的计算过程参见GM/T 0003。
预处理1是指使用签名方的用户身份标识和签名方公钥,通过运算得到Z指的过程。Z值用于预处理2,也用于SM2密钥协商协议。
输入
ID 字节串 用户身份标识
Q SM2PublicKey 用户的公钥
输出
Z 字节串 预处理1的输出
计算公式为:
Z = SM3(ENTL||ID||a||b||XG||yG||XA||yA)
其中:
详细计算过程参见GM/T 0003 和 GM/T 0004。
预处理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
BouncyCastle在1.57版本已经提供对SM椭圆曲线密码算法的支持,不过对SM2仅提供C1C2C3
模式的加解密。在1.62+版本中已经增加了对C1C3C2
模式加解密的支持。
public enum Mode
{
C1C2C3, C1C3C2;
}
这里使用BouncyCastle 1.68版本进行测试
@SpringBootTest
class DemoApplicationTests {
@Test
void 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
---->解密结果:=========待加密数据=========
文章浏览阅读331次。第一部分:准备工作1 安装虚拟机2 安装centos73 安装JDK以上三步是准备工作,至此已经完成一台已安装JDK的主机第二部分:准备3台虚拟机以下所有工作最好都在root权限下操作1 克隆上面已经有一台虚拟机了,现在对master进行克隆,克隆出另外2台子机;1.1 进行克隆21.2 下一步1.3 下一步1.4 下一步1.5 根据子机需要,命名和安装路径1.6 ..._创建一个hadoop项目
文章浏览阅读1.7k次。心脏滴血漏洞HeartBleed CVE-2014-0160 是由heartbeat功能引入的,本文从深入码层面的分析该漏洞产生的原因_heartbleed代码分析
文章浏览阅读1.4k次。前言ofd是国家文档标准,其对标的文档格式是pdf。ofd文档是容器格式文件,ofd其实就是压缩包。将ofd文件后缀改为.zip,解压后可看到文件包含的内容。ofd文件分析工具下载:点我下载。ofd文件解压后,可以看到如下内容: 对于xml文件,可以用文本工具查看。但是对于印章文件(Seal.esl)、签名文件(SignedValue.dat)就无法查看其内容了。本人开发一款ofd内容查看器,..._signedvalue.dat
文章浏览阅读1.8w次,点赞29次,收藏313次。整体系统设计本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。其整体系统框图如下:图1:FPGA数据采集系统框图从图中可以看出,该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、D.._基于fpga的信息采集
文章浏览阅读2.5w次。1.背景错误信息:-- [http-nio-9904-exec-5] o.s.c.n.z.filters.post.SendErrorFilter : Error during filteringcom.netflix.zuul.exception.ZuulException: Forwarding error at org.springframework.cloud..._com.netflix.zuul.exception.zuulexception
文章浏览阅读358次。1.介绍图的相关概念 图是由顶点的有穷非空集和一个描述顶点之间关系-边(或者弧)的集合组成。通常,图中的数据元素被称为顶点,顶点间的关系用边表示,图通常用字母G表示,图的顶点通常用字母V表示,所以图可以定义为: G=(V,E)其中,V(G)是图中顶点的有穷非空集合,E(G)是V(G)中顶点的边的有穷集合1.1 无向图:图中任意两个顶点构成的边是没有方向的1.2 有向图:图中..._给定一个邻接矩阵未必能够造出一个图
文章浏览阅读321次。(十二)、WDS服务器安装通过前面的测试我们会发现,每次安装的时候需要加域光盘映像,这是一个比较麻烦的事情,试想一个上万个的公司,你天天带着一个光盘与光驱去给别人装系统,这将是一个多么痛苦的事情啊,有什么方法可以解决这个问题了?答案是肯定的,下面我们就来简单说一下。WDS服务器,它是Windows自带的一个免费的基于系统本身角色的一个功能,它主要提供一种简单、安全的通过网络快速、远程将Window..._doc server2012上通过wds+mdt无人值守部署win11系统.doc
文章浏览阅读219次。python–xlrd/xlwt/xlutilsxlrd只能读取,不能改,支持 xlsx和xls 格式xlwt只能改,不能读xlwt只能保存为.xls格式xlutils能将xlrd.Book转为xlwt.Workbook,从而得以在现有xls的基础上修改数据,并创建一个新的xls,实现修改xlrd打开文件import xlrdexcel=xlrd.open_workbook('E:/test.xlsx') 返回值为xlrd.book.Book对象,不能修改获取sheett_xlutils模块可以读xlsx吗
文章浏览阅读8.2w次,点赞267次,收藏656次。运行Selenium出现'WebDriver' object has no attribute 'find_element_by_id'或AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'等定位元素代码错误,是因为selenium更新到了新的版本,以前的一些语法经过改动。..............._unresolved attribute reference 'find_element_by_id' for class 'webdriver
文章浏览阅读198次。一:模态窗口//父页面JSwindow.showModalDialog(ifrmehref, window, 'dialogWidth:550px;dialogHeight:150px;help:no;resizable:no;status:no');//子页面获取父页面DOM对象//window.showModalDialog的DOM对象var v=parentWin..._jquery获取父window下的dom对象
文章浏览阅读1.7w次,点赞15次,收藏129次。算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵 魂。二、算法的特征1.可行性 算法中执行的任何计算步骤都可以分解为基本可执行的操作步,即每个计算步都可以在有限时间里完成(也称之为有效性) 算法的每一步都要有确切的意义,不能有二义性。例如“增加x的值”,并没有说增加多少,计算机就无法执行明确的运算。 _算法
文章浏览阅读1.5k次,点赞18次,收藏26次。网络安全的标准和规范是网络安全领域的重要组成部分。它们为网络安全提供了技术依据,规定了网络安全的技术要求和操作方式,帮助我们构建安全的网络环境。下面,我们将详细介绍一些主要的网络安全标准和规范,以及它们在实际操作中的应用。_网络安全标准规范