gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (3a0df963 -> dc03013d)


From: gnunet
Subject: [libeufin] branch master updated (3a0df963 -> dc03013d)
Date: Wed, 27 Mar 2024 01:11:28 +0100

This is an automated email from the git hooks/post-receive script.

antoine pushed a change to branch master
in repository libeufin.

    from 3a0df963 Support EBICS 3 key management
     new 00e8988a Clean ebics setup code
     new 9dfbc939 Improve self-signed certificate
     new dc03013d Clean and optimize EBICS mess

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 bank/src/main/kotlin/tech/libeufin/bank/Config.kt  |   4 +-
 .../main/kotlin/crypto/{utils.kt => CryptoUtil.kt} | 219 +++++++--------------
 .../kotlin/crypto/{password.kt => PwCrypto.kt}     |   0
 common/src/test/kotlin/CryptoUtilTest.kt           |  67 ++-----
 .../main/kotlin/tech/libeufin/nexus/EbicsFetch.kt  |   4 +-
 .../main/kotlin/tech/libeufin/nexus/EbicsSetup.kt  |  85 ++++----
 .../main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt |   6 +-
 .../main/kotlin/tech/libeufin/nexus/KeyFiles.kt    |  22 +--
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  |  81 +++-----
 nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt   |   4 +-
 .../kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt   |   4 +-
 .../tech/libeufin/nexus/ebics/EbicsCommon.kt       |  58 +++---
 .../tech/libeufin/nexus/ebics/EbicsConstants.kt    |   9 +-
 .../tech/libeufin/nexus/ebics/EbicsKeyMng.kt       |  83 ++++----
 nexus/src/test/kotlin/CliTest.kt                   |   4 +-
 nexus/src/test/kotlin/Keys.kt                      |   4 +-
 nexus/src/test/kotlin/MySerializers.kt             |  12 +-
 nexus/src/test/kotlin/XmlUtilTest.kt               |   2 +-
 nexus/src/test/kotlin/helpers.kt                   |   8 +-
 19 files changed, 259 insertions(+), 417 deletions(-)
 rename common/src/main/kotlin/crypto/{utils.kt => CryptoUtil.kt} (52%)
 rename common/src/main/kotlin/crypto/{password.kt => PwCrypto.kt} (100%)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
index 54f2c44b..f48a1b35 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
@@ -28,9 +28,7 @@ import java.time.Duration
 
 private val logger: Logger = LoggerFactory.getLogger("libeufin-bank")
 
-/**
- * Application the parsed configuration.
- */
+/** Configuration for libeufin-bank */
 data class BankConfig(
     val name: String,
     val regionalCurrency: String,
diff --git a/common/src/main/kotlin/crypto/utils.kt 
b/common/src/main/kotlin/crypto/CryptoUtil.kt
similarity index 52%
rename from common/src/main/kotlin/crypto/utils.kt
rename to common/src/main/kotlin/crypto/CryptoUtil.kt
index 1228eb91..a508c5b2 100644
--- a/common/src/main/kotlin/crypto/utils.kt
+++ b/common/src/main/kotlin/crypto/CryptoUtil.kt
@@ -43,31 +43,13 @@ import javax.crypto.spec.PBEParameterSpec
 import javax.crypto.spec.SecretKeySpec
 import tech.libeufin.common.*
 
-/**
- * Helpers for dealing with cryptographic operations in EBICS / LibEuFin.
- */
+/** Helpers for dealing with cryptographic operations in EBICS / LibEuFin */
 object CryptoUtil {
-    // TODO split common and ebics crypto
-
-    /**
-     * RSA key pair.
-     */
-    data class RsaCrtKeyPair(val private: RSAPrivateCrtKey, val public: 
RSAPublicKey)
-
-    // FIXME(dold): This abstraction needs to be improved.
-    class EncryptionResult(
-        val encryptedTransactionKey: ByteArray,
-        val pubKeyDigest: ByteArray,
-        val encryptedData: ByteArray,
-        val plainTransactionKey: SecretKey
-    )
 
     private val provider = BouncyCastleProvider()
 
-    /**
-     * Load an RSA private key from its binary PKCS#8 encoding.
-     */
-    fun loadRsaPrivateKey(encodedPrivateKey: ByteArray): RSAPrivateCrtKey {
+    /** Load an RSA private key from its binary PKCS#8 encoding  */
+    fun loadRSAPrivate(encodedPrivateKey: ByteArray): RSAPrivateCrtKey {
         val spec = PKCS8EncodedKeySpec(encodedPrivateKey)
         val priv = KeyFactory.getInstance("RSA").generatePrivate(spec)
         if (priv !is RSAPrivateCrtKey)
@@ -75,10 +57,8 @@ object CryptoUtil {
         return priv
     }
 
-    /**
-     * Load an RSA public key from its binary X509 encoding.
-     */
-    fun loadRsaPublicKey(encodedPublicKey: ByteArray): RSAPublicKey {
+    /** Load an RSA public key from its binary X509 encoding */
+    fun loadRSAPublic(encodedPublicKey: ByteArray): RSAPublicKey {
         val spec = X509EncodedKeySpec(encodedPublicKey)
         val pub = KeyFactory.getInstance("RSA").generatePublic(spec)
         if (pub !is RSAPublicKey)
@@ -86,52 +66,42 @@ object CryptoUtil {
         return pub
     }
 
-    /**
-     * Load an RSA public key from its components.
-     *
-     * @param exponent
-     * @param modulus
-     * @return key
-     */
-    fun loadRsaPublicKeyFromComponents(modulus: ByteArray, exponent: 
ByteArray): RSAPublicKey {
+    /** Create an RSA public key from its components: [modulus] and [exponent] 
*/
+    fun RSAPublicFromComponents(modulus: ByteArray, exponent: ByteArray): 
RSAPublicKey {
         val modulusBigInt = BigInteger(1, modulus)
         val exponentBigInt = BigInteger(1, exponent)
-
-        val keyFactory = KeyFactory.getInstance("RSA")
-        val tmp = RSAPublicKeySpec(modulusBigInt, exponentBigInt)
-        return keyFactory.generatePublic(tmp) as RSAPublicKey
+        val spec = RSAPublicKeySpec(modulusBigInt, exponentBigInt)
+        return KeyFactory.getInstance("RSA").generatePublic(spec) as 
RSAPublicKey
     }
 
-    fun loadRsaPublicKeyFromCertificate(certificate: ByteArray): RSAPublicKey {
-        val cf = CertificateFactory.getInstance("X.509");
-        val c = cf.generateCertificate(certificate.inputStream());
-        return c.getPublicKey() as RSAPublicKey
+    /** Extract an RSA public key from a [raw] X.509 certificate */
+    fun RSAPublicFromCertificate(raw: ByteArray): RSAPublicKey {
+        val certificate = 
CertificateFactory.getInstance("X.509").generateCertificate(raw.inputStream());
+        return certificate.getPublicKey() as RSAPublicKey
     }
 
-    /**
-     * Load an RSA public key from its binary X509 encoding.
-     */
-    fun getRsaPublicFromPrivate(rsaPrivateCrtKey: RSAPrivateCrtKey): 
RSAPublicKey {
-        val spec = RSAPublicKeySpec(rsaPrivateCrtKey.modulus, 
rsaPrivateCrtKey.publicExponent)
-        val pub = KeyFactory.getInstance("RSA").generatePublic(spec)
-        if (pub !is RSAPublicKey)
-            throw Exception("wrong encoding")
-        return pub
+    /** Generate an RSA public key from a [private] one */
+    fun RSAPublicFromPrivate(private: RSAPrivateCrtKey): RSAPublicKey {
+        val spec = RSAPublicKeySpec(private.modulus, private.publicExponent)
+        return KeyFactory.getInstance("RSA").generatePublic(spec) as 
RSAPublicKey
     }
 
-    fun certificateFromPrivate(rsaPrivateCrtKey: RSAPrivateCrtKey): 
X509Certificate {
-        val now = System.currentTimeMillis()
+    /** Generate a self-signed X.509 certificate from an RSA [private] key */
+    fun X509CertificateFromRSAPrivate(private: RSAPrivateCrtKey, name: 
String): X509Certificate {
+        val start = Date()
         val calendar = Calendar.getInstance()
-        calendar.time = Date(now)
-        calendar.add(Calendar.YEAR, 1) // TODO certificate validity
+        calendar.time = start
+        calendar.add(Calendar.YEAR, 1_000)
+        val end = calendar.time
 
+        val name = X500Name("CN=$name")
         val builder = JcaX509v3CertificateBuilder(
-            X500Name("CN=test"),  // TODO certificate CN
-            BigInteger(now.toString()), // TODO certificate serial number
-            Date(now), 
-            calendar.time, 
-            X500Name("CN=test"),
-            getRsaPublicFromPrivate(rsaPrivateCrtKey)
+            name,
+            BigInteger(20, Random()),
+            start,
+            end, 
+            name,
+            RSAPublicFromPrivate(private)
         )
 
         
@@ -148,30 +118,24 @@ object CryptoUtil {
         ))
         builder.addExtension(Extension.basicConstraints, true, 
BasicConstraints(true))
 
-        val certificate = 
JcaContentSignerBuilder("SHA256WithRSA").build(rsaPrivateCrtKey)
+        val certificate = 
JcaContentSignerBuilder("SHA256WithRSA").build(private)
         return JcaX509CertificateConverter()
             .setProvider(provider)
             .getCertificate(builder.build(certificate))
 
     }
 
-    /**
-     * Generate a fresh RSA key pair.
-     *
-     * @param nbits size of the modulus in bits
-     */
-    fun generateRsaKeyPair(nbits: Int): RsaCrtKeyPair {
+    /** Generate an RSA key pair of [keysize] */
+    fun genRSAPair(keysize: Int): Pair<RSAPrivateCrtKey, RSAPublicKey> {
         val gen = KeyPairGenerator.getInstance("RSA")
-        gen.initialize(nbits)
+        gen.initialize(keysize)
         val pair = gen.genKeyPair()
-        val priv = pair.private
-        val pub = pair.public
-        if (priv !is RSAPrivateCrtKey)
-            throw Exception("key generation failed")
-        if (pub !is RSAPublicKey)
-            throw Exception("key generation failed")
-        return RsaCrtKeyPair(priv, pub)
+        return Pair(pair.private as RSAPrivateCrtKey, pair.public as 
RSAPublicKey)
     }
+    /** Generate an RSA private key of [keysize] */
+    fun genRSAPrivate(keysize: Int): RSAPrivateCrtKey = 
genRSAPair(keysize).first
+    /** Generate an RSA public key of [keysize] */
+    fun genRSAPublic(keysize: Int): RSAPublicKey = genRSAPair(keysize).second
 
     /**
      * Hash an RSA public key according to the EBICS standard (EBICS 2.5: 
4.4.1.2.3).
@@ -185,73 +149,61 @@ object CryptoUtil {
         return digest.digest(keyBytes.toByteArray())
     }
 
-    fun encryptEbicsE002(data: InputStream, encryptionPublicKey: 
RSAPublicKey): EncryptionResult {
+    fun genEbicsE002Key(encryptionPublicKey: RSAPublicKey): Pair<SecretKey, 
ByteArray> {
+        // Gen transaction key
         val keygen = KeyGenerator.getInstance("AES", provider)
         keygen.init(128)
         val transactionKey = keygen.generateKey()
-        return encryptEbicsE002withTransactionKey(
-            data,
-            encryptionPublicKey,
-            transactionKey
+        // Encrypt transaction keyA
+        val cipher = Cipher.getInstance(
+            "RSA/None/PKCS1Padding",
+            provider
         )
+        cipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey)
+        val encryptedTransactionKey = cipher.doFinal(transactionKey.encoded)
+        return Pair(transactionKey, encryptedTransactionKey)
     }
+    
     /**
      * Encrypt data according to the EBICS E002 encryption process.
      */
-    fun encryptEbicsE002withTransactionKey(
-        data: InputStream,
-        encryptionPublicKey: RSAPublicKey,
-        transactionKey: SecretKey
-    ): EncryptionResult {
-        val symmetricCipher = Cipher.getInstance(
+    fun encryptEbicsE002(
+        transactionKey: SecretKey,
+        data: InputStream
+    ): CipherInputStream {
+        val cipher = Cipher.getInstance(
             "AES/CBC/X9.23Padding",
             provider
         )
         val ivParameterSpec = IvParameterSpec(ByteArray(16))
-        symmetricCipher.init(Cipher.ENCRYPT_MODE, transactionKey, 
ivParameterSpec)
-        val encryptedData = CipherInputStream(data, 
symmetricCipher).readAllBytes()
-        val asymmetricCipher = Cipher.getInstance(
+        cipher.init(Cipher.ENCRYPT_MODE, transactionKey, ivParameterSpec)
+        return CipherInputStream(data, cipher)
+    }
+
+    fun decryptEbicsE002Key(
+        privateKey: RSAPrivateCrtKey,
+        encryptedTransactionKey: ByteArray
+    ): SecretKeySpec {
+        val cipher = Cipher.getInstance(
             "RSA/None/PKCS1Padding",
             provider
         )
-        asymmetricCipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey)
-        val encryptedTransactionKey = 
asymmetricCipher.doFinal(transactionKey.encoded)
-        val pubKeyDigest = getEbicsPublicKeyHash(encryptionPublicKey)
-        return EncryptionResult(
-            encryptedTransactionKey,
-            pubKeyDigest,
-            encryptedData,
-            transactionKey
-        )
-    }
-
-    fun decryptEbicsE002(enc: EncryptionResult, privateKey: RSAPrivateCrtKey): 
ByteArray {
-        return decryptEbicsE002(
-            enc.encryptedTransactionKey,
-            enc.encryptedData.inputStream(),
-            privateKey
-        ).readBytes()
+        cipher.init(Cipher.DECRYPT_MODE, privateKey)
+        val transactionKeyBytes = cipher.doFinal(encryptedTransactionKey)
+        return SecretKeySpec(transactionKeyBytes, "AES")
     }
 
     fun decryptEbicsE002(
-        encryptedTransactionKey: ByteArray,
-        encryptedData: InputStream,
-        privateKey: RSAPrivateCrtKey
+        transactionKey: SecretKeySpec,
+        encryptedData: InputStream
     ): CipherInputStream {
-        val asymmetricCipher = Cipher.getInstance(
-            "RSA/None/PKCS1Padding",
-            provider
-        )
-        asymmetricCipher.init(Cipher.DECRYPT_MODE, privateKey)
-        val transactionKeyBytes = 
asymmetricCipher.doFinal(encryptedTransactionKey)
-        val secretKeySpec = SecretKeySpec(transactionKeyBytes, "AES")
-        val symmetricCipher = Cipher.getInstance(
+        val cipher = Cipher.getInstance(
             "AES/CBC/X9.23Padding",
             provider
         )
         val ivParameterSpec = IvParameterSpec(ByteArray(16))
-        symmetricCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, 
ivParameterSpec)
-        return CipherInputStream(encryptedData, symmetricCipher)
+        cipher.init(Cipher.DECRYPT_MODE, transactionKey, ivParameterSpec)
+        return CipherInputStream(encryptedData, cipher)
     }
 
     /**
@@ -308,31 +260,6 @@ object CryptoUtil {
         return priv
     }
 
-    fun encryptKey(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
-    }
-
-    fun hashStringSHA256(input: String): ByteArray {
-        return 
MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8))
-    }
+    fun hashStringSHA256(input: String): ByteArray =
+        
MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8))
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/crypto/password.kt 
b/common/src/main/kotlin/crypto/PwCrypto.kt
similarity index 100%
rename from common/src/main/kotlin/crypto/password.kt
rename to common/src/main/kotlin/crypto/PwCrypto.kt
diff --git a/common/src/test/kotlin/CryptoUtilTest.kt 
b/common/src/test/kotlin/CryptoUtilTest.kt
index 82f18ab9..6e062b1a 100644
--- a/common/src/test/kotlin/CryptoUtilTest.kt
+++ b/common/src/test/kotlin/CryptoUtilTest.kt
@@ -34,71 +34,38 @@ class CryptoUtilTest {
 
     @Test
     fun loadFromModulusAndExponent() {
-        val keyPair = CryptoUtil.generateRsaKeyPair(1024)
-        val pub2 = CryptoUtil.loadRsaPublicKeyFromComponents(
-            keyPair.public.modulus.toByteArray(),
-            keyPair.public.publicExponent.toByteArray()
+        val public = CryptoUtil.genRSAPublic(1024)
+        val pub2 = CryptoUtil.RSAPublicFromComponents(
+            public.modulus.toByteArray(),
+            public.publicExponent.toByteArray()
         )
-        assertEquals(keyPair.public, pub2)
-    }
-
-    @Test
-    fun keyGeneration() {
-        val gen: KeyPairGenerator = KeyPairGenerator.getInstance("RSA")
-        gen.initialize(2048)
-        val pair = gen.genKeyPair()
-        println(pair.private)
-        assertTrue(pair.private is RSAPrivateCrtKey)
+        assertEquals(public, pub2)
     }
 
     @Test
     fun testCryptoUtilBasics() {
-        val keyPair = CryptoUtil.generateRsaKeyPair(1024)
-        val encodedPriv = keyPair.private.encoded
-        val encodedPub = keyPair.public.encoded
-        val otherKeyPair =
-            
CryptoUtil.RsaCrtKeyPair(CryptoUtil.loadRsaPrivateKey(encodedPriv), 
CryptoUtil.loadRsaPublicKey(encodedPub))
-        assertEquals(keyPair.private, otherKeyPair.private)
-        assertEquals(keyPair.public, otherKeyPair.public)
+        val (private, public) = CryptoUtil.genRSAPair(1024)
+        assertEquals(private, CryptoUtil.loadRSAPrivate(private.encoded))
+        assertEquals(public, CryptoUtil.loadRSAPublic(public.encoded))
     }
 
     @Test
     fun testEbicsE002() {
         val data = "Hello, World!".toByteArray()
-        val keyPair = CryptoUtil.generateRsaKeyPair(1024)
-        val enc = CryptoUtil.encryptEbicsE002(data.inputStream(), 
keyPair.public)
-        val dec = CryptoUtil.decryptEbicsE002(enc, keyPair.private)
+        val (private, public) = CryptoUtil.genRSAPair(1024)
+        val (txKey, encryptedKey) = CryptoUtil.genEbicsE002Key(public)
+        val enc = CryptoUtil.encryptEbicsE002(txKey, data.inputStream())
+        val txKey2 = CryptoUtil.decryptEbicsE002Key(private, encryptedKey)
+        val dec = CryptoUtil.decryptEbicsE002(txKey2, enc).readBytes()
         assertTrue(data.contentEquals(dec))
     }
 
     @Test
     fun testEbicsA006() {
-        val keyPair = CryptoUtil.generateRsaKeyPair(1024)
+        val (private, public) = CryptoUtil.genRSAPair(1024)
         val data = "Hello, World".toByteArray(Charsets.UTF_8)
-        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.inputStream(), 
keyPair.public)
-
-        /* encrypt and decrypt private key */
-        val encPriv = CryptoUtil.encryptKey(keyPair.private.encoded, "secret")
-        val plainPriv = 
CryptoUtil.decryptKey(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)
-        )
+        val sig = CryptoUtil.signEbicsA006(data, private)
+        assertTrue(CryptoUtil.verifyEbicsA006(sig, data, public))
     }
 
     @Test
@@ -129,7 +96,7 @@ class CryptoUtilTest {
 
         val expectedHash = expectedHashStr.replace(" ", "").replace("\n", 
"").toByteArray(Charsets.UTF_8)
 
-        val pub = 
CryptoUtil.loadRsaPublicKeyFromComponents(moduloStr.decodeUpHex(), 
exponentStr.decodeUpHex())
+        val pub = CryptoUtil.RSAPublicFromComponents(moduloStr.decodeUpHex(), 
exponentStr.decodeUpHex())
 
         println("echoed pub exp: ${pub.publicExponent.encodeHex()}")
         println("echoed pub mod: ${pub.modulus.encodeHex()}")
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
index 298d84b4..f25934cd 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -46,7 +46,7 @@ data class FetchContext(
     /**
      * Config handle.
      */
-    val cfg: EbicsSetupConfig,
+    val cfg: NexusConfig,
     /**
      * HTTP client handle to reach the bank
      */
@@ -359,7 +359,7 @@ class EbicsFetch: CliktCommand("Fetches EBICS files") {
      * FIXME: reduce code duplication with the submit subcommand.
      */
     override fun run() = cliCmd(logger, common.log) {
-        val cfg: EbicsSetupConfig = extractEbicsConfig(common.config)
+        val cfg = extractEbicsConfig(common.config)
         val dbCfg = cfg.config.dbConfig()
 
         Database(dbCfg.dbConnStr).use { db ->
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
index f9c83eba..dd13eb30 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -27,6 +27,7 @@ import io.ktor.client.plugins.*
 import tech.libeufin.common.*
 import tech.libeufin.common.crypto.*
 import tech.libeufin.nexus.ebics.*
+import tech.libeufin.nexus.ebics.EbicsKeyMng.Order.*
 import java.nio.file.*
 import java.time.Instant
 import kotlin.io.path.*
@@ -50,16 +51,6 @@ private fun loadOrGenerateClientKeys(path: Path): 
ClientPrivateKeysFile {
     return newKeys
 }
 
-/**
- * Expresses the type of keying message that the user wants
- * to send to the bank.
- */
-enum class KeysOrderType {
-    INI,
-    HIA,
-    HPB
-}
-
 /**
  * @return the "this" string with a space every two characters.
  */
@@ -104,39 +95,34 @@ private fun askUserToAcceptKeys(bankKeys: 
BankPublicKeysFile): Boolean {
  *        the --auto-accept-key CLI flag.
  */
 suspend fun doKeysRequestAndUpdateState(
-    cfg: EbicsSetupConfig,
+    cfg: NexusConfig,
     privs: ClientPrivateKeysFile,
     client: HttpClient,
-    orderType: KeysOrderType
+    order: EbicsKeyMng.Order
 ) {
-    logger.info("Doing key request ${orderType.name}")
-    val impl = EbicsKeyMng(cfg, privs, true)
-    val req = when(orderType) {
-        KeysOrderType.INI -> impl.INI()
-        KeysOrderType.HIA -> impl.HIA()
-        KeysOrderType.HPB -> impl.HPB()
-    }
-    val xml = client.postToBank(cfg.hostBaseUrl, req, "$orderType")
+    logger.info("Doing key request $order")
+    val req = EbicsKeyMng(cfg, privs, true).request(order)
+    val xml = client.postToBank(cfg.hostBaseUrl, req, order.name)
     val resp = EbicsKeyMng.parseResponse(xml, privs.encryption_private_key)
     
-    when (orderType) {
-        KeysOrderType.INI, KeysOrderType.HIA -> {
+    when (order) {
+        INI, HIA -> {
             if (resp.technicalCode == 
EbicsReturnCode.EBICS_INVALID_USER_OR_USER_STATE) {
-                throw Exception("$orderType status code ${resp.technicalCode}: 
either your IDs are incorrect, or you already have keys registered with this 
bank")
+                throw Exception("$order status code ${resp.technicalCode}: 
either your IDs are incorrect, or you already have keys registered with this 
bank")
             }
         }
-        KeysOrderType.HPB -> {
+        HPB -> {
             if (resp.technicalCode == 
EbicsReturnCode.EBICS_AUTHENTICATION_FAILED) {
-                throw Exception("$orderType status code ${resp.technicalCode}: 
could not download bank keys, send client keys (and/or related PDF document 
with --generate-registration-pdf) to the bank")
+                throw Exception("$order status code ${resp.technicalCode}: 
could not download bank keys, send client keys (and/or related PDF document 
with --generate-registration-pdf) to the bank")
             }
         }
     }
     
-    val orderData = resp.okOrFail("${orderType.name}")
-    when (orderType) {
-        KeysOrderType.INI -> privs.submitted_ini = true
-        KeysOrderType.HIA -> privs.submitted_hia = true
-        KeysOrderType.HPB -> {
+    val orderData = resp.okOrFail(order.name)
+    when (order) {
+        INI -> privs.submitted_ini = true
+        HIA -> privs.submitted_hia = true
+        HPB -> {
             val orderData = requireNotNull(orderData) {
                 "HPB: missing order data"
             }
@@ -147,17 +133,17 @@ suspend fun doKeysRequestAndUpdateState(
                 accepted = false
             )
             try {
-                persistBankKeys(bankKeys, cfg.bankPublicKeysFilename)
+                persistBankKeys(bankKeys, cfg.bankPublicKeysPath)
             } catch (e: Exception) {
-                throw Exception("Could not update the ${orderType.name} state 
on disk", e)
+                throw Exception("Could not update the $order state on disk", e)
             }
         }
     }
-    if (orderType != KeysOrderType.HPB) {
+    if (order != HPB) {
         try {
-            persistClientKeys(privs, cfg.clientPrivateKeysFilename)
+            persistClientKeys(privs, cfg.clientPrivateKeysPath)
         } catch (e: Exception) {
-            throw Exception("Could not update the ${orderType.name} state on 
disk", e)
+            throw Exception("Could not update the $order state on disk", e)
         }
     }
     
@@ -169,9 +155,9 @@ suspend fun doKeysRequestAndUpdateState(
  * @param configFile location of the configuration entry point.
  * @return internal representation of the configuration.
  */
-fun extractEbicsConfig(configFile: Path?): EbicsSetupConfig {
+fun extractEbicsConfig(configFile: Path?): NexusConfig {
     val config = loadConfig(configFile)
-    return EbicsSetupConfig(config)
+    return NexusConfig(config)
 }
 
 /**
@@ -181,7 +167,7 @@ fun extractEbicsConfig(configFile: Path?): EbicsSetupConfig 
{
  * @param privs client private keys.
  * @param cfg configuration handle.
  */
-private fun makePdf(privs: ClientPrivateKeysFile, cfg: EbicsSetupConfig) {
+private fun makePdf(privs: ClientPrivateKeysFile, cfg: NexusConfig) {
     val pdf = generateKeysPdf(privs, cfg)
     val path = 
Path("/tmp/libeufin-nexus-keys-${Instant.now().epochSecond}.pdf")
     try {
@@ -213,8 +199,8 @@ class EbicsSetup: CliktCommand("Set up the EBICS 
subscriber") {
     override fun run() = cliCmd(logger, common.log) {
         val cfg = extractEbicsConfig(common.config)
         // Config is sane.  Go (maybe) making the private keys.
-        val clientKeys = 
loadOrGenerateClientKeys(cfg.clientPrivateKeysFilename)
-        val httpClient =  HttpClient {
+        val clientKeys = loadOrGenerateClientKeys(cfg.clientPrivateKeysPath)
+        val client =  HttpClient {
             install(HttpTimeout) {
                 // It can take a lot of time for the bank to generate documents
                 socketTimeoutMillis = 5 * 60 * 1000
@@ -223,23 +209,18 @@ class EbicsSetup: CliktCommand("Set up the EBICS 
subscriber") {
         // Privs exist.  Upload their pubs
         val keysNotSub = !clientKeys.submitted_ini
         if ((!clientKeys.submitted_ini) || forceKeysResubmission)
-            doKeysRequestAndUpdateState(cfg, clientKeys, httpClient, 
KeysOrderType.INI)
+            doKeysRequestAndUpdateState(cfg, clientKeys, client, INI)
         // Eject PDF if the keys were submitted for the first time, or the 
user asked.
         if (keysNotSub || generateRegistrationPdf) makePdf(clientKeys, cfg)
         if ((!clientKeys.submitted_hia) || forceKeysResubmission)
-            doKeysRequestAndUpdateState(cfg, clientKeys, httpClient, 
KeysOrderType.HIA)
+            doKeysRequestAndUpdateState(cfg, clientKeys, client, HIA)
         
         // Checking if the bank keys exist on disk.
-        var bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
+        var bankKeys = loadBankKeys(cfg.bankPublicKeysPath)
         if (bankKeys == null) {
-            doKeysRequestAndUpdateState(
-                cfg,
-                clientKeys,
-                httpClient,
-                KeysOrderType.HPB
-            )
-            logger.info("Bank keys stored at ${cfg.bankPublicKeysFilename}")
-            bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)!!
+            doKeysRequestAndUpdateState(cfg, clientKeys, client, HPB)
+            logger.info("Bank keys stored at ${cfg.bankPublicKeysPath}")
+            bankKeys = loadBankKeys(cfg.bankPublicKeysPath)!!
         }
 
         if (!bankKeys.accepted) {
@@ -251,7 +232,7 @@ class EbicsSetup: CliktCommand("Set up the EBICS 
subscriber") {
                 throw Exception("Cannot successfully finish the setup without 
accepting the bank keys")
             }
             try {
-                persistBankKeys(bankKeys, cfg.bankPublicKeysFilename)
+                persistBankKeys(bankKeys, cfg.bankPublicKeysPath)
             } catch (e: Exception) {
                 throw Exception("Could not set bank keys as accepted on disk", 
e)
             }
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
index cf206380..34d57969 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -42,7 +42,7 @@ data class SubmissionContext(
     /**
      * Configuration handle.
      */
-    val cfg: EbicsSetupConfig,
+    val cfg: NexusConfig,
     /**
      * Subscriber EBICS private keys.
      */
@@ -83,7 +83,7 @@ private suspend fun submitInitiatedPayment(
         initiationTimestamp = payment.initiationTime,
         amount = payment.amount,
         creditAccount = creditAccount,
-        debitAccount = ctx.cfg.myIbanAccount,
+        debitAccount = ctx.cfg.account,
         wireTransferSubject = payment.wireTransferSubject
     )
     ctx.fileLogger.logSubmit(xml)
@@ -146,7 +146,7 @@ class EbicsSubmit : CliktCommand("Submits any initiated 
payment found in the dat
      * FIXME: reduce code duplication with the fetch subcommand.
      */
     override fun run() = cliCmd(logger, common.log) {
-        val cfg: EbicsSetupConfig = extractEbicsConfig(common.config)
+        val cfg = extractEbicsConfig(common.config)
         val dbCfg = cfg.config.dbConfig()
         val (clientKeys, bankKeys) = expectFullKeys(cfg)
         val ctx = SubmissionContext(
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
index fd91cfd4..172cffa2 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
@@ -58,7 +58,7 @@ object RSAPublicKeySerializer : KSerializer<RSAPublicKey> {
     override fun deserialize(decoder: Decoder): RSAPublicKey {
         val fieldValue = decoder.decodeString()
         val bytes = Base32Crockford.decode(fieldValue)
-        return CryptoUtil.loadRsaPublicKey(bytes)
+        return CryptoUtil.loadRSAPublic(bytes)
     }
 }
 
@@ -76,7 +76,7 @@ object RSAPrivateCrtKeySerializer : 
KSerializer<RSAPrivateCrtKey> {
     override fun deserialize(decoder: Decoder): RSAPrivateCrtKey {
         val fieldValue = decoder.decodeString()
         val bytes = Base32Crockford.decode(fieldValue)
-        return CryptoUtil.loadRsaPrivateKey(bytes)
+        return CryptoUtil.loadRSAPrivate(bytes)
     }
 }
 
@@ -111,9 +111,9 @@ data class BankPublicKeysFile(
  */
 fun generateNewKeys(): ClientPrivateKeysFile =
     ClientPrivateKeysFile(
-        authentication_private_key = 
CryptoUtil.generateRsaKeyPair(2048).private,
-        encryption_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
-        signature_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
+        authentication_private_key = CryptoUtil.genRSAPrivate(2048),
+        encryption_private_key = CryptoUtil.genRSAPrivate(2048),
+        signature_private_key = CryptoUtil.genRSAPrivate(2048),
         submitted_hia = false,
         submitted_ini = false
     )
@@ -202,18 +202,16 @@ fun loadClientKeys(location: Path): 
ClientPrivateKeysFile? = loadJsonFile(locati
  * @param cfg configuration handle.
  * @return both client and bank keys
  */
-fun expectFullKeys(
-    cfg: EbicsSetupConfig
-): Pair<ClientPrivateKeysFile, BankPublicKeysFile> {
-    val clientKeys = loadClientKeys(cfg.clientPrivateKeysFilename)
+fun expectFullKeys(cfg: NexusConfig): Pair<ClientPrivateKeysFile, 
BankPublicKeysFile> {
+    val clientKeys = loadClientKeys(cfg.clientPrivateKeysPath)
     if (clientKeys == null) {
-        throw Exception("Missing client private keys file at 
'${cfg.clientPrivateKeysFilename}', run 'libeufin-nexus ebics-setup' first")
+        throw Exception("Missing client private keys file at 
'${cfg.clientPrivateKeysPath}', run 'libeufin-nexus ebics-setup' first")
     } else if (!clientKeys.submitted_ini || !clientKeys.submitted_hia) {
         throw Exception("Unsubmitted client private keys, run 'libeufin-nexus 
ebics-setup' first")
     }
-    val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
+    val bankKeys = loadBankKeys(cfg.bankPublicKeysPath)
     if (bankKeys == null) {
-        throw Exception("Missing bank public keys at 
'${cfg.bankPublicKeysFilename}', run 'libeufin-nexus ebics-setup' first")
+        throw Exception("Missing bank public keys at 
'${cfg.bankPublicKeysPath}', run 'libeufin-nexus ebics-setup' first")
     } else if (!bankKeys.accepted) {
         throw Exception("Unaccepted bank public keys, run 'libeufin-nexus 
ebics-setup' until accepting the bank keys")
     }
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index b9582976..4cc67eec 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -61,68 +61,37 @@ fun Instant.fmtDate(): String =
 fun Instant.fmtDateTime(): String =
     
DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.of("UTC")).format(this)
 
-/**
- * Keeps all the options of the ebics-setup subcommand.  The
- * caller has to handle TalerConfigError if values are missing.
- * If even one of the fields could not be instantiated, then
- * throws TalerConfigError.
- */
-class EbicsSetupConfig(val config: TalerConfig) {
-    // abstracts the section name.
-    private val ebicsSetupRequireString = { option: String ->
-        config.requireString("nexus-ebics", option)
-    }
-    private val ebicsSetupRequirePath = { option: String ->
-        config.requirePath("nexus-ebics", option)
-    }
-    // debug utility to inspect what was loaded.
-    fun _dump() {
-        this.javaClass.declaredFields.forEach {
-            println("cfg obj: ${it.name} -> ${it.get(this)}")
-        }
-    }
-    /**
-     * The bank's currency.
-     */
-    val currency = ebicsSetupRequireString("currency")
-    /**
-     * The bank base URL.
-     */
-    val hostBaseUrl = ebicsSetupRequireString("host_base_url")
-    /**
-     * The bank EBICS host ID.
-     */
-    val ebicsHostId = ebicsSetupRequireString("host_id")
-    /**
-     * EBICS user ID.
-     */
-    val ebicsUserId = ebicsSetupRequireString("user_id")
-    /**
-     * EBICS partner ID.
-     */
-    val ebicsPartnerId = ebicsSetupRequireString("partner_id")
-    /**
-     * Bank account metadata.
-     */
-    val myIbanAccount = IbanAccountMetadata(
-        iban = ebicsSetupRequireString("iban"),
-        bic = ebicsSetupRequireString("bic"),
-        name = ebicsSetupRequireString("name")
+/** Configuration for libeufin-nexus */
+class NexusConfig(val config: TalerConfig) {
+    private fun requireString(option: String): String = 
config.requireString("nexus-ebics", option)
+    private fun requirePath(option: String): Path = 
config.requirePath("nexus-ebics", option)
+
+    /** The bank's currency */
+    val currency = requireString("currency")
+    /** The bank base URL */
+    val hostBaseUrl = requireString("host_base_url")
+    /** The bank EBICS host ID */
+    val ebicsHostId = requireString("host_id")
+    /** EBICS user ID */
+    val ebicsUserId = requireString("user_id")
+    /** EBICS partner ID */
+    val ebicsPartnerId = requireString("partner_id")
+    /** Bank account metadata */
+    val account = IbanAccountMetadata(
+        iban = requireString("iban"),
+        bic = requireString("bic"),
+        name = requireString("name")
     )
-    /**
-     * Filename where we store the bank public keys.
-     */
-    val bankPublicKeysFilename = ebicsSetupRequirePath("bank_public_keys_file")
-    /**
-     * Filename where we store our private keys.
-     */
-    val clientPrivateKeysFilename = 
ebicsSetupRequirePath("client_private_keys_file")
+    /** Path where we store the bank public keys */
+    val bankPublicKeysPath = requirePath("bank_public_keys_file")
+    /** Path where we store our private keys */
+    val clientPrivateKeysPath = requirePath("client_private_keys_file")
     /**
      * A name that identifies the EBICS and ISO20022 flavour
      * that Nexus should honor in the communication with the
      * bank.
      */
-    val bankDialect: String = ebicsSetupRequireString("bank_dialect").run {
+    val bankDialect: String = requireString("bank_dialect").run {
         if (this != "postfinance") throw Exception("Only 'postfinance' dialect 
is supported.")
         return@run this
     }
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt
index a485477c..71937a42 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt
@@ -37,7 +37,7 @@ import tech.libeufin.common.crypto.*
  */
 fun generateKeysPdf(
     clientKeys: ClientPrivateKeysFile,
-    cfg: EbicsSetupConfig
+    cfg: NexusConfig
 ): ByteArray {
     val po = ByteArrayOutputStream()
     val pdfWriter = PdfWriter(po)
@@ -73,7 +73,7 @@ fun generateKeysPdf(
     }
 
     fun writeKey(doc: Document, priv: RSAPrivateCrtKey) {
-        val pub = CryptoUtil.getRsaPublicFromPrivate(priv)
+        val pub = CryptoUtil.RSAPublicFromPrivate(priv)
         val hash = CryptoUtil.getEbicsPublicKeyHash(pub)
         
doc.add(Paragraph("Exponent:\n${formatHex(pub.publicExponent.toByteArray())}"))
         doc.add(Paragraph("Modulus:\n${formatHex(pub.modulus.toByteArray())}"))
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt
index 9e5ee4e2..1c4294e3 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt
@@ -40,7 +40,7 @@ fun Instant.xmlDateTime(): String =
 
 /** EBICS protocol for business transactions */
 class EbicsBTS(
-    val cfg: EbicsSetupConfig, 
+    val cfg: NexusConfig, 
     val bankKeys: BankPublicKeysFile,
     val clientKeys: ClientPrivateKeysFile,
     val order: EbicsOrder
@@ -217,7 +217,7 @@ class EbicsBTS(
                     }
                     el("SignatureData") {
                         attr("authenticate", "true")
-                        
text(uploadData.userSignatureDataEncrypted.encodeBase64())
+                        text(uploadData.userSignatureDataEncrypted)
                     }
                     el("DataDigest") {
                         attr("SignatureVersion", "A006")
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
index 30bcd8de..f87c55b3 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
@@ -80,15 +80,17 @@ fun decryptAndDecompressPayload(
     clientEncryptionKey: RSAPrivateCrtKey,
     encryptionInfo: DataEncryptionInfo,
     chunks: List<ByteArray>
-): InputStream =
-    SequenceInputStream(Collections.enumeration(chunks.map { it.inputStream() 
})) // Aggregate
+): InputStream {
+    val transactionKey = CryptoUtil.decryptEbicsE002Key(clientEncryptionKey, 
encryptionInfo.transactionKey)
+    return SequenceInputStream(Collections.enumeration(chunks.map { 
it.inputStream() })) // Aggregate
         .run {
             CryptoUtil.decryptEbicsE002(
-                encryptionInfo.transactionKey,
-                this,
-                clientEncryptionKey
+                transactionKey,
+                this
             )
         }.inflate()
+}
+    
 
 sealed class EbicsError(msg: String, cause: Throwable? = null): Exception(msg, 
cause) {
     /** Http and network errors */
@@ -162,7 +164,7 @@ suspend fun EbicsBTS.postBTS(
  */
 suspend fun ebicsDownload(
     client: HttpClient,
-    cfg: EbicsSetupConfig,
+    cfg: NexusConfig,
     clientKeys: ClientPrivateKeysFile,
     bankKeys: BankPublicKeysFile,
     order: EbicsOrder,
@@ -267,43 +269,43 @@ suspend fun ebicsDownload(
  * @return [PreparedUploadData]
  */
 fun prepareUploadPayload(
-    cfg: EbicsSetupConfig,
+    cfg: NexusConfig,
     clientKeys: ClientPrivateKeysFile,
     bankKeys: BankPublicKeysFile,
     payload: ByteArray,
 ): PreparedUploadData {
+    val payloadDigest = CryptoUtil.digestEbicsOrderA006(payload)
     val innerSignedEbicsXml = XmlBuilder.toBytes("UserSignatureData") {
         attr("xmlns", "http://www.ebics.org/S002";)
         el("OrderSignatureData") {
             el("SignatureVersion", "A006")
             el("SignatureValue", CryptoUtil.signEbicsA006(
-                CryptoUtil.digestEbicsOrderA006(payload),
+                payloadDigest,
                 clientKeys.signature_private_key,
             ).encodeBase64())
             el("PartnerID", cfg.ebicsPartnerId)
             el("UserID", cfg.ebicsUserId)
         }
     }
-    val encryptionResult = CryptoUtil.encryptEbicsE002(
-        innerSignedEbicsXml.inputStream().deflate(),
-        bankKeys.bank_encryption_public_key
-    )
-    // Then only E002 symmetric (with ephemeral key) encrypt.
-    val compressedInnerPayload = payload.inputStream().deflate()
-    // TODO stream
-    val encryptedPayload = CryptoUtil.encryptEbicsE002withTransactionKey(
-        compressedInnerPayload,
-        bankKeys.bank_encryption_public_key,
-        encryptionResult.plainTransactionKey
-    )
-    val segment = encryptedPayload.encryptedData.encodeBase64()
-    // Split 1MB segment when we have payloads that big
+    // Generate ephemeral transaction key
+    val (transactionKey, encryptedTransactionKey) = 
CryptoUtil.genEbicsE002Key(bankKeys.bank_encryption_public_key)
+    // Compress and encrypt order signature
+    val orderSignature = CryptoUtil.encryptEbicsE002(
+        transactionKey,
+        innerSignedEbicsXml.inputStream().deflate()
+    ).encodeBase64()
+    // Compress and encrypt payload
+    val segment = CryptoUtil.encryptEbicsE002(
+        transactionKey,
+        payload.inputStream().deflate()
+    ).encodeBase64()
+    // TODO split 1MB segment when we have payloads that big
 
     return PreparedUploadData(
-        encryptionResult.encryptedTransactionKey, // ephemeral key
-        encryptionResult.encryptedData, // bank-pub-encrypted A006 signature.
-        CryptoUtil.digestEbicsOrderA006(payload), // used by EBICS 3
-        listOf(segment) // actual payload E002 encrypted.
+        encryptedTransactionKey,
+        orderSignature,
+        payloadDigest,
+        listOf(segment)
     )
 }
 
@@ -321,7 +323,7 @@ fun prepareUploadPayload(
  */
 suspend fun doEbicsUpload(
     client: HttpClient,
-    cfg: EbicsSetupConfig,
+    cfg: NexusConfig,
     clientKeys: ClientPrivateKeysFile,
     bankKeys: BankPublicKeysFile,
     order: EbicsOrder,
@@ -361,7 +363,7 @@ fun getNonce(size: Int): ByteArray {
 
 class PreparedUploadData(
     val transactionKey: ByteArray,
-    val userSignatureDataEncrypted: ByteArray,
+    val userSignatureDataEncrypted: String,
     val dataDigest: ByteArray,
     val segments: List<String>
 )
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsConstants.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsConstants.kt
index fc217c2a..8841866f 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsConstants.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsConstants.kt
@@ -46,9 +46,16 @@ enum class EbicsReturnCode(val code: String) {
     EBICS_TX_SEGMENT_NUMBER_EXCEEDED("091104"), 
     EBICS_INVALID_REQUEST_CONTENT("091113"),
     EBICS_PROCESSING_ERROR("091116"),
-    
 
     // Key-Management errors
+    EBICS_KEYMGMT_UNSUPPORTED_VERSION_SIGNATURE("091201"),
+    EBICS_KEYMGMT_UNSUPPORTED_VERSION_AUTHENTICATION("091202"),
+    EBICS_KEYMGMT_UNSUPPORTED_VERSION_ENCRYPTION("091203"),
+    EBICS_KEYMGMT_KEYLENGTH_ERROR_SIGNATURE("091204"),
+    EBICS_KEYMGMT_KEYLENGTH_ERROR_AUTHENTICATION("091205"),
+    EBICS_KEYMGMT_KEYLENGTH_ERROR_ENCRYPTION("091206"),
+    EBICS_X509_CERTIFICATE_EXPIRED("091208"),
+    EBICS_X509_CERTIFICATE_NOT_VALID_YET("091209"),
     EBICS_X509_WRONG_KEY_USAGE("091210"),
     EBICS_X509_WRONG_ALGORITHM("091211"),
     EBICS_X509_INVALID_THUMBPRINT("091212"),
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt
index 48d10d18..37e26e1c 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt
@@ -25,60 +25,53 @@ import tech.libeufin.common.*
 import tech.libeufin.nexus.*
 import tech.libeufin.nexus.BankPublicKeysFile
 import tech.libeufin.nexus.ClientPrivateKeysFile
-import tech.libeufin.nexus.EbicsSetupConfig
 import java.io.InputStream
 import java.time.Instant
 import java.time.ZoneId
 import java.util.*
 import javax.xml.datatype.DatatypeFactory
 import java.security.interfaces.*
+import tech.libeufin.nexus.ebics.EbicsKeyMng.Order.*
 
 /** EBICS protocol for key management */
 class EbicsKeyMng(
-    private val cfg: EbicsSetupConfig,
+    private val cfg: NexusConfig,
     private val clientKeys: ClientPrivateKeysFile,
     private val ebics3: Boolean
 ) {
     private val schema = if (ebics3) "H005" else "H004"
-    fun INI(): ByteArray {
-        val data = XMLOrderData(cfg, "SignaturePubKeyOrderData", 
"http://www.ebics.org/S00${if (ebics3) 2 else 1}") {
-            el("SignaturePubKeyInfo") {
-                RSAKeyXml(clientKeys.signature_private_key)
-                el("SignatureVersion", "A006")
-            }
-        }
-        return request("ebicsUnsecuredRequest", "INI", "0200", data)
+
+    enum class Order {
+        INI,
+        HIA,
+        HPB
     }
 
-    fun HIA(): ByteArray {
-        val data = XMLOrderData(cfg, "HIARequestOrderData", 
"urn:org:ebics:$schema") {
-            el("AuthenticationPubKeyInfo") {
-                RSAKeyXml(clientKeys.authentication_private_key)
-                el("AuthenticationVersion", "X002")
+    fun request(order: Order): ByteArray {
+        val (name, securityMedium, orderAttribute) = when (order) {
+            INI, HIA -> Triple("ebicsUnsecuredRequest", "0200", "DZNNN")
+            HPB -> Triple("ebicsNoPubKeyDigestsRequest", "0000", "DZHNN")
+        }
+        val data = when (order) {
+            INI -> XMLOrderData("SignaturePubKeyOrderData", 
"http://www.ebics.org/S00${if (ebics3) 2 else 1}") {
+                el("SignaturePubKeyInfo") {
+                    RSAKeyXml(clientKeys.signature_private_key)
+                    el("SignatureVersion", "A006")
+                }
             }
-            el("EncryptionPubKeyInfo") {
-                RSAKeyXml(clientKeys.encryption_private_key)
-                el("EncryptionVersion", "E002")
+            HIA -> XMLOrderData("HIARequestOrderData", 
"urn:org:ebics:$schema") {
+                el("AuthenticationPubKeyInfo") {
+                    RSAKeyXml(clientKeys.authentication_private_key)
+                    el("AuthenticationVersion", "X002")
+                }
+                el("EncryptionPubKeyInfo") {
+                    RSAKeyXml(clientKeys.encryption_private_key)
+                    el("EncryptionVersion", "E002")
+                }
             }
+            HPB -> null
         }
-        return request("ebicsUnsecuredRequest", "HIA", "0200", data)
-    }
-
-    fun HPB(): ByteArray {
-        val nonce = getNonce(128)
-        return request("ebicsNoPubKeyDigestsRequest", "HPB", "0000", timestamp 
= Instant.now(), sign = true)
-    }
-
-    /* ----- Helpers ----- */
-
-    private fun request(
-        name: String,
-        order: String,
-        securityMedium: String,
-        data: String? = null,
-        timestamp: Instant? = null,
-        sign: Boolean = false
-    ): ByteArray {
+        val sign = order == HPB
         val doc = XmlBuilder.toDom(name, "urn:org:ebics:$schema") {
             attr("http://www.w3.org/2000/xmlns/";, "xmlns", 
"urn:org:ebics:$schema")
             attr("http://www.w3.org/2000/xmlns/";, "xmlns:ds", 
"http://www.w3.org/2000/09/xmldsig#";)
@@ -88,18 +81,18 @@ class EbicsKeyMng(
                 attr("authenticate", "true")
                 el("static") {
                     el("HostID", cfg.ebicsHostId)
-                    if (timestamp != null) {
+                    if (order == HPB) {
                         el("Nonce", getNonce(128).encodeUpHex())
-                        el("Timestamp", timestamp.xmlDateTime())
+                        el("Timestamp", Instant.now().xmlDateTime())
                     }
                     el("PartnerID", cfg.ebicsPartnerId)
                     el("UserID", cfg.ebicsUserId)
                     el("OrderDetails") {
                         if (ebics3) {
-                            el("AdminOrderType", order)
+                            el("AdminOrderType", order.name)
                         } else {
-                            el("OrderType", order)
-                            el("OrderAttribute", if (order == "HPB") "DZHNN" 
else "DZNNN")
+                            el("OrderType", order.name)
+                            el("OrderAttribute", orderAttribute)
                         }
                     }
                     el("SecurityMedium", securityMedium)
@@ -117,7 +110,7 @@ class EbicsKeyMng(
 
     private fun XmlBuilder.RSAKeyXml(key: RSAPrivateCrtKey) {
         if (ebics3) {
-            val cert = CryptoUtil.certificateFromPrivate(key)
+            val cert = CryptoUtil.X509CertificateFromRSAPrivate(key, 
cfg.account.name)
             el("ds:X509Data") {
                 el("ds:X509Certificate", cert.encoded.encodeBase64())
             }
@@ -131,7 +124,7 @@ class EbicsKeyMng(
         }
     }
     
-    private fun XMLOrderData(cfg: EbicsSetupConfig, name: String, schema: 
String, build: XmlBuilder.() -> Unit): String {
+    private fun XMLOrderData(name: String, schema: String, build: 
XmlBuilder.() -> Unit): String {
         return XmlBuilder.toBytes(name) {
             attr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#";)
             attr("xmlns", schema)
@@ -181,10 +174,10 @@ class EbicsKeyMng(
             fun XmlDestructor.rsaPubKey(): RSAPublicKey {
                 val cert = 
opt("X509Data")?.one("X509Certificate")?.text()?.decodeBase64()
                 return if (cert != null) {
-                    CryptoUtil.loadRsaPublicKeyFromCertificate(cert)
+                    CryptoUtil.RSAPublicFromCertificate(cert)
                 } else {
                     one("PubKeyValue").one("RSAKeyValue") {
-                        CryptoUtil.loadRsaPublicKeyFromComponents(
+                        CryptoUtil.RSAPublicFromComponents(
                             one("Modulus").text().decodeBase64(),
                             one("Exponent").text().decodeBase64(),
                         )
diff --git a/nexus/src/test/kotlin/CliTest.kt b/nexus/src/test/kotlin/CliTest.kt
index 7e03053d..19bc0853 100644
--- a/nexus/src/test/kotlin/CliTest.kt
+++ b/nexus/src/test/kotlin/CliTest.kt
@@ -103,8 +103,8 @@ class CliTest {
         }
         // Unfinished bank
         persistBankKeys(BankPublicKeysFile(
-            bank_authentication_public_key = 
CryptoUtil.generateRsaKeyPair(2048).public,
-            bank_encryption_public_key = 
CryptoUtil.generateRsaKeyPair(2048).public,
+            bank_authentication_public_key = CryptoUtil.genRSAPublic(2048),
+            bank_encryption_public_key = CryptoUtil.genRSAPublic(2048),
             accepted = false
         ), bankKeysPath)
         for (cmd in cmds) {
diff --git a/nexus/src/test/kotlin/Keys.kt b/nexus/src/test/kotlin/Keys.kt
index 826ec77e..36dd6487 100644
--- a/nexus/src/test/kotlin/Keys.kt
+++ b/nexus/src/test/kotlin/Keys.kt
@@ -43,8 +43,8 @@ class PublicKeys {
         // artificially creating the keys.
         val fileContent = BankPublicKeysFile(
             accepted = true,
-            bank_authentication_public_key = 
CryptoUtil.generateRsaKeyPair(2028).public,
-            bank_encryption_public_key = 
CryptoUtil.generateRsaKeyPair(2028).public
+            bank_authentication_public_key = CryptoUtil.genRSAPublic(2028),
+            bank_encryption_public_key = CryptoUtil.genRSAPublic(2028)
         )
         // storing them on disk.
         persistBankKeys(fileContent, Path("/tmp/nexus-tests-bank-keys.json"))
diff --git a/nexus/src/test/kotlin/MySerializers.kt 
b/nexus/src/test/kotlin/MySerializers.kt
index 98707948..1f2d8969 100644
--- a/nexus/src/test/kotlin/MySerializers.kt
+++ b/nexus/src/test/kotlin/MySerializers.kt
@@ -28,9 +28,9 @@ class MySerializers {
     // Testing deserialization of RSA private keys.
     @Test
     fun rsaPrivDeserialization() {
-        val s = 
Base32Crockford.encode(CryptoUtil.generateRsaKeyPair(2048).private.encoded)
-        val a = 
Base32Crockford.encode(CryptoUtil.generateRsaKeyPair(2048).private.encoded)
-        val e = 
Base32Crockford.encode(CryptoUtil.generateRsaKeyPair(2048).private.encoded)
+        val s = Base32Crockford.encode(CryptoUtil.genRSAPrivate(2048).encoded)
+        val a = Base32Crockford.encode(CryptoUtil.genRSAPrivate(2048).encoded)
+        val e = Base32Crockford.encode(CryptoUtil.genRSAPrivate(2048).encoded)
         val obj = JSON.decodeFromString<ClientPrivateKeysFile>("""
             {
               "signature_private_key": "$s",
@@ -40,8 +40,8 @@ class MySerializers {
               "submitted_hia": true
             }
         """.trimIndent())
-        assertEquals(obj.signature_private_key, 
CryptoUtil.loadRsaPrivateKey(Base32Crockford.decode(s)))
-        assertEquals(obj.authentication_private_key, 
CryptoUtil.loadRsaPrivateKey(Base32Crockford.decode(a)))
-        assertEquals(obj.encryption_private_key, 
CryptoUtil.loadRsaPrivateKey(Base32Crockford.decode(e)))
+        assertEquals(obj.signature_private_key, 
CryptoUtil.loadRSAPrivate(Base32Crockford.decode(s)))
+        assertEquals(obj.authentication_private_key, 
CryptoUtil.loadRSAPrivate(Base32Crockford.decode(a)))
+        assertEquals(obj.encryption_private_key, 
CryptoUtil.loadRSAPrivate(Base32Crockford.decode(e)))
     }
 }
\ No newline at end of file
diff --git a/nexus/src/test/kotlin/XmlUtilTest.kt 
b/nexus/src/test/kotlin/XmlUtilTest.kt
index 03941f3f..a60e0904 100644
--- a/nexus/src/test/kotlin/XmlUtilTest.kt
+++ b/nexus/src/test/kotlin/XmlUtilTest.kt
@@ -67,7 +67,7 @@ class XmlUtilTest {
         val doc = XMLUtil.parseIntoDom(docText)
         val keyStream = 
classLoader.getResourceAsStream("signature1/public_key.txt")
         val keyBytes = keyStream.decodeBase64().readAllBytes()
-        val key = CryptoUtil.loadRsaPublicKey(keyBytes)
+        val key = CryptoUtil.loadRSAPublic(keyBytes)
         assertTrue(XMLUtil.verifyEbicsDocument(doc, key, "H004"))
     }
 }
\ No newline at end of file
diff --git a/nexus/src/test/kotlin/helpers.kt b/nexus/src/test/kotlin/helpers.kt
index 57a1d2da..61aed19d 100644
--- a/nexus/src/test/kotlin/helpers.kt
+++ b/nexus/src/test/kotlin/helpers.kt
@@ -29,20 +29,20 @@ import kotlin.io.path.Path
 
 fun conf(
     conf: String = "test.conf",
-    lambda: suspend (EbicsSetupConfig) -> Unit
+    lambda: suspend (NexusConfig) -> Unit
 ) = runBlocking {
     val config = NEXUS_CONFIG_SOURCE.fromFile(Path("conf/$conf"))
-    val ctx = EbicsSetupConfig(config)
+    val ctx = NexusConfig(config)
     lambda(ctx) 
 }
 
 fun setup(
     conf: String = "test.conf",
-    lambda: suspend (Database, EbicsSetupConfig) -> Unit
+    lambda: suspend (Database, NexusConfig) -> Unit
 ) = runBlocking {
     val config = NEXUS_CONFIG_SOURCE.fromFile(Path("conf/$conf"))
     val dbCfg = config.dbConfig()
-    val ctx = EbicsSetupConfig(config)
+    val ctx = NexusConfig(config)
     Database(dbCfg.dbConnStr).use { 
         it.conn { conn ->
             resetDatabaseTables(conn, dbCfg, "libeufin-nexus")

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]