libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 8547eeb81f0a6a433331571a9eaaa148851e89fa
parent 9e3880d39fe2793ee4cd459799a307154f7369c4
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Thu, 21 Nov 2019 04:16:07 +0100

Routines to protect private keys with passphrases.

Diffstat:
Msandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msandbox/src/test/kotlin/CryptoUtilTest.kt | 24+++++++++++++++++++++++-
2 files changed, 83 insertions(+), 8 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt @@ -22,17 +22,14 @@ package tech.libeufin.sandbox import org.bouncycastle.jce.provider.BouncyCastleProvider import java.io.ByteArrayOutputStream import java.math.BigInteger -import java.security.KeyFactory -import java.security.KeyPairGenerator -import java.security.MessageDigest -import java.security.Signature +import java.security.* import java.security.interfaces.RSAPrivateCrtKey import java.security.interfaces.RSAPublicKey import java.security.spec.* -import javax.crypto.Cipher -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey +import javax.crypto.* import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.PBEKeySpec +import javax.crypto.spec.PBEParameterSpec import javax.crypto.spec.SecretKeySpec /** @@ -214,4 +211,60 @@ object CryptoUtil { } return digest.digest() } + + + fun decryptSecret(data: EncryptedPrivateKeyInfo, passphrase: String): RSAPrivateCrtKey { + + /* make key out of passphrase */ + val pbeKeySpec = PBEKeySpec(passphrase.toCharArray()) + val keyFactory = SecretKeyFactory.getInstance(data.algName) + val secretKey = keyFactory.generateSecret(pbeKeySpec) + + /* Make a cipher */ + val cipher = Cipher.getInstance(data.algName) + cipher.init( + Cipher.DECRYPT_MODE, + secretKey, + data.algParameters // has hash count and salt + ) + + /* Ready to decrypt */ + val decryptedKeySpec: PKCS8EncodedKeySpec = data.getKeySpec(cipher) + val priv = KeyFactory.getInstance("RSA").generatePrivate(decryptedKeySpec) + if (priv !is RSAPrivateCrtKey) + throw Exception("wrong encoding") + return priv + } + + fun encryptSecret(data: ByteArray, passphrase: String): ByteArray { + + /* Cipher parameters: salt and hash count */ + val hashIterations = 30 + val salt = ByteArray(8) + SecureRandom().nextBytes(salt) + val pbeParameterSpec = PBEParameterSpec(salt, hashIterations) + + /* *Other* cipher parameters: symmetric key (from password) */ + val pbeAlgorithm = "PBEWithSHA1AndDESede" + val pbeKeySpec = PBEKeySpec(passphrase.toCharArray()) + val keyFactory = SecretKeyFactory.getInstance(pbeAlgorithm) + val secretKey = keyFactory.generateSecret(pbeKeySpec) + + /* Make a cipher */ + val cipher = Cipher.getInstance(pbeAlgorithm) + cipher.init(Cipher.ENCRYPT_MODE, secretKey, pbeParameterSpec) + + /* ready to encrypt now */ + val cipherText = cipher.doFinal(data) + + /* Must now bundle a PKCS#8-compatible object, that contains + * algorithm, salt and hash count information */ + + val bundleAlgorithmParams = AlgorithmParameters.getInstance(pbeAlgorithm) + bundleAlgorithmParams.init(pbeParameterSpec) + + val bundle = EncryptedPrivateKeyInfo(bundleAlgorithmParams, cipherText) + + return bundle.encoded + } } diff --git a/sandbox/src/test/kotlin/CryptoUtilTest.kt b/sandbox/src/test/kotlin/CryptoUtilTest.kt @@ -22,9 +22,9 @@ package tech.libeufin.sandbox import org.junit.Test import java.security.KeyPairGenerator import java.security.interfaces.RSAPrivateCrtKey +import javax.crypto.EncryptedPrivateKeyInfo import kotlin.test.assertEquals import kotlin.test.assertTrue -import kotlin.collections.contentEquals class CryptoUtilTest { @@ -74,4 +74,26 @@ class CryptoUtilTest { val sig = CryptoUtil.signEbicsA006(data, keyPair.private) assertTrue(CryptoUtil.verifyEbicsA006(sig, data, keyPair.public)) } + + @Test + fun testPassphraseEncryption() { + + val keyPair = CryptoUtil.generateRsaKeyPair(1024) + + /* encrypt with original key */ + val data = "Hello, World!".toByteArray(Charsets.UTF_8) + val secret = CryptoUtil.encryptEbicsE002(data, keyPair.public) + + /* encrypt and decrypt private key */ + val encPriv = CryptoUtil.encryptSecret(keyPair.private.encoded, "secret") + val plainPriv = CryptoUtil.decryptSecret(EncryptedPrivateKeyInfo(encPriv),"secret") + + /* decrypt with decrypted private key */ + val revealed = CryptoUtil.decryptEbicsE002(secret, plainPriv) + + assertEquals( + String(revealed, charset = Charsets.UTF_8), + String(data, charset = Charsets.UTF_8) + ) + } } \ No newline at end of file