前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >解读国密非对称加密算法SM2

解读国密非对称加密算法SM2

作者头像
云水木石
发布于 2020-03-25 13:39:26
发布于 2020-03-25 13:39:26
5.6K00
代码可运行
举报
运行总次数:0
代码可运行

本文先介绍非对称加密算法,然后聊一聊椭圆曲线密码算法(Elliptic Curve Cryptography,ECC),最后才是本文的主题国密非对称加密算法SM2。因为我的数学知识有限,对于算法涉及的一些复杂的理论知识,也是不懂,所以本文不会涉及理论,仅仅从编程的角度解读一下SM2。

在进行国密算法开发的这段时间,我主要参考的书籍是《深入浅出HTTPS:从原理到实战》,微信读书上也有电子版,如果你也是进行网络安全方面的开发,建议你读一读。这篇文章中的密码学基础知识也是来自此书。

对计算机安全有点基础知识的人们应该知道,在密码学中,用于数据加密的算法主要有两种:对称加密算法(Symmetric-keyAlgorithms)和非对称加密算法(Asymmetrical Cryptography)。所谓对称加密算法,就是加密密钥和解密密钥相同,这个很好理解,就像我们给房间上锁,锁门的钥匙和开门的钥匙是一样的。而非对称密钥加密算法则是加密密钥和解密密钥不同,这个有点违反普通常理,但确实存在这样的算法,其背后的理论非常复杂。我们不需要懂得多少其背后的理论,也可以采用非对称密码算法做很多安全方面的工作。

在整个密码学体系中,非对称加密算法用途更广,可以用在加密解密、密钥协商、数字签名等方面。所以本文先介绍一下非对称加密算法。

非对称加密算法

非对称加密算法也称作公开密钥算法(Public Key Cryptography),有着一对密钥:公钥(Public Key)和私钥(Private Key)。

  1. 加密解密

非对称加密算法的首要用途是加密解密,通常加密解密过程如下图所示:

公开密钥算法加密解密过程

需要注意的是,上面只是其中一种用法,反过来,采用私钥加密,公钥解密也可以的。具体采用哪种方式,取决于使用场景。

非对称加密算法的安全性建立在复杂的数学模型之上,其背后的理论不用去深究。我们需要知道的是其安全性和密钥对的长度有关,尽量选择较长的密钥长度。例如,对于RSA非对称加密算法来说,一个2048比特长度的密钥对被认为是安全的。但较长的密钥长度会带来运算速度问题,需要综合权衡。

  1. 密钥协商

非对称密钥算法存在加解密速度慢的问题,因此不能用于需要频繁加密大量数据的场景,这个时候需要用到对称密钥加密算法。问题是,怎么保证对称密钥的安全呢?特别是互联网网络通信中,密钥如何通过不安全的网络发送,如何保证对方会安全的存储密钥?

这个时候,可以采用动态密钥,也叫作会话密钥:

  • 会话密钥的作用就是为了加密解密通信数据,也就是对称加密算法可以使用会话密钥进行加密解密。
  • 在加密解密通信数据之前,客户端和服务器端协商出会话密钥,而会话密钥只有服务器端和特定的客户端才能知晓。
  • 会话密钥的意思就是该密钥不用存储,一旦客户端和服务器端的连接关闭,该密钥就会消失,由于密钥不用存储,安全性就得到了很大的保障。

会话密钥可通过密钥协商算法完成,但密钥协商算法可以有很多种,目前主要采用的密钥协商算法有RSA密钥协商算法DH密钥协商算法

  1. 数字签名

数字签名可以想象为现实世界中的合同签名,其主要作用是防抵赖,比如你给对方发送了一段消息,然后你否认这个消息是你发送的。

在现实世界中,有哪些行为或者约定可以防止人抵赖呢?最明显的就是合同,合同一般需要人签字或者按指纹。有了合同,合同签署人就无法否认合同的合法性,原因就在于法律规定,指纹具备唯一性,每个人的指纹是不同的,或者说指纹就代表了一个人。

在密码学中,如果一个消息也含有特殊的指纹,那么它是否就不能抵赖呢?非对称加密算法中,私钥只有密钥对的生成者持有,如果不考虑密钥泄露的问题,私钥拥有者使用密钥(注意不是加密操作)签署一条消息,然后发送给任意的接收方,接收方只要拥有私钥对应的公钥,就能成功反解签署消息,由于只有私钥持有者才能“签署”消息,不能抵赖说这条签署消息不是他发送的,这就是数字签名技术的全部。

签名生成流程:

签名流程

签名验证流程:

验签流程

需要注意,上面只是总的流程,签名算法也有很多种,各种算法在实现上还是有很多不同,请根据需要再深入到具体的算法中。

非对称密钥算法中最出名、使用最广泛的要数RSA算法,该算法是Ron Rivest、Adi Shamir、Leonard Adleman三个人创建的,以三个人名字的首字母命名。算法自1977年公开,沿用至今,体现出理论知识的威力。然而,前面也说过,RSA的安全性和密钥长度有关,随着计算机运算速度的提升,需要更长的密钥长度保证安全性。问题是,密钥长度越长,生成密钥的时间也越长,加密解密的速度也越慢。

这么些年来,密码学家也没有闲着,设计出了新一代的公开密钥算法:椭圆曲线密码算法(ECC)。

ECC

ECC主要的优点就是安全性,极短的密钥能够提供很大的安全性。比如224比特的ECC密钥和2048比特的RSA密钥可以达到同样的安全水平,由于ECC密钥具有很短的长度,运算速度非常快。

ECC基于非常复杂的算法,我看了一些这方面的理论知识,也是云里雾里,所以这里只说说和我们使用和编程相关的基础知识。

不理解ECC理论知识没有关系,但需要了解以下这张图:

ECC模型

ECC椭圆曲线由很多点组成,这些点由特定的方程式组成的,比如方程式可以是y^2 = x^3 + ax + b,这些点连接起来就是一条曲线,但曲线并不是一个椭圆。

椭圆曲线有个特点,任意两个点能够得到这条椭圆曲线上的另外一点,这个操作称为打点,经过多次(比如d次)打点后,能够生成一个最终点(F)。

在上面的图中,A点称为基点(G)或者生成器。A可以和自己打点从而生成B点,在实际应用的时候,一般有基点就可以了。经过多次打点,就得到了最终点G。

ECC密码学的关键点就在于就算知道具体方程式、基点(G)、最终点(F),也无法知晓一共打点了多少次(d)。

ECC中,打点次数(d)就是私钥,这通常是一个随机数,公钥就是最终点(F),包含(x,y)两个分量,通常组合成一个数字来传输和存储。

ECC由方程式(比如a、b这样的方程式参数)、基点(G)、质数(P)组成。理论上方程式和各种参数组合可以是任意的,但是在密码学中,为了安全,系统预先定义了一系列的曲线,称为命名曲线(name curve),比如secp256k1就是一个命名曲线。对于开发者而言,在使用ECC密码学的时候,就是选择具体的命名曲线。

说到这儿,和国密SM2算法有什么关系?

国密SM2算法

SM2算法就是一种ECC算法,准确来说,就是设计了一条ECC命名曲线。这算抄袭么?也不是,因为设计一条安全的命名曲线,也是一件非常难的事情,需要丰富的理论知识。ECC本质上就是一个数学公式,任何人基于公式都可以设计出椭圆曲线,但要注意ECC离散对数问题(Elliptic-Curve Discrete-Logarithm Problem,简称ECDLP),如果实现不当,那么ECC公式就会存在安全风险。一些组织为此还定义了命名曲线的一些设计标准,不同的设计标准有不同的目标,比如有的以安全性为首要目标,有的以效率为首要目标。

在《GMT 0003-2012》这份标准中,有SM2算法的设计背景知识,有兴趣的可以了解,对于开发者而言,最重要的是《GMT 0005-2012》标准中的曲线参数:

p、a、b、G(x,y)和n

现在的网络库,比如NSS、OpenSSL、libtomcrypt等,都有ECC算法的支持,要在网络库中加入SM2算法支持,只需加入命名曲线的参数即可。

比如在GmSSL代码的 ec_curve.c 文件中就有 sm2p256v1 命名曲线的参数定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static const struct {    EC_CURVE_DATA h;    unsigned char data[0 + 32 * 6];} _EC_SM2_PRIME_256V1 = {    {        NID_X9_62_prime_field, 0, 32, 1    },    {        /* no seed */        /* p */        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,        /* a */        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,        /* b */        0x28, 0xE9, 0xFA, 0x9E, 0x9D, 0x9F, 0x5E, 0x34, 0x4D, 0x5A, 0x9E, 0x4B,        0xCF, 0x65, 0x09, 0xA7, 0xF3, 0x97, 0x89, 0xF5, 0x15, 0xAB, 0x8F, 0x92,        0xDD, 0xBC, 0xBD, 0x41, 0x4D, 0x94, 0x0E, 0x93,        /* x */        0x32, 0xC4, 0xAE, 0x2C, 0x1F, 0x19, 0x81, 0x19, 0x5F, 0x99, 0x04, 0x46,        0x6A, 0x39, 0xC9, 0x94, 0x8F, 0xE3, 0x0B, 0xBF, 0xF2, 0x66, 0x0B, 0xE1,        0x71, 0x5A, 0x45, 0x89, 0x33, 0x4C, 0x74, 0xC7,        /* y */        0xBC, 0x37, 0x36, 0xA2, 0xF4, 0xF6, 0x77, 0x9C, 0x59, 0xBD, 0xCE, 0xE3,        0x6B, 0x69, 0x21, 0x53, 0xD0, 0xA9, 0x87, 0x7C, 0xC6, 0x2A, 0x47, 0x40,        0x02, 0xDF, 0x32, 0xE5, 0x21, 0x39, 0xF0, 0xA0,        /* order */        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,        0xFF, 0xFF, 0xFF, 0xFF, 0x72, 0x03, 0xDF, 0x6B, 0x21, 0xC6, 0x05, 0x2B,        0x53, 0xBB, 0xF4, 0x09, 0x39, 0xD5, 0x41, 0x23    }};

代码中的order就是参数表中的 n。对于大部分ECC操作来说,不需要该值,但在计算签名的时候会对n取模。

小结

本文从非对称密码算法开始,逐步介绍到国密SM2算法。我们可以看到,SM2并不是一个全新设计的算法,而是借助现有的ECC理论,设计了一条命名曲线。这样,在已经实现了ECC算法的网络库上增加SM2算法的支持就非常简单,只需要将曲线参数添加即可。

但这是否就已经完全实现了SM2算法呢?也不是,因为SM2算法不仅用在加解密,还用在数字签名、密钥协商中,国密标准另外定义了数字签名算法、密钥交换协议、公钥加密算法,所以要把这些都实现完整,才算实现完全了国密SM2算法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云水木石 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Callable/Future 使用及原理分析,Future .get()为啥能等待呢?
Callable/Future 和 Thread 之类的线程构建最大的区别在于,能够很方便的获取线程执行完以后的结果。首先来看一个简单的例子
源码之路
2020/09/04
5K0
Callable/Future 使用及原理分析,Future .get()为啥能等待呢?
Java线程(七):Callable和Future
本文介绍了Java线程的Callable和Future,包括如何使用Callable创建有返回值的线程,以及如何使用Future获取返回值。同时介绍了ExecutorService、Future和CompletionService等工具类,用于简化线程并发编程。
高爽
2017/12/28
5710
6.实现 Callable 接口
6.实现 Callable 接口 前言 本篇章来介绍一下创建线程的第三种方式,其中创建线程一共有四种方式: 继承 Thread 类 实现 Runnable 接口 实现 Callable 接口 使用线程池的方式 那么下面我们来介绍一下 实现 Callable 接口的方式。 Callable 接口 - Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式:Callable 接口 - Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程
Devops海洋的渔夫
2022/03/23
6010
6.实现 Callable 接口
Runnable和Callable源码及应用解析
里面使用Runnable的地方只有传递对象的时候,其他都是使用的Thread而Thread又实现了我们的Runnable,所以Runbale可以理解为执行代码的对象,执行的过程和线程的操作交由Thread控制,Thread源码可以看 ----》Thread源码解析。
余生大大
2022/11/02
2000
Runnable和Callable源码及应用解析
Thread, Runable, Callable 还傻傻分不清?
在java中你怎么创建线程?相信你很快能够想到继承Thread类和实现Runnable接口这两种方式。
Java技术栈
2020/12/21
7600
Thread, Runable, Callable 还傻傻分不清?
创建线程都有哪些方式?— Callable篇
相信大家回答这个问题没什么难度吧?通常问完创建方式,那么接下来就是问「1、2」跟「3」创建方式的不同了,只要说出「3」有返回值基本这个问题就过了,不管是出于好奇还是疑惑,我们今天来会会这个Callable。
niceyoo
2020/07/26
8010
Future和Callable学习
我们知道使用多线程时,最初的Thread到线程池,此时对于线程的使用,提供了其使用的复用率。而实现多线程的三种方式:继承Thread;实现Runnable接口,重写run方法;实现Callable接口,同时重写call方法,同时通过Future获取执行的返回值。也就是说callable执行任务,而Future拿到执行的结果。Future具有阻塞性在于其get()方法具有阻塞性,而isDone()是不具有阻塞性的。
路行的亚洲
2020/07/16
5050
Java并发之Executor + Callable + Future引入 Callable + FutureCallable + Future实例
Executor框架的优势之一就是,可以运行并发任务并且返回结果。 我们知道Runnable对象是没有返回值的,所以自然利用Runnable对象就无法返回结果,于是就定义了一个新的接口,可以理解为是“带有返回值的Runnable对象”。 这个接口就是 Callable接口:这个接口声明了一个call方法,类似于Runnable接口的run方法,是任务具体的逻辑,不同就在于可以有返回值,而且这个接口是一个泛型接口,这就意味着必须声明call方法的返回类型。
desperate633
2018/08/22
3120
Java并发之Executor + Callable + Future引入 Callable + FutureCallable + Future实例
Runnable 和Callable的实现与区别,应用场景
1:通过实现Runnable接口 2:通过继承Thread接口 3:通过Callable和Future创建线程
全栈程序员站长
2022/07/25
1.6K0
并发编程系列之Callable和Runnable的不同?
并发编程系列之Callable和Runnable的不同?在学习并发多线程的过程中,很多读者都知道怎么实现Runnable,下面是一道经典的例子
SmileNicky
2021/09/08
4161
并发编程系列之Callable和Runnable的不同?
Java并发编程之Future和FutureTask
搞过Java或者客户端Android开发的都知道,创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。不过,这2种方式都有一个缺陷,就是在执行完任务之后无法获取执行结果。
xiangzhihong
2022/11/30
3700
Callable与Future介绍
在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。我们一般只能采用共享变量或共享存储区以及线程通信的方式实现获得任务结果的目的。不过,Java中,也提供了使用Callable和Future来实现获取任务结果的操作。Callable用来执行任务,产生结果,而Future用来获得结果。
HLee
2021/10/13
1K0
Callable与Future介绍
java 多线程
就绪,当线程调用了strat()方法的时候,线程就绪,会为其创建方法调用栈和程序计数器。
mySoul
2018/11/19
8150
Future && FutureTask
线程的创建方式中有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,于是就有了Callable接口,Future接口与FutureTask类的配和取得返回的结果。
大学里的混子
2019/03/13
5540
Future解析与使用
Java 1.5开始,提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
林老师带你学编程
2019/05/25
5900
Java中的Runnable、Callable、Future、FutureTask的区别
Java中存在Runnable、Callable、Future、FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别。
开发者技术前线
2020/11/23
4700
Java中实现多线程的四种方式
Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。
Kevin_Zhang
2019/02/20
5520
Future FutrueTask Callable类源码说明以及原理使用
  JDK内置的Future主要使用到了Callable接口和FutureTask类。
小勇DW3
2018/08/30
5630
不会用Java Future,我怀疑你泡茶没我快, 又是超长图文!!
现陆续将Demo代码和技术文章整理在一起 Github实践精选 ,方便大家阅读查看,本文同样收录在此,觉得不错,还请Star
用户4172423
2020/07/14
5750
刚研究完Callable和Future,各位随便问!!
在Java的多线程编程中,除了Thread类和Runnable接口外,不得不说的就是Callable接口Future接口了。使用继承Thread类或者实现Runnable接口的线程,无法返回最终的执行结果数据,只能等待线程执行完成。此时,如果想要获取线程执行后的返回结果,那么,Callable和Future就派上用场了。
冰河
2021/04/30
6810
相关推荐
Callable/Future 使用及原理分析,Future .get()为啥能等待呢?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档