购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.6 密钥和参数生成

在现代密码学和密码应用中,一切安全保证的根基就是密钥,而密钥的随机性和安全性决定了密钥的强壮性,本节将介绍密钥的生成。准备密钥材料后,就可以通过对应的密码算法来生成密钥了。密钥生成包括对称密钥生成、非对称密钥生成和密码算法参数生成等。

2.6.1 对称密钥生成

本小节是对称密钥的生成实践,首先是国际算法的对称密钥生成,接着进行商用密码算法的密钥生成。本小节的实践内容对于第4章的对称算法实践加/解密有较强的铺垫,建议读者仔细阅读并进行代码试验。

1.国际对称算法(AES)——对称密钥生成

(1)密钥生成步骤

1)构建密钥生成器。使用javax.crypto包中KeyGenerator类的静态方法getInstance()产生密钥生成器实例kg,传递的参数是密码算法名称,例如本例是“AES”(算法名称可以参考2.3小节算法信息获取),这是个对称密码算法,中文是高级加密标准(Advanced Encryption Standard,AES),在以后章节会具体进行讲解。

2)初始化密钥生成器。输入密钥材料,调用kg的init()方法,初始化密钥产生器,并传递一个安全随机数作为参数输入。

3)生成对称密钥。调用密钥生成器kg的generateKey()方法产生密钥,返回值是SecretKey的一个对象。

4)提取对称密钥。使用SecretKey的getEncoded()方法,从密钥对象中提取密钥,并转换成字节数组。最后通过前面实践中的十六进制方法打印出来。

(2)完整代码实现如下

(3)代码结果输出

注意 :因为随机数的原因,每次运行都会不同,所以读者的运行结果和本例输出肯定也不一样。如果读者想使用192位长的AES密钥,可以使用init()的重载方法,用第一个参数来指定长度。如kg.init(192,sr)。

2.商用密码对称算法(SM4)——对称密钥生成

根据商用密码应用安全性评估(以下简称“ 密评 ”)相关标准的要求,我国密码算法标准中推荐使用SM4对称密码算法,修改前面代码,将代码中的“AES”换成“SM4”,具体如下代码所示:

如果再次运行程序时,编译环境出现了如下错误提示:

说明项目中的BC代码库没有引用或者引用错误。请按前面第二种引用BC库的方法配置实践环境。然后在前面的代码中增加如下两行语句(标黑体的两句):

最后,再次运行该代码应该可以顺利产生密钥了。

前面产生了对称密钥,读者肯定下一步想了解非对称密钥是如何产生的。像DSA、RSA、SM2等算法都是非对称的密码算法,它们的密钥都是 成对出现 的,一个是公钥另一个是私钥。下一个例子就是完整产生非对称密钥对keypair的实例。

2.6.2 非对称密钥生成

使用对称密码算法生成的密钥的长度,决定了密钥空间大小,对其安全性也会有影响。著名的DES算法就是因为密钥长度只有56位,在计算机算力增长的今天是不安全的,所以才被AES算法所替代。对称密钥的随机性选择非常重要,所以在项目建设时鼓励使用专用的密码产品来产生密钥。但非对称算法的机制是基于数学难题解决机制,如RSA是大素数因式分解、SM2是椭圆曲线的离散对数。所以非对称密钥并没有随机性的要求,它是和 算法参数选择 密切相关的,选择了参数就等于决定了密钥,所以非对称密钥的长短和对称密钥也完全不同,读者千万不要横向比较。因为非对称密钥的选择和数学密切相关,所以其就需要数学上的计算和推导,因此非对称算法也称为 计算密集型 的算法。

与对称密钥生成不同,非对称密钥的生成不需要输入随机数因子,算法参数的选择由包装好的类内部完成,如素数的选择、曲线的选择等,可以直接使用而不用了解内部数学细节。

1.国际非对称算法(DSA)密钥对生成

(1)实现步骤

1)构建密钥对生成器。调用KeyPairGenerator类的静态方法getInstance()来获得一个密码对生成器实例kpg。

2)初始化密钥对生成器。调用密码对生成器kpg的initialize()方法初始化1024位长度的密钥环境。

3)生成密钥对。调用密码对生成器kpg的genKeyPair()方法产生密钥对,返回KeyPair对象的一个实例keys。KeyPair对象有getPullic()和getPrivate()两个方法,很显然它们是获得密钥对的公钥对象和私钥对象。

4)提取密钥对。调用keys.getPullic()返回的是PublicKey对象,再调用该PublicKey对象的getEncoded()方法,返回一个字节数组,这个字节数组就是公钥。调用keys.getPrivate()返回的是PrivateKey对象,再调用该PrivateKey对象的getEncoded()方法,返回一个字节数组,这个字节数组就是私钥。最后使用十六进制的打印方法输出公、私钥。

(2)实现代码

(3)代码结果输出

从上面的展示可以看出,DSA的密钥对是 公钥长、私钥短 。也就是说验签慢,生成签名快。这也是DSA算法一直没有RSA使用次数多的原因之一。毕竟签名就一次,而验证签名可能会有很多次。

再次修改,把前面代码中的“DSA”换成“RSA”。而输出部分为了节省篇幅,只打印密钥长度,代码做如下修改:

再次运行代码,看看RSA的密钥对的情况,输出结果如下。

可以看出RSA密钥对和DSA正好相反, 公钥短、私钥长 。这样的公、私钥对于签名一次、频繁地验签将带来帮助。而且RSA基于大素数因式分解,易于理解和实现。所以目前大多数跨国的权威证书发布机构的证书多采用RSA算法。到目前读者可能有个疑惑,本例的RSA使用1024位长度来初始化,但是生成的两个密钥的长度都不是1024位,这是因为非对称RSA的1024不是指密钥长度,它是数学上两个大素数的乘积的模数,在第5章的非对称算法会具体进行介绍和分析,到时候读者再详细学习,这里只要明白公、私钥的长度情况即可。

有一个关键点需要提醒读者,在编者的密评工作中, 2048位以下的RSA密钥环境是被定为高风险项 的,如果读者发现有老系统还在使用短密钥,需要尽快整改。

2.商密非对称算法(SM2)密钥对生成

我国密码算法标准中推荐使用的非对称密码算法是SM2算法。再次对本实例前面的代码进行修改。

代码前添加如下两行,添加引用BC的Provider库,SM2只在BC库中。

创建密钥对生成器的语句进行如下修改:

这里getInstance()静态方法有两个参数:“EC”表示使用椭圆曲线算法,“BC”表示使用BC库里的算法。

当然,SM2的椭圆曲线算法生成的密钥对素数域是256位的,它也不是密钥的长度,初始化语句把1024换成256,最终修改成如下所示。

再次运行程序,产生了SM2算法的密钥对,输出结果如下:

可看出SM2的密钥比RSA的要短,效率更高。而RSA的密钥长度2048是最低要求,根据目前的硬件算力,低于2048位的RSA密钥已经不推荐使用。所以使用SM2替代RSA是有优势的。而且商用密码应用安全性评估也有相关要求,除非是跨国等原因,否则都应该使用国家商用密码SM2或SM9算法。具体算法内容在第5章会详细介绍。

2.6.3 密码算法参数生成

在前面小节非对称密钥生成中,介绍到它们是基于某个数学难题的,比如RSA基于因式分解,而密钥生成和素数的选择是密切相关的,这里的素数就是RSA算法的参数之一。密码算法依赖于很多底层数学方法和参数,而这些数学上的参数选择在Java中已经被完美地包装成了算法参数类,本小节就介绍算法参数类的使用。读者在生成密钥的同时可以把这些算法参数保存下来,以备后续调试使用,而且加密Cipher类也和参数类密切相关。

(1)实现代码

本段代码表面看有点复杂,实际上是三个算法参数的综合,第二行出现的类是AlgorithmParameterGenerator类。该类的静态方法getInstance()用来生成DSA非对称算法的参数实例。

接下来调用AlgorithmParameterGenerator类对象apg的init()方法,传递的参数是算法的模长度1024。

然后调用generateParameters()函数生成参数对象app,是AlgorithmParameters类的实例。

接下来的语句就是对参数进行编码,调用函数getEncoded(),生成字节数组,方便通信传递或者存储该参数。

最后通过println()进行屏幕输出。

关于对称密码参数部分的语句不复杂,其中调用init()方法时选择带有byte[]参数的重载方法,参数的长整数是经过ASN.1规范编码的结果值,可以查阅JDK文档进一步了解。

关于椭圆曲线算法的生成参数规范类ECGenParameterSpec,该类指定用于生成椭圆曲线(EC)域参数的参数集。使用标准(或预定义)名称stdName创建用于生成EC参数的参数规范,以便生成相应(预计算)椭圆曲线域参数。本处指定椭圆曲线称为secp256k1,如果读者不清楚这个曲线,那也应该听说过比特币这个概念,比特币采用的椭圆曲线算法就是secp256k1。其他可能常用的曲线还有secp256r1。

读者也不用记忆这些曲线名称,只要熟悉了程序的编写方法即可,到时候可以到网上查询各个曲线的标准名称。本书后面也会有这些曲线的进一步介绍。

本例程序如果不能完全理解也没关系,后面的算法加密、解密、签名、验签过程的实践,还会涉及这些参数的应用。本书学习完后再回头对应复习,应该就能完全理解了。

本例中的参数类AlgorithmParameters,在项目建设中意义很大,它通常用在Cipher类的init()方法中,作为第三个参数来构建一个密码算法实体类。

(2)输出结果

2.6.4 密钥工厂与密钥封装

非对称密钥在使用中会有多种标准编码规范,需要对其进行规范和封装,本节将集中介绍密钥工厂与密钥封装的实现方法,具体如下。

为了解决大量对象创建的问题,引入简单工厂设计模式。将密钥的创建和使用分开,密钥工厂负责创建密钥,使用者直接调用即可。

(1)实现代码

实例中的KeyPairGenerator类,在前面的实践例子中已经出现,它用2048位进行了初始化,产生了安全的RSA密钥对。initialize()和generateKeyPair()两个方法分别初始化密钥环境和产生密钥对,较容易理解,此处不再解释。

PKCS8EncodedKeySpec类是私钥包装的规范类。用该规范,工厂可以生产出符合PKCS8标准要求的Key类的实例对象,此处仅在其构造函数中传递了密钥对的私钥字节数组。

密钥工厂类KeyFactory是设计模式的优秀典范,后面还会遇到算法工厂类。此处调用了KeyFactory的静态方法getInstance()来实例化该对象,参数就是算法名称“RSA”。

随后调用了工厂类的generatePrivate()方法,参数就是前面私钥包装过的规范类实例pkcs8,它返回Key对象的一个实例,这里是个私钥对象。

接下来两行很容易理解,私钥的getEncoded()方法将编码转换成字节数组,然后通过println()打印出十六进制标识的私钥。

公钥规范采用不同的规范类X509EncodedKeySpec,与前面的私钥类似,在构造函数中传递公钥的字节数组。

这里需要理解工厂设计模式的用意:工厂模式。它主要是用来实例化有共同接口的类,可以动态决定应该实例化哪一个类,也就是工厂可以生成很多种“产品”。此处密钥工厂准许根据给定的密钥规范(PKCS8EncodedKeySpec、X509EncodedKeySpec)构建不透明的密钥对象,或者用统一恰当的格式来表述底层的密钥材料。密钥工厂还可用于兼容密钥规范之间的转换。所以读者对该实例可以反复体会,对深入理解很有帮助。

(2)结果输出 popNH0XTDqvC5FYLTq9CNeO3VgeoSm6AGmOmOcbYnYdB/iBWHLRxBnLfLOh1FAJD

点击中间区域
呼出菜单
上一章
目录
下一章
×