密码学
密码术是隐藏或混淆数据的过程,因此窥探眼睛无法理解它。
Shiro的加密目标是简化JDK的加密支持并使之可用。
需要特别注意的是,密码通常不是特定于主题的,因此Shiro API的其中一个领域不是特定于主题的。
即使未使用“主题”,您也可以在任何地方使用Shiro的加密支持。
Shiro真正侧重于其加密支持的两个领域是加密哈希(又名消息摘要)和加密密码领域。
让我们更详细地看看这两个。
散列
如果您使用了JDK的MessageDigest类,您很快就会意识到使用它有点麻烦。它具有笨拙的基于静态方法的基于工厂的API,而不是面向对象的API,因此您不得不捕获可能永远不需要捕获的已检查异常。如果您需要十六进制编码或Base64编码的消息摘要输出,则由您自己决定-两者均不提供标准的JDK支持。
Shiro通过干净直观的哈希API解决了这些问题。
例如,让我们考虑MD5散列文件并确定该散列的十六进制值的相对常见的情况。称为“校验和”,通常在提供文件下载时使用-用户可以对下载的文件执行自己的MD5哈希,并断言其校验和与下载站点上的校验和匹配。如果它们匹配,则用户可以充分假设文件在传输过程中未被篡改。
在没有Shiro的情况下,您可以尝试以下操作:
-
将文件转换为字节数组。 JDK中没有任何东西可以帮助您解决此问题,因此您需要创建一个辅助方法,该方法可以打开FileInputStream,使用字节缓冲区并抛出适当的IOException等。
-
使用MessageDigest类对字节数组进行哈希处理,以处理适当的异常,如下面的清单12所示。
-
将散列字节数组编码为十六进制字符。 JDK中也没有任何东西可以提供帮助,因此您需要创建另一个帮助器方法,并可能在实现中使用按位运算和移位。
- Listing 12. JDK’s MessageDigest
try {MessageDigest md = MessageDigest.getInstance("MD5");md.digest(bytes);byte[] hashed = md.digest();
} catch (NoSuchAlgorithmException e) {e.printStackTrace();
}
对于如此简单且相对常见的事物而言,这是一项巨大的工作。
现在,说明如何使用Shiro进行完全相同的操作。
String hex = new Md5Hash(myFile).toHex();
使用Shiro简化所有工作时,了解正在发生的事情非常简单和容易。
SHA-512哈希和密码的Base64编码也很容易。
String encodedPassword =new Sha512Hash(password, salt, count).toBase64();
您会看到Shiro在很大程度上简化了哈希和编码,从而在此过程中节省了一些理智。
密码
密码是可以使用密钥可逆地转换数据的密码算法。我们使用它们来保护数据安全,尤其是在传输或存储数据时,尤其是在数据容易被撬开的时候。
如果您曾经使用过JDK密码API,尤其是javax.crypto.Cipher类,那么您就会知道,驯服它可能是一件极其复杂的事情。对于初学者来说,每种可能的Cipher配置始终由javax.crypto.Cipher的实例表示。
需要做公钥/私钥加密吗?
您使用密码。是否需要使用分组密码进行流操作?您使用密码。是否需要创建AES 256位密码来保护数据?您使用密码。你明白了。
以及如何创建所需的Cipher实例?
您创建了一个复杂的,不直观的,用令牌分隔的密码选项字符串,称为“转换字符串”,并将该字符串传递给Cipher.getInstance静态工厂方法。使用这种密码选项字符串方法,没有类型安全性来确保您使用有效的选项。这也隐含地意味着没有JavaDoc可以帮助您了解相关选项。而且,即使您知道配置正确,也需要处理经过检查的异常,以防您的字符串格式错误。如您所见,使用JDK Ciphers是一项繁琐的任务。这些技术很久以前曾经是Java API的标准,但是时代已经改变,我们希望有一种更简单的方法。
Shiro试图通过引入其CipherService API简化整个密码算法的概念。
大多数开发人员在保护数据时都希望使用CipherService:一种简单,无状态,线程安全的API,可以在一个方法调用中完整地加密或解密数据。您所需要做的就是提供密钥,然后可以根据需要加密或解密。
例如,可以使用256位AES加密,如下面的清单13所示。
- Listing 13. Apache Shiro’s Encryption API
AesCipherService cipherService = new AesCipherService();
cipherService.setKeySize(256);
//create a test key:
byte[] testKey = cipherService.generateNewKey();//encrypt a file’s bytes:
byte[] encrypted =cipherService.encrypt(fileBytes, testKey);
与JDK的Cipher API相比,Shiro示例更简单:
-
您可以直接实例化CipherService-没有奇怪或令人困惑的工厂方法。
-
密码配置选项表示为与JavaBeans兼容的getter和setter-没有奇怪且难以理解的“转换字符串”。
-
加密和解密在单个方法调用中执行。
-
没有强制检查的异常。 如果需要,请捕获Shiro的CryptoException。
Shiro的CipherService API还有其他好处,例如既支持基于字节数组的加密/解密(称为“块”操作),又支持基于流的加密/解密(例如,加密音频或视频)。
Java密码术不必太痛苦。
Shiro的密码学支持旨在简化您保护数据安全的工作。