使用 Groovy™ 进行加密和解密
发布时间:2022-09-19 02:34PM
受这篇近期博客文章的启发,这里有一个示例,展示如何使用 Groovy 进行加密和解密。
使用 JDK 加密类
首先,我们需要一些要加密的文本。我们将使用上述博客文章中的一个摘录。
var text = 'Contrary to popular belief, Lorem Ipsum is not simply random text. It has \
roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.'
接下来,我们将为我们的密码实例创建一个工厂,生成一个密钥,并设置一个初始化向量。
首先,密码工厂
var factory = { Cipher.getInstance('AES/CBC/PKCS5Padding') }
接下来我们生成我们的密钥。我们的密钥就是我们的密码。只有拥有密码的人才能解密加密的消息。我们可以使用任何随机位作为密钥,但像密码一样,我们希望选择一个强密钥而不是弱密钥。加密库提供了生成此类密钥的类。我们只需要提供密钥大小。AES 支持 128、192 和 256 位密钥。我们这里选择 192 位。
var key = generateKey('AES', 192)
我们的代码使用这个辅助方法
def generateKey(String algorithm, Integer size) {
var generator = KeyGenerator.getInstance(algorithm)
generator.init(size)
generator.generateKey()
}
接下来,我们生成一个初始化向量
var ivParameterSpec = randomParameterSpec(factory)
它使用这个辅助方法(我们使用算法块大小作为初始化向量大小)
def randomParameterSpec(Closure<Cipher> factory) {
var block = new byte[factory().blockSize]
SecureRandom.instanceStrong.nextBytes(block)
new IvParameterSpec(block)
}
使用初始化向量是为了引入一些额外的随机性,以避免输入中重复的模式导致加密字节中出现重复的模式。
有了所有这些,我们几乎可以进行加密或解密了,但首先,让我们再定义两个辅助方法
def encrypt(byte[] bytes, Key key, IvParameterSpec spec, Closure<Cipher> factory) {
var cipher = factory()
cipher.init(ENCRYPT_MODE, key, spec)
cipher.doFinal(bytes)
}
def decrypt(byte[] bytes, Key key, IvParameterSpec spec, Closure<Cipher> factory) {
var cipher = factory()
cipher.init(DECRYPT_MODE, key, spec)
cipher.doFinal(bytes)
}
以下是我们加密和解密的方式
var encrypted = encrypt(text.bytes, key, ivParameterSpec, factory)
println "Encrypted bytes : $encrypted"
println "Encrypted text : ${new String(encrypted)}"
var decrypted = decrypt(encrypted, key, ivParameterSpec, factory)
println "Decrypted bytes : $decrypted"
println "Decrypted text : ${new String(decrypted)}"
其输出如下
Encrypted bytes : [-117, 36, 18, 69, -101, -8, 35, 93, -102, -49, -12, …, -19, -100]
Encrypted text : ‹$E›ø#]šÏôæ”Á˜çp^µ³=L(Ö^_ŒC>CIË„ö,1É8ÆŸ.Š?vßG,Èw‰å¼zÜf>?µ›D¹éÆk€ °˜2êÔ}í©àhl$>?¹¡Kå3ÔO?±&…êî¶Ê–¾°®q®à—0ú‘ÔhO<H¦ç®Ç”ÈhAëjó QPyƒy6Ĥ*´un¼ï¯m¨´ÙjeJtëº\ó6ƪKªœíœ
Decrypted bytes : [67, 111, 110, 116, 114, 97, 114, 121, 32, 116, 111, 32, …, 100, 46]
Decrypted text : Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.
我们可以看到一切都按预期工作,因为最终输出与我们的原始输入文本匹配。
使用 Bouncy Castle 库
import org.bouncycastle.jce.provider.BouncyCastleProvider
var bc = new BouncyCastleProvider()
factory = { Cipher.getInstance('CAST5', bc) }
key = generateKey('HmacSHA1', 128)
ivParameterSpec = randomParameterSpec(factory)
CAST5 是某些 GPG 和 PGP 版本中使用的默认算法。它默认不包含在 JDK 中,因此为此我们将使用 Bouncy Castle 库。
注意
我们现在像以前一样进行加密和解密
encrypted = encrypt(text.bytes, key, ivParameterSpec, factory)
println "Encrypted text : ${new String(encrypted)}"
decrypted = decrypt(encrypted, key, ivParameterSpec, factory)
println "Decrypted text : ${new String(decrypted)}"
其输出如下
Encrypted text : Mªá?r?v9£÷~4µT'›ÙÝÁl¿Þg¾0ñŽ¡?Ü=³9Q¬»3«ÖÁ¡µ ¾@4÷`FñÙŠfø7¥#›v¤Í–‰¼Ü¢ƒE6ôŽTÙlæÏz>o?àL›¡¢z1nÖo9]šOÔ¼SÔOÍ#Ý7LœÀî}ó5m%q•»l%/AWT´¢zH#t솱l¶£—Œ«©wˆÃ®>®Ü6ër-E
Decrypted text : Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.
其他有用功能
传递二进制数据,如我们的密钥或加密数据,本身就有问题。Groovy 提供了扩展方法来<编码>这类数据(以及相应的<解码>方法)。例如,我们可以用各种方式编码我们的密钥
var keyBytes = key.encoded
println keyBytes.encodeHex()
println keyBytes.encodeBase64()
println keyBytes.encodeBase64Url()
其输出如下(密钥是随机的,因此每次运行的输出都会不同)
85a0d3f0ce0cbe6402dc9579fbffcf1d
haDT8M4MvmQC3JV5+//PHQ==
haDT8M4MvmQC3JV5-__PHQ
Groovy 还提供了各种校验和的扩展方法(但在安全敏感的场景中,您可能需要考虑更强的校验和算法)
println "SHA256 : ${text.sha256()}"
println "MD5 : ${text.md5()}"
其输出如下
SHA256 : ccb184e35e4c32bafc730d84ec924ea2980035ea5fadb012e3b2b31abf4323c9
MD5 : 46c61a174c2dc99204521ca89f09f63c
如果您正在加密和解密整个文件,JDK 也有专门的类,从 Groovy 调用这些类也很容易。目前就这些。
参考文献
-
加密库比较(维基百科)
-
如何在 Java 中加密和解密数据(也在 medium.com 上)
-
Bouncy castle 在 Maven Central
-
Java 中的 AES 加密和解密 (Baeldung)
-
Java 中加密的初始化向量 (Baeldung)
-
Java 中的 3DES (Baeldung)
结论
我们简要了解了使用 Apache Groovy 进行加密和解密。