diff --git a/README.md b/README.md index 5709bed..8e8fb46 100755 --- a/README.md +++ b/README.md @@ -59,6 +59,13 @@ Description: `Send a GET request to this to receive back an invoiceRequest binar Verb: `GET` Response: `Binary invoiceRequest` +### Initial invoice request Encrypted + +Endpoint: `/initial-invoice-request-encrypted` +Description: `Send a GET request to this to receive back an invoiceRequest binary encrypted so that you can test parsing thing` +Verb: `GET` +Response: `Binary invoiceRequest encrypted` + Verb: `POST` Description: `If you want to test your full flow with getting an invoiceRequest object at your correct endpoint use the POST as described and it will send the binary object to that URL.` Params: @@ -91,3 +98,11 @@ Verb: `POST` Params: - payment: `Binary containing payment` Response: `Binary containing paymentAck` + +### Encryption +You can generate EncryptedMessages to test this functionality. Once you start the service you can fetch a set of ECDSA keys to use in your ProtocolMessages. + +Endpoint: `/encryption/keys` +Description: `Send a GET request to this to receive a set of sender/recipient keys to test encrypted messages` +Verb: `GET` +Response: `Set of keys to do encryption` diff --git a/build.gradle.kts b/build.gradle.kts index ec0251e..722695e 100755 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "com.netki" -version = "0.1.0-alpha15" +version = "0.1.0-alpha16" java.sourceCompatibility = JavaVersion.VERSION_1_8 repositories { @@ -18,7 +18,7 @@ repositories { } dependencies { - implementation("com.netki:transactid:0.1.0-alpha15") + implementation("com.netki:transactid:0.1.0-alpha16") implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") diff --git a/src/main/java/com/netki/transactidlibraryjavademo/TransactidLibraryJavaDemoApplication.kt b/src/main/java/com/netki/transactidlibraryjavademo/TransactidLibraryJavaDemoApplication.kt index 6cec35a..b5bedd1 100755 --- a/src/main/java/com/netki/transactidlibraryjavademo/TransactidLibraryJavaDemoApplication.kt +++ b/src/main/java/com/netki/transactidlibraryjavademo/TransactidLibraryJavaDemoApplication.kt @@ -1,10 +1,33 @@ package com.netki.transactidlibraryjavademo +import com.netki.transactidlibraryjavademo.model.EncryptionKeys +import com.netki.transactidlibraryjavademo.util.CryptoModule +import com.netki.transactidlibraryjavademo.util.KeyGenerator.Keys.generateKeyPairECDSA import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean +import java.security.AlgorithmParameters +import java.security.KeyPair +import java.security.KeyPairGenerator +import java.security.SecureRandom +import java.security.spec.ECGenParameterSpec +import java.security.spec.ECParameterSpec @SpringBootApplication -class TransactidLibraryJavaDemoApplication +class TransactidLibraryJavaDemoApplication { + + @Bean + fun getEncryptionKeys(): EncryptionKeys { + val senderKeys = generateKeyPairECDSA() + val recipientKeys = generateKeyPairECDSA() + return EncryptionKeys( + CryptoModule.objectToPrivateKeyPem(senderKeys.private), + CryptoModule.objectToPublicKeyPem(senderKeys.public), + CryptoModule.objectToPrivateKeyPem(recipientKeys.private), + CryptoModule.objectToPublicKeyPem(recipientKeys.public) + ) + } +} fun main(args: Array) { runApplication(*args) diff --git a/src/main/java/com/netki/transactidlibraryjavademo/controller/TransactIdController.kt b/src/main/java/com/netki/transactidlibraryjavademo/controller/TransactIdController.kt index 84d3558..a685bd4 100755 --- a/src/main/java/com/netki/transactidlibraryjavademo/controller/TransactIdController.kt +++ b/src/main/java/com/netki/transactidlibraryjavademo/controller/TransactIdController.kt @@ -1,5 +1,6 @@ package com.netki.transactidlibraryjavademo.controller +import com.netki.transactidlibraryjavademo.model.EncryptionKeys import com.netki.transactidlibraryjavademo.service.TransactIdService import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter @@ -20,6 +21,18 @@ class TransactIdController { @Autowired private lateinit var transactIdService: TransactIdService + @Operation( + summary = "Get Encryption keys", + description = "Get the set of keys for the sender/recipient, this is needed if you want to generate encrypted messages" + ) + @RequestMapping( + method = [RequestMethod.GET], + value = ["/encryption/keys"], + produces = [MediaType.APPLICATION_JSON_VALUE] + ) + fun getEncryptionKeys(): ResponseEntity = + ResponseEntity.ok(transactIdService.getEncryptionKeys()) + @Operation( summary = "Get invoiceRequest binary", description = "Request receive back an invoiceRequest binary so that you can test parsing things" @@ -29,8 +42,24 @@ class TransactIdController { value = ["/initial-invoice-request"], produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE] ) - fun getInitialInvoiceRequest(): ResponseEntity = - ResponseEntity.ok(transactIdService.getInitialInvoiceRequest()) + fun getInitialInvoiceRequest() = ResponseEntity( + transactIdService.getInitialInvoiceRequest(), + HttpStatus.CREATED + ) + + @Operation( + summary = "Get invoiceRequest binary encrypted", + description = "Request receive back an invoiceRequest binary encrypted so that you can test parsing things" + ) + @RequestMapping( + method = [RequestMethod.GET], + value = ["/initial-invoice-request-encrypted"], + produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE] + ) + fun getInitialInvoiceRequestEncrypted() = ResponseEntity( + transactIdService.getInitialInvoiceRequestEncrypted(), + HttpStatus.CREATED + ) @Operation( summary = "Post invoiceRequest binary", diff --git a/src/main/java/com/netki/transactidlibraryjavademo/model/EncryptionKeys.kt b/src/main/java/com/netki/transactidlibraryjavademo/model/EncryptionKeys.kt new file mode 100644 index 0000000..b1a913e --- /dev/null +++ b/src/main/java/com/netki/transactidlibraryjavademo/model/EncryptionKeys.kt @@ -0,0 +1,8 @@ +package com.netki.transactidlibraryjavademo.model + +data class EncryptionKeys( + val senderPrivateKey: String, + val senderPublicKey: String, + val recipientPrivateKey: String, + val recipientPublicKey: String +) diff --git a/src/main/java/com/netki/transactidlibraryjavademo/model/ServiceError.kt b/src/main/java/com/netki/transactidlibraryjavademo/model/ServiceError.kt new file mode 100644 index 0000000..f628297 --- /dev/null +++ b/src/main/java/com/netki/transactidlibraryjavademo/model/ServiceError.kt @@ -0,0 +1,6 @@ +package com.netki.sapphire.model + +data class ServiceError( + var type: ServiceErrorType, + val message: String +) diff --git a/src/main/java/com/netki/transactidlibraryjavademo/model/ServiceErrorType.kt b/src/main/java/com/netki/transactidlibraryjavademo/model/ServiceErrorType.kt new file mode 100644 index 0000000..4ac9363 --- /dev/null +++ b/src/main/java/com/netki/transactidlibraryjavademo/model/ServiceErrorType.kt @@ -0,0 +1,20 @@ +package com.netki.sapphire.model + +enum class ServiceErrorType { + ADDRESS_PROVIDER_ERROR, + ADDRESS_PROVIDER_UNAUTHORIZED, + CERTIFICATE_PROVIDER, + CERTIFICATE_PROVIDER_UNAUTHORIZED, + INVALID_CERTIFICATE_CHAIN, + INVALID_CERTIFICATE, + INVALID_OBJECT, + INVALID_OWNERS, + INVALID_PRIVATE_KEY, + INVALID_SIGNATURE, + KEY_MANAGEMENT_FETCH, + KEY_MANAGEMENT_STORE, + OBJECT_NOT_FOUND, + INVALID_DATA, + ENCRYPTION_ERROR, + UNKNOWN +} diff --git a/src/main/java/com/netki/transactidlibraryjavademo/service/TransactIdService.kt b/src/main/java/com/netki/transactidlibraryjavademo/service/TransactIdService.kt index 972979f..94b2cd4 100755 --- a/src/main/java/com/netki/transactidlibraryjavademo/service/TransactIdService.kt +++ b/src/main/java/com/netki/transactidlibraryjavademo/service/TransactIdService.kt @@ -1,22 +1,59 @@ package com.netki.transactidlibraryjavademo.service import com.netki.TransactId +import com.netki.model.EncryptionParameters +import com.netki.model.MessageInformation +import com.netki.model.RecipientParameters +import com.netki.model.SenderParameters +import com.netki.transactidlibraryjavademo.model.EncryptionKeys import com.netki.transactidlibraryjavademo.util.TestData.Attestations.ATTESTATIONS_REQUESTED import com.netki.transactidlibraryjavademo.util.TestData.InvoiceRequest.INVOICE_REQUEST_DATA import com.netki.transactidlibraryjavademo.util.TestData.Owners.NO_PRIMARY_OWNER_PKI_X509SHA256 import com.netki.transactidlibraryjavademo.util.TestData.Owners.PRIMARY_OWNER_PKI_X509SHA256 import com.netki.transactidlibraryjavademo.util.TestData.Payment.PAYMENT_PARAMETERS import com.netki.transactidlibraryjavademo.util.TestData.PaymentRequest.PAYMENT_REQUEST_PARAMETERS +import com.netki.transactidlibraryjavademo.util.TestData.PkiData.PKI_DATA_SENDER_X509SHA256 import com.netki.transactidlibraryjavademo.util.TestData.Senders.SENDER_PKI_X509SHA256 import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service +import javax.annotation.PostConstruct @Service class TransactIdService { + @Autowired + private lateinit var encryptionKeys: EncryptionKeys + private lateinit var recipientParameters: RecipientParameters + private lateinit var senderParameters: SenderParameters + private val logger = LoggerFactory.getLogger(this.javaClass) private var transactId = TransactId.getInstance("src/main/resources/certificates") val ownerParameters = listOf(PRIMARY_OWNER_PKI_X509SHA256, NO_PRIMARY_OWNER_PKI_X509SHA256) + val messageInformationEncrypted = MessageInformation( + encryptMessage = true + ) + + @PostConstruct + fun setUp() { + recipientParameters = RecipientParameters( + "VASP_1", + "1234567890ABCD", + EncryptionParameters( + encryptionKeys.recipientPrivateKey, + encryptionKeys.recipientPublicKey + ) + ) + senderParameters = SenderParameters( + PKI_DATA_SENDER_X509SHA256, + EncryptionParameters( + encryptionKeys.senderPrivateKey, + encryptionKeys.senderPublicKey + ) + ) + } + + fun getEncryptionKeys() = encryptionKeys fun getInitialInvoiceRequest(): ByteArray { logger.info("Creating InvoiceRequest...") @@ -30,45 +67,107 @@ class TransactIdService { return invoiceRequest } + fun getInitialInvoiceRequestEncrypted(): ByteArray { + logger.info("Creating InvoiceRequest encrypted...") + val invoiceRequest = transactId.createInvoiceRequest( + INVOICE_REQUEST_DATA, + ownerParameters, + senderParameters, + ATTESTATIONS_REQUESTED, + recipientParameters, + messageInformationEncrypted + ) + logger.info("Returning InvoiceRequest...") + return invoiceRequest + } + fun postInvoiceRequest(invoiceRequest: ByteArray): ByteArray { logger.info("InvoiceRequest received") - logger.info("InvoiceRequest valid? ${transactId.isInvoiceRequestValid(invoiceRequest)}") - val invoiceRequestModel = transactId.parseInvoiceRequest(invoiceRequest) + logger.info( + "InvoiceRequest valid? ${transactId.isInvoiceRequestValid( + invoiceRequest, + recipientParameters + )}" + ) + val invoiceRequestModel = + transactId.parseInvoiceRequest(invoiceRequest, recipientParameters) logger.info("InvoiceRequest parsed: $invoiceRequestModel") - logger.info("Creating PaymentRequest...") - val paymentRequest = transactId.createPaymentRequest( - PAYMENT_REQUEST_PARAMETERS, - ownerParameters, - SENDER_PKI_X509SHA256, - ATTESTATIONS_REQUESTED, - 1 - ) - logger.info("Returning PaymentRequest...") - return paymentRequest + return if (invoiceRequestModel.protocolMessageMetadata.encrypted) { + logger.info("Creating PaymentRequest Encrypted...") + logger.info("Returning PaymentRequest Encrypted...") + transactId.createPaymentRequest( + PAYMENT_REQUEST_PARAMETERS, + ownerParameters, + senderParameters, + ATTESTATIONS_REQUESTED, + 1, + messageInformationEncrypted, + recipientParameters + ) + } else { + logger.info("Creating PaymentRequest...") + logger.info("Returning PaymentRequest...") + transactId.createPaymentRequest( + PAYMENT_REQUEST_PARAMETERS, + ownerParameters, + SENDER_PKI_X509SHA256, + ATTESTATIONS_REQUESTED, + 1 + ) + } } fun postPaymentRequest(paymentRequest: ByteArray): ByteArray { logger.info("PaymentRequest received") - logger.info("PaymentRequest valid? ${transactId.isPaymentRequestValid(paymentRequest)}") - val paymentRequestModel = transactId.parsePaymentRequest(paymentRequest) + logger.info( + "PaymentRequest valid? ${transactId.isPaymentRequestValid( + paymentRequest, + recipientParameters + )}" + ) + val paymentRequestModel = + transactId.parsePaymentRequest(paymentRequest, recipientParameters) logger.info("PaymentRequest parsed: $paymentRequestModel") - logger.info("Creating Payment...") - val payment = transactId.createPayment(PAYMENT_PARAMETERS, ownerParameters) - logger.info("Returning Payment...") - return payment + return if (paymentRequestModel.protocolMessageMetadata.encrypted) { + logger.info("Creating Payment Encrypted...") + logger.info("Returning Payment Encrypted...") + transactId.createPayment( + PAYMENT_PARAMETERS, + ownerParameters, + messageInformationEncrypted, + senderParameters, + recipientParameters + ) + + } else { + logger.info("Creating Payment...") + logger.info("Returning Payment...") + transactId.createPayment(PAYMENT_PARAMETERS, ownerParameters) + } } fun postPayment(payment: ByteArray): ByteArray { logger.info("Payment received") - logger.info("Payment valid? ${transactId.isPaymentValid(payment)}") - val paymentModel = transactId.parsePayment(payment) + logger.info("Payment valid? ${transactId.isPaymentValid(payment, recipientParameters)}") + val paymentModel = transactId.parsePayment(payment, recipientParameters) logger.info("Payment parsed: $paymentModel") - logger.info("Creating PaymentAck...") - val paymentAck = transactId.createPaymentAck(paymentModel, "Payment successful") - logger.info("Returning PaymentAck...") - return paymentAck + return if (paymentModel.protocolMessageMetadata!!.encrypted) { + logger.info("Creating PaymentAck Encrypted...") + logger.info("Returning PaymentAck Encrypted...") + transactId.createPaymentAck( + paymentModel, + "Payment successful", + messageInformationEncrypted, + senderParameters, + recipientParameters + ) + } else { + logger.info("Creating PaymentAck...") + logger.info("Returning PaymentAck...") + transactId.createPaymentAck(paymentModel, "Payment successful") + } } } diff --git a/src/main/java/com/netki/transactidlibraryjavademo/util/CryptoModule.kt b/src/main/java/com/netki/transactidlibraryjavademo/util/CryptoModule.kt new file mode 100644 index 0000000..cb9ae4b --- /dev/null +++ b/src/main/java/com/netki/transactidlibraryjavademo/util/CryptoModule.kt @@ -0,0 +1,40 @@ +package com.netki.transactidlibraryjavademo.util + +import org.bouncycastle.util.io.pem.PemObject +import org.bouncycastle.util.io.pem.PemWriter +import java.io.StringWriter +import java.security.PrivateKey +import java.security.PublicKey +import java.security.cert.Certificate + +object CryptoModule { + + /** + * Transform PublicKey to String in PEM format. + * + * @param publicKey to transform. + * @return String in PEM format. + */ + fun objectToPublicKeyPem(publicKey: PublicKey) = objectToPemString(publicKey) + + /** + * Transform PrivateKey to String in PEM format. + * + * @param privateKey to transform. + * @return String in PEM format. + */ + fun objectToPrivateKeyPem(privateKey: PrivateKey) = objectToPemString(privateKey) + + private fun objectToPemString(objectToParse: Any): String { + val stringWriter = StringWriter() + val pemWriter = PemWriter(stringWriter) + when (objectToParse) { + is PrivateKey -> pemWriter.writeObject(PemObject("PRIVATE KEY", objectToParse.encoded)) + is PublicKey -> pemWriter.writeObject(PemObject("PUBLIC KEY", objectToParse.encoded)) + is Certificate -> pemWriter.writeObject(PemObject("CERTIFICATE", objectToParse.encoded)) + } + pemWriter.flush() + pemWriter.close() + return stringWriter.toString() + } +} diff --git a/src/main/java/com/netki/transactidlibraryjavademo/util/KeyGenerator.kt b/src/main/java/com/netki/transactidlibraryjavademo/util/KeyGenerator.kt index 53c569e..5c46a59 100755 --- a/src/main/java/com/netki/transactidlibraryjavademo/util/KeyGenerator.kt +++ b/src/main/java/com/netki/transactidlibraryjavademo/util/KeyGenerator.kt @@ -12,6 +12,8 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import java.math.BigInteger import java.security.* import java.security.cert.Certificate +import java.security.spec.ECGenParameterSpec +import java.security.spec.ECParameterSpec import java.time.Duration import java.time.Instant import java.util.* @@ -27,6 +29,15 @@ object KeyGenerator { return keyPairGenerator.generateKeyPair() } + fun generateKeyPairECDSA(): KeyPair { + val parameters = AlgorithmParameters.getInstance("EC") + parameters.init(ECGenParameterSpec("secp256k1")) + val ecParameterSpec: ECParameterSpec = parameters.getParameterSpec(ECParameterSpec::class.java) + val keyGen = KeyPairGenerator.getInstance("EC") + keyGen.initialize(ecParameterSpec, SecureRandom()) + return keyGen.generateKeyPair() + } + fun generateCertificate(keyPair: KeyPair, hashAlgorithm: String, cn: String): Certificate { val now = Instant.now() val notBefore = Date.from(now)