summaryrefslogtreecommitdiff
path: root/nexus/src/main/kotlin/tech
diff options
context:
space:
mode:
Diffstat (limited to 'nexus/src/main/kotlin/tech')
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt4
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt22
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt6
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt18
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt81
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt2
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt4
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt58
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt11
9 files changed, 87 insertions, 119 deletions
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 a4870834..dd13eb30 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -95,7 +95,7 @@ private fun askUserToAcceptKeys(bankKeys: BankPublicKeysFile): Boolean {
* the --auto-accept-key CLI flag.
*/
suspend fun doKeysRequestAndUpdateState(
- cfg: EbicsSetupConfig,
+ cfg: NexusConfig,
privs: ClientPrivateKeysFile,
client: HttpClient,
order: EbicsKeyMng.Order
@@ -133,7 +133,7 @@ suspend fun doKeysRequestAndUpdateState(
accepted = false
)
try {
- persistBankKeys(bankKeys, cfg.bankPublicKeysFilename)
+ persistBankKeys(bankKeys, cfg.bankPublicKeysPath)
} catch (e: Exception) {
throw Exception("Could not update the $order state on disk", e)
}
@@ -141,7 +141,7 @@ suspend fun doKeysRequestAndUpdateState(
}
if (order != HPB) {
try {
- persistClientKeys(privs, cfg.clientPrivateKeysFilename)
+ persistClientKeys(privs, cfg.clientPrivateKeysPath)
} catch (e: Exception) {
throw Exception("Could not update the $order state on disk", e)
}
@@ -155,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)
}
/**
@@ -167,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 {
@@ -199,7 +199,7 @@ 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 clientKeys = loadOrGenerateClientKeys(cfg.clientPrivateKeysPath)
val client = HttpClient {
install(HttpTimeout) {
// It can take a lot of time for the bank to generate documents
@@ -216,11 +216,11 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
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, client, HPB)
- logger.info("Bank keys stored at ${cfg.bankPublicKeysFilename}")
- bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)!!
+ logger.info("Bank keys stored at ${cfg.bankPublicKeysPath}")
+ bankKeys = loadBankKeys(cfg.bankPublicKeysPath)!!
}
if (!bankKeys.accepted) {
@@ -232,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 b4acbb75..172cffa2 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
@@ -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 dd595d31..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)
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/EbicsKeyMng.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt
index 9141430a..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,7 +25,6 @@ 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
@@ -36,7 +35,7 @@ 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
) {
@@ -54,13 +53,13 @@ class EbicsKeyMng(
HPB -> Triple("ebicsNoPubKeyDigestsRequest", "0000", "DZHNN")
}
val data = when (order) {
- INI -> XMLOrderData(cfg, "SignaturePubKeyOrderData", "http://www.ebics.org/S00${if (ebics3) 2 else 1}") {
+ INI -> XMLOrderData("SignaturePubKeyOrderData", "http://www.ebics.org/S00${if (ebics3) 2 else 1}") {
el("SignaturePubKeyInfo") {
RSAKeyXml(clientKeys.signature_private_key)
el("SignatureVersion", "A006")
}
}
- HIA -> XMLOrderData(cfg, "HIARequestOrderData", "urn:org:ebics:$schema") {
+ HIA -> XMLOrderData("HIARequestOrderData", "urn:org:ebics:$schema") {
el("AuthenticationPubKeyInfo") {
RSAKeyXml(clientKeys.authentication_private_key)
el("AuthenticationVersion", "X002")
@@ -111,7 +110,7 @@ class EbicsKeyMng(
private fun XmlBuilder.RSAKeyXml(key: RSAPrivateCrtKey) {
if (ebics3) {
- val cert = CryptoUtil.X509CertificateFromRSAPrivate(key, cfg.myIbanAccount.name)
+ val cert = CryptoUtil.X509CertificateFromRSAPrivate(key, cfg.account.name)
el("ds:X509Data") {
el("ds:X509Certificate", cert.encoded.encodeBase64())
}
@@ -125,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)