commit 855c0bb1fc8a9287320fa161552a85bac9acb98a
parent e30e4700e165ec5c38d21a9619b4f24ef8f0f21a
Author: Florian Dold <florian.dold@gmail.com>
Date: Mon, 4 Nov 2019 12:39:15 +0100
implement and test EBICS E002
Diffstat:
2 files changed, 58 insertions(+), 10 deletions(-)
diff --git a/sandbox/src/main/kotlin/CryptoUtil.kt b/sandbox/src/main/kotlin/CryptoUtil.kt
@@ -20,12 +20,11 @@
package tech.libeufin.sandbox
import org.bouncycastle.jce.provider.BouncyCastleProvider
+import java.io.ByteArrayOutputStream
import java.lang.Exception
import java.math.BigInteger
import java.security.KeyFactory
import java.security.KeyPairGenerator
-import java.security.PrivateKey
-import java.security.PublicKey
import java.security.interfaces.RSAPrivateCrtKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.PKCS8EncodedKeySpec
@@ -33,6 +32,15 @@ import java.security.spec.RSAPublicKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
+import java.security.MessageDigest
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+
+
+
+
+
/**
* RSA key pair.
@@ -120,15 +128,52 @@ class CryptoUtil {
return keyFactory.generatePublic(tmp) as RSAPublicKey
}
- fun encryptEbicsE002(data: ByteArray, signingPrivateKey: RSAPrivateCrtKey) {
- val prov = BouncyCastleProvider()
+ /**
+ * Hash an RSA public key according to the EBICS standard (EBICS 2.5: 4.4.1.2.3).
+ */
+ fun getEbicsPublicKeyHash(publicKey: RSAPublicKey): ByteArray {
+ val keyBytes = ByteArrayOutputStream()
+ keyBytes.writeBytes(publicKey.publicExponent.toByteArray())
+ keyBytes.write(' '.toInt())
+ keyBytes.writeBytes(publicKey.modulus.toByteArray())
+ val digest = MessageDigest.getInstance("SHA-256")
+ return digest.digest(keyBytes.toByteArray())
+ }
+
+ /**
+ * Encrypt data according to the EBICS E002 encryption process.
+ */
+ fun encryptEbicsE002(data: ByteArray, encryptionPublicKey: RSAPublicKey): EncryptionResult {
val keygen = KeyGenerator.getInstance("AES", bouncyCastleProvider)
keygen.init(128)
- val transportKey = keygen.generateKey()
+ val transactionKey = keygen.generateKey()
+ val symmetricCipher = Cipher.getInstance("AES/CBC/X9.23Padding", bouncyCastleProvider)
+ val ivParameterSpec = IvParameterSpec(ByteArray(16))
+ symmetricCipher.init(Cipher.ENCRYPT_MODE, transactionKey, ivParameterSpec)
+ val encryptedData = symmetricCipher.doFinal(data)
+ val asymmetricCipher = Cipher.getInstance("RSA/None/PKCS1Padding", bouncyCastleProvider)
+ asymmetricCipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey)
+ val encryptedTransactionKey = asymmetricCipher.doFinal(transactionKey.encoded)
+ val pubKeyDigest = getEbicsPublicKeyHash(encryptionPublicKey)
+ return EncryptionResult(encryptedTransactionKey, pubKeyDigest, encryptedData)
+ }
+
+ fun decryptEbicsE002(enc: EncryptionResult, privateKey: RSAPrivateCrtKey): ByteArray {
+ val asymmetricCipher = Cipher.getInstance("RSA/None/PKCS1Padding", bouncyCastleProvider)
+ asymmetricCipher.init(Cipher.DECRYPT_MODE, privateKey)
+ val transactionKeyBytes = asymmetricCipher.doFinal(enc.encryptedTransactionKey)
+ val secretKeySpec = SecretKeySpec(transactionKeyBytes, "AES")
+ val symmetricCipher = Cipher.getInstance("AES/CBC/X9.23Padding", bouncyCastleProvider)
+ val ivParameterSpec = IvParameterSpec(ByteArray(16))
+ symmetricCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
+ val data = symmetricCipher.doFinal(enc.encryptedData)
+ return data
+ }
- val cipher = Cipher.getInstance("AES/CBC/X9.23Padding", bouncyCastleProvider)
- cipher.init(Cipher.ENCRYPT_MODE, transportKey)
- val encryptedData = cipher.doFinal(data)
+ fun ByteArray.toHexString() : String {
+ return this.joinToString("") {
+ java.lang.String.format("%02x", it)
+ }
}
}
}
diff --git a/sandbox/src/test/kotlin/CryptoUtilTest.kt b/sandbox/src/test/kotlin/CryptoUtilTest.kt
@@ -24,6 +24,7 @@ import java.security.KeyPairGenerator
import java.security.interfaces.RSAPrivateCrtKey
import kotlin.test.assertEquals
import kotlin.test.assertTrue
+import kotlin.collections.contentEquals
class CryptoUtilTest {
@@ -59,8 +60,10 @@ class CryptoUtilTest {
@Test
fun testEbicsE002() {
- val data = "Hello, World!"
+ val data = "Hello, World!".toByteArray()
val keyPair = CryptoUtil.generateRsaKeyPair(1024)
- CryptoUtil.encryptEbicsE002(data.toByteArray(), keyPair.private)
+ val enc = CryptoUtil.encryptEbicsE002(data, keyPair.public)
+ val dec = CryptoUtil.decryptEbicsE002(enc, keyPair.private)
+ assertTrue(data.contentEquals(dec))
}
}
\ No newline at end of file