Encryption plugin for Grails

  • Tags: security, encryption
  • Latest: 2.0
  • Last Updated: 27 March 2009
  • Grails version: *
  • Authors: Ricardo J. Mendez and Robert Fischer
4 votes

3% of Grails users

Dependency:
compile ":crypto:2.0"

Summary

Installation

grails install-plugin crypto

Description

A Grails plugin with some Groovy wrappers for both Blowfish and PGP, as well as a tool class for generating random passwords, salting passwords when hashing them, and verifying salted passwords. Uses the Bouncy Castle encryption libraries.

Password Management

Mr. Smart Developer Says:

"Storing plaintext passwords is EEEEEEVIL. Simply don't do it, because you're exposing your users to all kinds of potential threat, and may be tempted for evil yourself. If you aren't going to use some kind of externalized authentication service, then store your passwords salted and hashed."

While Mr. Smart Developer is totally right, that's traditionally been difficult to do in Grails, because there's no easy way to salt and hash passwords. This plugin solves that.

Password management utilities are found in cr.co.arquetipos.password.PasswordTools. In there are two sets of static methods for working with salted and hashed passwords:

saltPasswordBase64(String) / checkDigestBase64(String,String)
saltPasswordHex(String) / checkDigestHex(String,String)
The first method in each of these sets generates a String consisted of the salted and hashed password, and the second checks a submitted password (the first argument) against a salted and hashed password (the second argument).

In addition, there are two utility static methods to return a random login and password:

generateRandomPassword(int size=10)
generateRandomLogin(int size=10)

The set of allowed password characters are the alphanumerics along with "!@,;.-=+" (not including quotes). The set of allowed login characters are the alphanumerics along with just "." (not including quotes).

Blowfish

The following encrypts a message with blowfish, and attempts to decrypt it with both the correct password and a bad one.

String message = 'Hush hush'
String password = 'DeVitto'
String badPassword = 'Danny'

// Encrypts the message as a byte array byte[] encrypted = Blowfish.encrypt(message.getBytes(), password) assert encrypted

// Decrypts with the correct password, and compares def decrypted = Blowfish.decrypt(encrypted, password) assert decrypted String decryptedString = new String(decrypted)

// Decrypted strings may contain trailing char(0)s decryptedString = decryptedString.trim()

println "$decryptedString ${decryptedString.getBytes()}" println "$message ${message.getBytes()}"

assertEquals decryptedString, message

// Decrypting with a bad password returns null decrypted = Blowfish.decrypt(encrypted, badPassword) assert decrypted == null

Encrypted values can also be returned base64-encoded, where there's an extra option to trim the result string.
String message = 'Hush hush'
String password ='DeVitto'
String badPassword = 'Danny'

// Encrypts a message and returns it base64-encoded String encrypted = Blowfish.encryptBase64(message, password) assert encrypted String decrypted = Blowfish.decryptBase64(encrypted, password) assert decrypted

assertEquals decrypted, message

// There's an option to not trim the return value, even though the // default is to do so. In that case, the decrypted value may not // exactly match the original message decrypted = Blowfish.decryptBase64(encrypted, password, false) assert decrypted != message assertEquals decrypted.trim(), message

println "The following two rows should be different:" println "$decrypted ${decrypted.getBytes()}"

// Decrypting with an invalid password returns null decrypted = Blowfish.decryptBase64(encrypted, badPassword) assertEquals decrypted, null

PGP

Key generation and encoding:

// Generates a PGP key pair
def pgp = PGP.generateKeyPair()

// Verifies that all values are present assert pgp assert pgp.keyPair assert pgp.publicKey assert pgp.privateKey

assertEquals pgp.keyPair.publicKey, pgp.publicKey assertEquals pgp.keyPair.privateKey, pgp.privateKey

// Obtains the public and private keys, encoded armored-encoded String encodedPublic = pgp.encodedPublicKey String encodedPrivate = pgp.encodedPrivateKey

// Creates PGP objects which contain only the public or private keys PGP publicOnly = new PGP(encodedPublic, '') PGP privateOnly = new PGP('', encodedPrivate)

// Verifies that the previously encoded public key matches the // one from the object we just created with it assertEquals encodedPublic, publicOnly.encodedPublicKey

assert publicOnly assert privateOnly // Key created from the private key must have the public key as well assert privateOnly.encodedPrivateKey assert privateOnly.encodedPublicKey

println "From privateOnly:" println publicOnly.encodedPublicKey println privateOnly.encodedPublicKey println privateOnly.encodedPrivateKey

For encryption, the following code encrypts a message in three different ways, which should all be equivalent. While the example returns the encrypted text as base64-encoded, a function that returns it as a byte array is also available (the equivalent call would be pgp.decrypt)
def pgp = PGP.generateKeyPair()

String encodedPublic = pgp.encodedPublicKey String encodedPrivate = pgp.encodedPrivateKey

PGP publicOnly = new PGP(encodedPublic, '') PGP privateOnly = new PGP('', encodedPrivate)

assert publicOnly assert privateOnly

assertEquals encodedPublic, publicOnly.encodedPublicKey

String message = 'Hush hush' /* Encrypts the message in three different ways: with the original key pair, with the one that only has the public key, and with the one that was generated from the private key. They will later be decrypted in several ways to ensure that all are equivalent. */ String encrypted = pgp.encryptBase64(message) String encrypted2 = publicOnly.encryptBase64(message) String encrypted3 = privateOnly.encryptBase64(message)

assert encrypted assert encrypted2 assert encrypted3

println "Encrypted number one:" println encrypted println "Encrypted number two:" println encrypted2

String decryptedPrivate = privateOnly.decryptBase64(encrypted) assert decryptedPrivate

String decryptedBoth = pgp.decryptBase64(encrypted2) assert decryptedBoth

assertEquals decryptedPrivate, decryptedBoth assertEquals decryptedPrivate, message

decryptedPrivate = privateOnly.decryptBase64(encrypted2) assert decryptedPrivate

decryptedBoth = pgp.decryptBase64(encrypted) assert decryptedBoth

String decryptedPrivate3 = privateOnly.decryptBase64(encrypted3) assert decryptedPrivate3

String decryptedBoth3 = pgp.decryptBase64(encrypted3) assert decryptedBoth3

assertEquals decryptedPrivate, decryptedBoth assertEquals decryptedPrivate, message assertEquals decryptedBoth3, message assertEquals decryptedPrivate3, message

And this code encrypts a message and instead of returning the byte array base64-encoded, returns it encoded as armored text.
def pgp = PGP.generateKeyPair()

String encodedPublic = pgp.encodedPublicKey String encodedPrivate = pgp.encodedPrivateKey

PGP publicOnly = new PGP(encodedPublic, '') PGP privateOnly = new PGP('', encodedPrivate)

assert publicOnly assert privateOnly

assertEquals encodedPublic, publicOnly.encodedPublicKey

String message = 'Hush hush' String encrypted = pgp.encryptArmored(message) String encrypted2 = pgp.encryptArmored(message)

println "Armored 1:n$encryptedn" println "Armored 2:n$encrypted2n"

In the case of a decryption failure, the decrypt function will return null:
def pgp = PGP.generateKeyPair()
def pgp2 = PGP.generateKeyPair()

String message = 'Hush hush' String encrypted = pgp.encryptBase64(message) assert encrypted

String decrypted = pgp2.decryptBase64(encrypted) assertEquals decrypted, null

While the default encodedPrivateKey function returns the key encrypted with a blank passphrase, one can easily be added:
def pgp = PGP.generateKeyPair()

String passphrase = PasswordTools.generateRandomPassword(10) assert passphrase assertEquals passphrase.size(), 10

String encodedPublic = pgp.encodedPublicKey String encodedPrivate = pgp.getEncodedPrivateKey(passphrase)

// ..... // The encoded key is later instantiated using the passphrase

PGP privateOnly = new PGP('', encodedPrivate, passphrase) assert privateOnly

The above code would fail in JDK6 because of a String.getBytes() problem. The solution would be to:

import java.nio.charset.Charset

....

byte (+) xencrypted = Blowfish.encrypt(message.getBytes(Charset.forName("ISO-8859-1")), password)

Similar for decrypt:

String decryptedString = new String(decrypted,Charset.forName("ISO-8859-1"))

BTW, Some how "UTF-8" does not work here