The Java API for creating message digests and ciphers for symmetric encryption are complex difficult to get started with. This groovy extension provides simple interfaces that make crypto groovier.
The main goals are:
-
A simple interface
-
Secure choices for defaults
-
Interoperate easily with other crypto libraries
-
Table of Contents {:toc}
Byte arrays and input streams have been augumented with methods
md5()
, sha1()
, and digest()
. digest()
takes the name of a
message digest algorithm as an argument; md5()
and sha1()
are
shortcuts for the two most commonly used digests. To get the MD5
digest of a string:
"Hello World!".bytes.md5().encodeHex()
===> ed076287532e86365e841e92bfc50d8c
The digest is returned as an array of bytes (16 bytes in the case of MD5). encodeHex() displays it the familiar readable form.
The digest of an input stream can also be generated:
// confirm the sha1 of groovy-all artifact from http://search.maven.org/#artifactdetails|org.codehaus.groovy|groovy-all|2.1.5|jar
def jarLocation = 'http://search.maven.org/remotecontent?filepath=org/codehaus/groovy/groovy-all/2.1.5/groovy-all-2.1.5.jar'
def sha1 = new URL(jarLocation).openConnection().inputStream.sha1().encodeHex().toString()
assert sha1 == 'eda9522cc90f16c06dd51739e2d02daafad0b36f'
Byte arrays and input streams have been augumented with methods
mac()
and , hmac()
. Like the digest methods, they take the name
of an algorithm as an argument but they also require a key. To get a
SHA256 HMAC of a string:
key = "password".toKey(length: 32)
hmac = "some plaintext".bytes.hmac(key: key).encodeHex()
===> 29e17e11f251d058eed0ac05933be6dafad4afca9d30c5a1783a83185af74937
Method toKey()
has been added to byte arrays and Strings. The byte
array version of toKey()
requires the byte array to be the exact
size of the key. For example, AES-128 requires a 16 byte (128 bit)
key:
key = '0123456789012345'.bytes.toKey()
The String version of toKey
uses
PBKDF2 to generate a key from
a String of any size. The salt value should be overriden:
key = 'correct horse battery staple'.toKey(salt: 'TrOub4dor&3'.bytes)
Byte arrays and input streams have also been augmented with
encrypt()
and decrypt()
.
Both of these methods take a map of parameters that must include at minimum, a key. Some examples:
key = "password".toKey()
ciphertext = "some plaintext".bytes.encrypt(key: key)
new String(ciphertext.decrypt(key: key))
===> some plaintext
The default is AES/CBC/PKCS5Padding, with a randomly generated
initalization vector. The initalization vector is prepended to the
output cipher text; to set the initialization vector manually, add
prependIvToCipherText: true
and initializationVector: myIV
to the
parameters:
key = "password".toKey()
ciphertext = "some plaintext".bytes.encrypt(key: key, initializationVector: [0] * 16, prependIvToCipherText: false) // don't actually use this IV!
new String(ciphertext.decrypt(key: key, initializationVector: [0] * 16, prependIvToCipherText: false))
===> some plaintext
Another example, padding manually using ECB mode:
key = "password".toKey()
ciphertext = "some plaintext".padRight(16).bytes.encrypt(key: key, padding: 'NoPadding', mode: 'ECB')
new String(ciphertext.decrypt(key: key, padding: 'NoPadding', mode: 'ECB')).trim()
===> some plaintext
All the algorithms supported by JCE are supported:
key = "password".toKey(algorithm: 'DES', length: 8)
ciphertext = "some plaintext".bytes.encrypt(key: key, algorithm: 'DES')
new String(ciphertext.decrypt(key: key, algorithm: 'DES'))
===> some plaintext
Use InputStream an input stream with an out parameter to encrypt a stream:
key = "password".toKey()
out = new FileOutputStream('README.md.encrypted')
new FileInputStream('README.md').encrypt(key: key, out: out)