哈Ha! 我向您介绍了Jakob Jenkov撰写的第二篇文章“ Java Cipher”的译文,该文章来自一系列初学者 ,他们希望学习Java密码学的基础知识。
目录:
- 密码学
- 密码
- 留言摘要
- Mac电脑
- 签章
- 密钥对
- 密钥生成器
- 密钥对生成器
- 密钥库
- 按键工具
- 证明书
- 证书工厂
- 证书路径
Java密码(Cipher)
Java Cipher类( javax.crypto.Cipher )是一种加密算法。 术语“密码”是密码学领域中加密算法的标准术语。 这就是为什么Java类称为Cipher而不是Encryptor / Decryptor或其他名称的原因。 您可以使用Cipher实例在Java中加密和解密数据。 本章介绍了Cipher类的工作方式。
密码创作
在使用密码之前,必须通过调用带参数的getInstance()方法来创建Cipher类的实例,该参数指示您要使用哪种加密算法。 这是创建Java Cipher实例的示例:
Cipher cipher = Cipher.getInstance("AES");
本示例使用AES加密算法创建一个Cipher实例。
加密方式
某些加密算法可以在不同的模式下工作。 加密模式确定如何加密数据的详细信息。 因此,加密模式会部分影响加密算法。 加密模式有时可以在几种不同的加密算法中使用-作为添加到主要加密算法中的一种方法。 这就是为什么将模式与加密算法本身分开考虑,而不是将其视为加密算法的“附加项”。 以下是一些最著名的加密模式:
- EBC-电子密码本(电子密码本模式 )
- CBC-密码块链接
- CFB-密码反馈(密码反馈模式 )
- OFB-输出反馈
- 点击率-计数器(计数器模式 )
创建密码实例时,可以将其模式添加到加密算法的名称中。 要使用块耦合模式-密码块链接(CBC)创建AES密码实例,您可以执行以下操作:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
由于加密块的耦合模式也需要“填充方案”,因此将填充方案( PKCS5Padding )添加到加密算法名称的字符串的末尾。
重要的是要知道,Java SDK加密提供程序默认不支持所有加密算法和模式。 要使用所需的模式和填充模式创建所需的密码实例,可能需要安装第三方提供程序,例如Bouncy Castle。
密码初始化
在使用Cipher实例之前,必须对其进行初始化。 通过调用其init()方法来初始化密码。 init()方法采用两个参数:
在加密模式下初始化Cipher实例的示例:
Key key = ...
这是初始化已经处于解密模式的Cipher实例的示例:
Key key = ...
数据加密与解密
要使用Cipher实例加密或解密数据,可以使用以下两种方法之一:
有几个采用不同参数的update()和doFinal()方法替代版本。 考虑这里最常用的。 如果您需要加密或解密一个数据块,只需对数据调用doFinal()即可进行加密或解密。 一个例子:
byte[] plainText = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] cipherText = cipher.doFinal(plainText);
实际上,在数据解密的情况下,代码看起来大致相同。 只需记住,密码实例必须在解密模式下初始化。 这是解密一密文块的样子:
byte[] plainText = cipher.doFinal(cipherText);
如果需要加密或解密分为几个块的大文件,则对每个数据块调用一次update() ,并以对最后一个数据块的doFinal()方法的调用结束。 这是加密多个数据块的示例:
byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8"); byte[] data3 = "01234567890123456789012345".getBytes("UTF-8"); byte[] cipherText1 = cipher.update(data1); byte[] cipherText2 = cipher.update(data2); byte[] cipherText3 = cipher.doFinal(data3);
最后一个数据块需要doFinal()调用的原因是,某些加密算法需要对数据进行补充以适合特定的密码块大小(例如8字节边界)。 不需要补充中间加密数据。 因此,对中间数据块调用update()方法,对最后一个数据块调用doFinal() 。
解密多个数据块时,您还为中间数据块调用update()方法,为最后一个块调用doFinal()方法。 解密几个数据块的示例:
byte[] plainText1 = cipher.update(cipherText1); byte[] plainText2 = cipher.update(cipherText2); byte[] plainText3 = cipher.doFinal(cipherText3);
同样,必须在解密模式下初始化密码实例,此示例才能正常工作。
加密/解密字节数组的一部分
Cipher类的加密和解密方法可以对存储在字节数组中的某些数据进行加密或解密。 update()和/或doFinal()方法需要传递偏移量和长度。
int offset = 10; int length = 24; byte[] cipherText = cipher.doFinal(data, offset, length);
在此示例中,索引10和前向24个字节中的字节将被加密(或解密,具体取决于密码的初始化)。
加密/解密到现有的字节数组
本章中的所有加密和解密示例均以新的字节数组返回加密或解密的数据。 但是,也可以将数据加密或解密为现有的字节数组。 这对于减少创建的字节数组的数量很有用。 为此,请将目标字节数组作为参数传递给update()和/或doFinal()方法。
int offset = 10; int length = 24; byte[] dest = new byte[1024]; cipher.doFinal(data, offset, length, dest);
在此示例中,数据从10个索引24字节加密到偏移量为0的dest字节数组。如果要为dest字节数组设置不同的偏移量,则可以使用update()和doFinal()版本来接受附加的offset参数。 在dest数组中使用偏移量调用doFinal()方法的示例:
int offset = 10; int length = 24; byte[] dest = new byte[1024]; int destOffset = 12 cipher.doFinal(data, offset, length, dest, destOffset);
重用密码实例
初始化Cipher实例是一项昂贵的操作,而重用Cipher实例是一个好主意。 幸运的是,密码类在设计时考虑了可重用性。 在Cipher实例上调用doFinal()方法时 ,它返回到初始化后立即处于的状态。 然后,可以将Cipher实例用于加密或解密更多数据。
重用Java Cipher实例的示例:
Cipher cipher = Cipher.getInstance("AES"); Key key = ...
首先,创建并初始化一个Cipher实例,然后将其用于加密两个一致的数据块。 注意对这两个数据块的update()然后doFinal()的调用。 之后,可以再次使用Cipher实例对数据进行加密。 这是通过对第三个数据块调用doFinal()来完成的。 调用doFinal()之后,您可以使用相同的Java Cipher实例加密另一个数据块。