首页 > 其他分享 >还原 SM2 压缩公钥的几种方法

还原 SM2 压缩公钥的几种方法

时间:2023-06-30 10:23:33浏览次数:47  
标签:公钥 压缩 SM2 算法 还原 new

写这篇文章的起因是朋友让我帮忙解决一个与 SM2 算法加密相关的问题。由于我对 SM2 算法并不熟悉,因此在解决问题的过程中走了很多弯路,花了很多时间去了解 SM2 算法以及如何通过代码还原压缩公钥。随着越来越多的系统采用国密算法,我们在与其对接时难免会遇到类似的问题。然而,关于这方面的资料在网上相对较少。因此,趁周末有空,我决定将还原压缩公钥的方法记录下来,希望对你有所帮助。

在介绍几种还原 SM2 压缩公钥的方法之前,让我们先了解一下什么是 SM2 压缩公钥。

文章会持续修订,转载请注明来源地址:https://her-cat.com/posts/2023/06/19/several-ways-to-decompress-sm2-compressed-public-key/

什么是 SM2 压缩公钥?

在 SM2 算法中,公钥的大小为 64 字节,算上前缀 04 的话就是 65 字节。公钥由椭圆曲线上的坐标点(x, y)组成,即每个坐标点都是 32 字节的大数。为了节省存储空间,通常会对公钥进行压缩后使用,也就是压缩公钥。

压缩公钥分别由前缀和坐标点 x 一共 33 字节组成,当坐标点 y 是偶数时,使用 02 作为前缀,否则使用 03 作为前缀。使用 16 进制字符串表示时,字符串长度为 66 个字符。

还原压缩公钥的原理:先通过压缩公钥的前缀,确定坐标点 y 是奇数还是偶数,然后根据椭圆曲线的公式计算得到完整的公钥。

没看懂?没关系,下面我介绍几种还原压缩公钥的方法,让你不需要知道原理也能还原压缩公钥。

第一种方法:使用在线工具

下面是我找到的两个比较好用的在线工具:

第一个网站,只要将压缩公钥粘贴到输入框,点击「还原公钥」按钮就可以得到完整的公钥,该工具输出的结果还需要去除其中的空格才能使用。

第二个网站,除了支持压缩公钥以外,还支持 HEX、PEM 格式的公钥,先将公钥粘贴到输入框,网站会自动将其转换成 PEM 格式(sm2p256v1),然后借助下面这段代码就能得到完整的公钥。

$pemStr = 'PEM 格式的公钥';
$pemStr = str_replace(['-----BEGIN PUBLIC KEY-----', '-----END PUBLIC KEY-----', PHP_EOL], '', $pemStr);
$uncompressedPublicKey = substr(bin2hex(base64_decode($pemStr)), -128);
echo "未压缩公钥:" . $uncompressedPublicKey;

第二种方法:使用 Java 的 Bouncy Castle 类库

虽然在线工具非常便捷,并且也能够达到我们想要的目的,但还是缺乏一些安全性,因为我们不清楚这些在线工具是否会收集信息,所以最好还是使用本地运行的代码来还原压缩公钥。然后我开始在网上搜索还原压缩公钥的相关资料,但找了很久都没有找到。于是我修改了搜索词,最后,我在某个使用 Java 基于 Bouncy Castle 封装 SM2 工具类的文章中找到了一些思路,也就有了 Java 版还原压缩公钥的代码。

import cn.hutool.core.util.HexUtil;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

public class Main {
    public static void main(String[] args) {
        String compressedPublicKey = "02 或 03 开头的压缩公钥";
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(compressedPublicKey));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
        String uncompressedPublicKey = HexUtil.encodeHexStr(publicKeyParameters.getQ().getEncoded(false));
        System.out.println("未压缩公钥:" + uncompressedPublicKey);
    }
}

第三种方法:使用 PHP 的 lpilp/guomi 包

实际上,一开始就只有上面 Java 版本的代码,在准备写这篇文章的时候,突然想到 PHP 应该也能实现才对,然后我又去研究了下怎么在 PHP 中实现还原压缩公钥。

在 PHP 中对于国密算法相关的操作,一般都是使用 lpilp/guomi 这个包,它实现了 SM2、SM3、SM4 国密算法,其中 SM2 算法是基于 mdanter/ecc 这个包实现的。虽然 lpilp/guomi 支持 SM2 算法相关的操作,但是对外提供的方法都只支持未压缩公钥,对于压缩公钥只能我们自己想办法。

于是,我开始研究它对外提供的这几个方法,最后在 RtSm2::verifySignOutKey 方法中找到了一些蛛丝马迹。

public function verifySignOutKey( $document, $sign, $publickeyFile, $userId = null ) {  
	...
    // Parse signature  
    $sigSerializer = new DerSignatureSerializer();  
    $sig = $sigSerializer->parse( $sigData );  
  
    // Parse public key  
    $keyData = file_get_contents( $publickeyFile );  
    $derSerializer = new DerPublicKeySerializer( $adapter );  
    $pemSerializer = new PemPublicKeySerializer( $derSerializer );  
    $key = $pemSerializer->parse( $keyData );  
  
    $pubKeyX = $this->decHex( $key->getPoint()->getX() );  
    $pubKeyY = $this->decHex( $key->getPoint()->getY() );  
    $hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId );  
    $signer = new Sm2Signer( $adapter );  
  
    return $signer->verify( $key, $sig, $hash );  
}

上面这段代码中的 $pubKeyY 不就是 SM2 算法中公钥的另一个坐标点 y 的值吗?将 $pubKeyX 和 $pubKeyY 拼接在一起就可以得到完整的公钥。经过一顿调试并将上面的代码进行简化后,就有了 PHP 版本的实现。

use Mdanter\Ecc\Serializer\Point\CompressedPointSerializer;
use Mdanter\Ecc\Serializer\Point\UncompressedPointSerializer;
use Rtgm\ecc\RtEccFactory;

$adapter = RtEccFactory::getAdapter();
$curve = RtEccFactory::getSmCurves()->curveSm2();

$compressedPublicKey = '02 或 03 开头的压缩公钥';

$compressedPointSerializer = new CompressedPointSerializer($adapter);
$point = $compressedPointSerializer->unserialize($curve, $key);

$uncompressedPointSerializer = new UncompressedPointSerializer();
$uncompressedPublicKey = $uncompressedPointSerializer->serialize($point);

echo "未压缩公钥:" . $uncompressedPublicKey;

总结

在本文中,我向你介绍了三种还原压缩公钥的方法。首先是使用在线工具,它们可以直接将压缩公钥转换为完整的公钥,使用起来比较方便,但缺乏了安全性。其次是使用 Java 的 Bouncy Castle 类库,通过编写代码来还原压缩公钥,保证了安全性和隐私性。最后是使用 PHP 的 lpilp/guomi 包,其效果与 Java 版本的一致。在实际使用中,你可以根据自己的需求来选择合适的方法。

标签:公钥,压缩,SM2,算法,还原,new
From: https://www.cnblogs.com/her-cat/p/several-ways-to-decompress-sm2-compressed-public-key.htm

相关文章

  • 数据库备份、还原与恢复
    开篇一张图,内容全靠编。一本正经de胡说八道:数据库备份、还原与恢复基本概念  如果了解过其它数据库(如oracle)的备份,肯定见过类似这样的一句话:普通完全备份不可以做为增量备份的基备份;或者,执行增量备份时,系统会自动做一次0级的备份(如果没有0级备份)。那为什么其它数据库不可以......
  • git 入门、reset的3种模式、回滚文件、还原文件、变基、merge
    git基础知识盗用网上的一张图,git有工作目录、索引区(也叫暂存区)、历史区,这3个区,一定要记在脑子里,基本上git所有的操作都是操作这3个区。新建一个文件并提交的一般操作是,新建文件gitadd到索引区gitcommit到历史区(添加-a参数会自动提交到索引区,相当于第2步+第3步)gitreset有3种......
  • gitlab备份与还原
    一.备份配置修改vim/etc/gitlab/gitlab.rbgitlab_rails['manage_backup_path']=truegitlab_rails['backup_path']="/data/gitlab-backups"//备份文件存储路径,如果没有配置,则备份数据到默认”/var/opt/gitlab/backups“下gitlab_rails['backup_archive_permi......
  • 椭圆曲线公钥加密
    (224条消息)椭圆曲线上两种基本的运算:点集运算、P+Q详解_椭圆曲线点加运算_怀恋的愤怒的博客-CSDN博客首先,了解一下这里的点加,接着就是基础流程了假设我们有一个要加密的消息M。加密过程如下:随机选择一个整数k。计算点P=kG。将P的x坐标作为密文的一部分。计算临时密钥K......
  • 深度学习助力版面分析技术,图像“还原”有方
    1.前言背景近期,2023年度视觉与学习青年学者研讨会(VisionAndLearningSEminar,VALSE)在无锡圆满落幕,此研讨会是图像视觉领域的重磅会议。作为智能文档处理领域代表的合合信息自然不会缺席,合合信息出席会议并进行智能文档处理技术研发与实践成果分享,重点介绍了其在版面分析与......
  • js加密与java解密的RSA(公钥/私钥)算法
    前言:公司用的加密算法是对称密钥加密算法,服务器上的公钥与客户端的私钥都是相同的,如果不小心泄露或者通过反编译软件把客户端重新编译就会泄露密钥,这样加密后的数据就不安全了。商量使用非对称密钥RSA来解决这种问题,非对称密钥分为公钥和私钥,公钥可以公开给客户端分发给所有......
  • 非root用户实现ssh免密码连接创建公钥
    非root用户实现ssh免密码连接创建公钥ssh-keygen-trsa,一直回车,会显示公钥位置【注意当前服务器有id_rsa.pub那么就不用生成】文件名作用known_hosts记录ssh访问过计算机的公钥(publickey)id_rsa生成的私钥id_rsa.pub生成的公钥authorized_keys......
  • ESXI的备份,备份虚拟机以防万一,简单易用版(还原篇)
    继上篇的备份完虚拟机的基础之上,这篇说说如何还原esxi虚拟机一、打开备份管理 新建还原 选择来源 选择对应的虚拟机 点确定就进入还原阶段。......
  • Day03 3.3 使用Python还原算法
    Day033.3使用Python还原算法加密分类1、单向加密:MD5、sha系列不可逆2、对称加密:AES、DES3、非对称加密:RSA、DSA4、补充算法:base64【一】md5importhashlibm=hashlib.md5()m.update('helloworld'.encode("utf8"))print(m.hexdigest())【二......
  • 使用 Easysearch 还原 Elasticsearch 快照数据
    本文主要验证Elasticsearch快照在Easysearch中进行数据恢复。准备测试数据索引别名模版生命周期策略创建快照PUT/_snapshot/my_backup{"type":"fs","settings":{"location":"/infini/test/es_backup"}}PUT/_snapshot/my_bac......