利用JCA和JCE对称加解密(一、学习笔记)

Java Cryptography Architecture

密钥根接口Key

接口Key位于包java.security中。密钥在加解密运算中的地位至关重要,密钥丢失,安全目标中的机密性就已经失去了。

Kerckhoff原则:只有密钥保密。密码学的发展经历了漫长的试错过程,而一个更成熟的观点也在这个过程中产生:为了对密码学原语的安全性获得足够的信心,每个密码原语必须由密码专家对其进行公开分析。若做不到这一点,密码原语的可靠性只能依赖于含糊不清的安全性说明,而历史证明了其效果不尽如人意。

所有的密钥都具有三个特征

  • 真正随机:密钥一定是来自一个可靠的随机源产生的随机数,所以一个好的密钥必须来自好的随机数。
  • 算法相关:密钥和加解密算法是相关的,不同的算法对于密钥的长度都有要求,比如DES算法要求密钥是56位的,如果想让Java生成一个57位的DES算法密钥,就会直接运行报错。
  • 特定编码:密钥数据通常具有标准的编码格式,主要是为了方便密钥管理和与其他加解密函数库进行交互和解析。比如,Java提供PEM和DER两种编码方式对这些密钥进行编码,并提供相关指令以使用户在这两种格式之间进行转换,其他加解密函数库(比如OpenSSL)也是采用的这两种编码格式。

SecretKey、PublicKey、PrivateKey三大接口继承于Key接口,定义了对称密钥和非对称密钥接口。

安全随机数SecureRandom

Java提供了两个类来为用户提供随机数功能,分别是类Random和类SecureRandom。SecureRandom是从Random派生而来的,且专门用于要求高的密码学场合。类Random一般用于随机性要求不高的场合,但是速度快。

伪随机Random

伪随机数是用确定性的算法计算出来的均匀分布的随机数序列,并不真正随机,但是具有类似于随机数的统计特征,如均匀性、独立性等。在计算伪随机数时,若使用的初值(种子)不变,那么伪随机数的数序也不变。

类Random用来创建伪随机数,如果给定一个初始的种子,产生的随机数序列是完全一样的;如果不给定种子,就使用系统当前时间戳作为种子,那么每次运行时,种子不同,得到的伪随机数序列就不同。
如果我们在创建Random实例时指定一个种子,就会得到完全确定的随机数序列,比如:

真随机SecureRandom

严格意义上的真随机可能仅存在于量子力学之中,我们当前所想要的(或者所能要的)并不是这种随机。我们其实想要一种不可预测、统计意义上、密码学安全的随机数,只要能做到这一点的随机数生成器都可以称为真随机数生成器。

想直接利用算法得到真随机数有点难度,但是也不是没有办法。计算机不能产生随机数,但是现实世界中有非常多的随机因素,将这种随机因素引入计算机就能实现真随机。Linux内核中的随机数发生器(/dev/random)理论上能产生真随机,即这个随机数的生成独立于生成函数,我们说这个随机数发生器是非确定的(不可预见的)。这种真随机并不一定非得是特殊设计的硬件。Linux操作系统内核中的随机数生成器(/dev/random)维护了一个熵池,用于搜集硬件噪声,比如键盘敲击速度、鼠标位置变化、网络信号强度变化、时钟、IO请求的响应时间、特定硬件中断的时间间隔甚至周围的电磁波等。直观地讲,按一次键盘、动一下鼠标、邻居家Wi-Fi信号强度变化、磁盘写入速度等都能够提供最大可能的随机数据熵。Linux维护着这样一个熵池,不断收集非确定性的设备事件来作为种子(种子就是输入到随机数产生器中的比特串),因此可认为是高品质的真随机数生成器。不过/dev/random是阻塞的,也就是说,如果熵池空了,对于/dev/random的读操作将被挂起,直至收集到足够的环境噪声为止。因此,在开发程序时,我们应使用/dev/urandom作为/dev/random的一个副本,它不会阻塞,但其输出的熵可能会小于/dev/random。真随机数有一个非常基本的特征就是不可预测性。
类SecureRandom提供了能满足加密要求的强随机数生成器。随机数在密码学中有着地基的地位。至关重要的密钥首先必须是一个安全的随机数。密码学意义上的安全随机数要求必须保证其不可预测性,实际上,密码学中有15项随机数检测规则,限于篇幅,这里不展开了。怎么得到安全的随机数?通常可以通过硬件随机数芯片来获得,也可以通过非物理的随机数产生器来获得。目前,三大非物理的随机数产生器有Linux操作系统的/dev/random设备接口、Windows操作系统的CryptGenRandom接口、JDK的java.security.SecureRandom类。

Java Cryptography Extension

密钥接口SecretKey

接口SecretKey用于保存对称密钥,从接口Key派生。该接口自己并没有提供成员方法,而是使用Key中的方法,比如getFormat和getEncoded,前者返回密钥的编码格式,后者得到原始密钥(没有编码过的密钥)数组。对于没有编码过的密钥,getFormat返回的结果是“RAW”。
生成密钥常用以下两种方式:

  • 第一种方式是使用密钥生成器keyGenerator,定义密钥生成器keyGenerator的实例对象(密钥由密钥生成器来生成,就像米饭是由电饭锅来生成的一样),然后调用init方法进行密钥生成器的初始化,也就是为密钥生成器设置一些我们所需密钥的属性参数,比如密钥长度、随机数等(没有随机数肯定是没有办法生成密钥的,密钥通常是一个随机数)。就像我们要为电饭锅设置烧饭时间,并给电饭锅放入大米(没有大米,电饭锅这个米饭生成器是肯定无法生成米饭的)一样,最后调用密钥生成方法generateKey来生成密钥(相当于电饭锅开始烧出米饭来了),返回值存于Secretkey接口中。
  • 第二种方式稍微简便些,即使用接口Secretkey的实现类SecretKeySpec(该类是密钥规范类),实例化SecretKeySpec对象产生密钥,返回结果存于Secretkey接口中。

密钥生成器KeyGenerator

1、创建密钥生成器

//得到一个AES算法的密钥生成器对象
KeyGenerator keygen=KeyGenerator.getInstance("AES");

2、初始化密钥生成器对象
比如,初始化一个DES算法的56比特的密钥:

keygen.init(56);

如果密钥生成器KeyGenerator定义了DES算法,但是初始化传入的密钥长度不是56比特,会如何呢?运行报错!因为不同的加密算法对密钥长度都是有要求的。

3、生成密钥

        KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
        keyGenerator.init(56);
        SecretKey secretKey = keyGenerator.generateKey();
        String secretKeyFormat = secretKey.getFormat();
        System.out.println(secretKeyFormat);

密钥规范类SecretKeySpec

String deskey="12345678";
SecretKey secretKey = new SecretKeySpec(deskey.getBytes(), "DES");

初始向量类IvParameterSpec

在分组加密算法的CBC模式下需要一个称为初始向量(IV)的字节数组。有了初始向量iv,可以增加加密算法的强度。这个初始向量以后会作为参数传入加解密对象(Cipher)的初始化函数(init函数)。

IvParameterSpec iv = IvParameterSpec("0123456789101112".getBytes());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值