Grails

Allow encryption of database password in Datasource

Details

  • Type: Improvement Improvement
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: 1.1-beta1
  • Component/s: Configuration
  • Labels:
    None
  • Environment:
    This is in all environments where one has a password to connect to a database.

Description

In Datasource.groovy, the password field is 'clear text'. This is not acceptible in our company, and would prevent our company (Citigroup) from using Grails. The suggestion is to do what we now do in the Jrun configuration file.
This is to supply the encrypted password, plus the name of a java class that is used to decrypt the password.
I'm including here the description of how this works. This is just a suggestion - if Graeme et al has a better way, that's all the better.

Here is the description of how this now works in Jrun which can be used as a model:

AES Encryption

Jrun comes with a Java class that provides AES encryption named JRunCrypterForTwofish. This implements the AES Encyrption technique known as TwoFish, and is a sophisticated and powerful algorthim that complies with Citigroup encryption standards. In order to encrypt the password, enter this command:

java jrun.security.JRunCrypterForTwofish thisword

Then plug in the encryption lines n the jrun-resources.xml file:

<password>8F89EBB13AC7C0840F2C623CFBB284D8</password>
<encrypted>true</encrypted>
<encryption-class>jrun.security.JRunCrypterForTwofish</encryption-class>

3DES Encryption
3DES is another very advanced encryption standard. Jrun does not come with a Java class that automaically provides this type of encryption. However there are many Java classes that provide this standard, either in the public domain or available for purchase. The two steps are the same: using the class to encrypt the password, and then specifying in jrun-resources.xml the encrypted password and the name of the class to be used.

First use the java class that provides 3DES Encryption to encrypt the password. Then specify the encrypted password and the class as follows in jrun-resources.xml:

<password>ENCRYPTEDPASSWORD</password>
<encrypted>true</encrypted>
<encryption-class>java.class.that.provides.3DES.security</encryption-class>

Activity

Hide
Graeme Rocher added a comment -

So in DataSource.groovy you can now specify an encrypted password as follows:

dataSource {
       username = "foo"
       password = "438uodf9s872398783r"
       passwordEncryptionCodec="my.company.encryption.BlowfishCodec"
}

You need to provide an implementation by specifying the paswordEncryptionCodec property. The class specified basically needs to implement two methods, encode and decode. For example below is a sample implementation:

iimport java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

class BlowfishCodec {
    static encode(target) {
        def cipher = getCipher(Cipher.ENCRYPT_MODE)
        return cipher.doFinal(target.bytes).encodeBase64()
    }

    static decode(target) {
        def cipher = getCipher(Cipher.DECRYPT_MODE)
        return new String(cipher.doFinal(target.decodeBase64()))
    }

    private static getCipher(mode) {
        def keySpec = new PBEKeySpec(getPassword())
        def cipher = Cipher.getInstance("Blowfish")
        def keyFactory = SecretKeyFactory.getInstance("Blowfish")
        cipher.init(mode, keyFactory.generateSecret(keySpec))        
    }

    private static getPassword() { "secret".toCharArray() }

   static void main(args) {
        if(args) {
            println encode(args[0])
        }
   }
}

with this class you can also generate a key by running the main method. Note the password here is hard coded, this is only a sample impl. If you are happy with this implementation then please close the issue.

Show
Graeme Rocher added a comment - So in DataSource.groovy you can now specify an encrypted password as follows:
dataSource {
       username = "foo"
       password = "438uodf9s872398783r"
       passwordEncryptionCodec="my.company.encryption.BlowfishCodec"
}
You need to provide an implementation by specifying the paswordEncryptionCodec property. The class specified basically needs to implement two methods, encode and decode. For example below is a sample implementation:
iimport java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

class BlowfishCodec {
    static encode(target) {
        def cipher = getCipher(Cipher.ENCRYPT_MODE)
        return cipher.doFinal(target.bytes).encodeBase64()
    }

    static decode(target) {
        def cipher = getCipher(Cipher.DECRYPT_MODE)
        return new String(cipher.doFinal(target.decodeBase64()))
    }

    private static getCipher(mode) {
        def keySpec = new PBEKeySpec(getPassword())
        def cipher = Cipher.getInstance("Blowfish")
        def keyFactory = SecretKeyFactory.getInstance("Blowfish")
        cipher.init(mode, keyFactory.generateSecret(keySpec))        
    }

    private static getPassword() { "secret".toCharArray() }

   static void main(args) {
        if(args) {
            println encode(args[0])
        }
   }
}
with this class you can also generate a key by running the main method. Note the password here is hard coded, this is only a sample impl. If you are happy with this implementation then please close the issue.
Hide
Peter Ledbrook added a comment -

Note that you can also use a class literal:

import my.company.encryption.BlowfishCodec
dataSource {
       username = "foo"
       password = "438uodf9s872398783r"
       passwordEncryptionCodec = BlowfishCodec
}

I also think that encode and decode should be closures so that the codec follows the "Codec" convention, which would mean putting it in the grails-app/utils directory. This would then allow you to do:

"some string".encodeAsBlowfish()

In addition, something that might be nice is a Grails command that generates the encoded password using the configured encryption codec.

Show
Peter Ledbrook added a comment - Note that you can also use a class literal:
import my.company.encryption.BlowfishCodec
dataSource {
       username = "foo"
       password = "438uodf9s872398783r"
       passwordEncryptionCodec = BlowfishCodec
}
I also think that encode and decode should be closures so that the codec follows the "Codec" convention, which would mean putting it in the grails-app/utils directory. This would then allow you to do:
"some string".encodeAsBlowfish()
In addition, something that might be nice is a Grails command that generates the encoded password using the configured encryption codec.
Hide
Peter Ledbrook added a comment -

Just to confirm: this is the standard Grails codec mechanism. "encode" and "decode" can either be implemented as methods or closures. In addition, you can specify the name of the codec based on convention:

dataSource {
    username = "foo"
    password = "438uodf9s872398783r"
    passwordEncryptionCodec = "blowfish"
}
Show
Peter Ledbrook added a comment - Just to confirm: this is the standard Grails codec mechanism. "encode" and "decode" can either be implemented as methods or closures. In addition, you can specify the name of the codec based on convention:
dataSource {
    username = "foo"
    password = "438uodf9s872398783r"
    passwordEncryptionCodec = "blowfish"
}
Hide
Peter Ledbrook added a comment -

First, a correction to a previous comment: at the moment "encode" and "decode" must be closures for your class to be treated as a standard Grails codec, but we plan to allow methods as well for the final 1.1 release (GRAILS-3725).

Here's an example that works out of the box:

import java.security.*
import javax.crypto.*
import javax.crypto.spec.*

class DESCodec {
    static encode = { String target ->
        def cipher = getCipher(Cipher.ENCRYPT_MODE)
        return cipher.doFinal(target.bytes).encodeBase64()
    }

    static decode = { String target ->
        def cipher = getCipher(Cipher.DECRYPT_MODE)
        return new String(cipher.doFinal(target.decodeBase64()))
    }

    private static getCipher(mode) {
        def keySpec = new DESKeySpec(getPassword())
        def cipher = Cipher.getInstance("DES")
        def keyFactory = SecretKeyFactory.getInstance("DES")
        cipher.init(mode, keyFactory.generateSecret(keySpec))
        return cipher
    }

    private static getPassword() { "secret12".getBytes("UTF-8") }

    static void main(args) {
        if(args) {
            println encode(args[0])
        }
    }
}

This should go in the file grails-app/utils/DESCodec.groovy in your Grails application. If you want to create an encrypted password to put in DataSource.groovy, then run the class like so:

groovy grails-app/utils/DESCodec.groovy password

Replace "password" with the real password. This will print the encrypted password to the console, so just copy it into your DataSource.groovy.

One thing you should be aware of in this example is that the key used for the encryption ("secret12") is insecure since it's hard-coded in the class. Instead, you should probably retrieve the key from a keystore or maybe a properties file.

Show
Peter Ledbrook added a comment - First, a correction to a previous comment: at the moment "encode" and "decode" must be closures for your class to be treated as a standard Grails codec, but we plan to allow methods as well for the final 1.1 release (GRAILS-3725). Here's an example that works out of the box:
import java.security.*
import javax.crypto.*
import javax.crypto.spec.*

class DESCodec {
    static encode = { String target ->
        def cipher = getCipher(Cipher.ENCRYPT_MODE)
        return cipher.doFinal(target.bytes).encodeBase64()
    }

    static decode = { String target ->
        def cipher = getCipher(Cipher.DECRYPT_MODE)
        return new String(cipher.doFinal(target.decodeBase64()))
    }

    private static getCipher(mode) {
        def keySpec = new DESKeySpec(getPassword())
        def cipher = Cipher.getInstance("DES")
        def keyFactory = SecretKeyFactory.getInstance("DES")
        cipher.init(mode, keyFactory.generateSecret(keySpec))
        return cipher
    }

    private static getPassword() { "secret12".getBytes("UTF-8") }

    static void main(args) {
        if(args) {
            println encode(args[0])
        }
    }
}
This should go in the file grails-app/utils/DESCodec.groovy in your Grails application. If you want to create an encrypted password to put in DataSource.groovy, then run the class like so:
groovy grails-app/utils/DESCodec.groovy password
Replace "password" with the real password. This will print the encrypted password to the console, so just copy it into your DataSource.groovy. One thing you should be aware of in this example is that the key used for the encryption ("secret12") is insecure since it's hard-coded in the class. Instead, you should probably retrieve the key from a keystore or maybe a properties file.
Hide
Graeme Rocher added a comment -

Bulk closing bunch of resolved issues

Show
Graeme Rocher added a comment - Bulk closing bunch of resolved issues
Hide
priyank added a comment -

Hello,
I tried doing the same by using DESCodec in util directory and having the datasource block defined as follows:
dataSource { pooled = true driverClassName = "com.mysql.jdbc.Driver" username = "root" password = "sw3s55dH+Dg6D2EhjMlrg==" passwordEncryptionCodec= "gra.reports.DESCodec" dbname="mydb" }

I get the encrypted string by running the codec seperately as specified above.
The following change wont allow the server to start, it gets shutdown during the startup. I also tried to put the DESCodec in a package and then importing that package in the datasource.groovy, but still not working.
I really want to encrypt the password here and cannot just do the config externalization.
Looking forward for assistance on this.
Thanks
Priyank

Show
priyank added a comment - Hello, I tried doing the same by using DESCodec in util directory and having the datasource block defined as follows: dataSource { pooled = true driverClassName = "com.mysql.jdbc.Driver" username = "root" password = "sw3s55dH+Dg6D2EhjMlrg==" passwordEncryptionCodec= "gra.reports.DESCodec" dbname="mydb" } I get the encrypted string by running the codec seperately as specified above. The following change wont allow the server to start, it gets shutdown during the startup. I also tried to put the DESCodec in a package and then importing that package in the datasource.groovy, but still not working. I really want to encrypt the password here and cannot just do the config externalization. Looking forward for assistance on this. Thanks Priyank

People

Vote (0)
Watch (2)

Dates

  • Created:
    Updated:
    Resolved: