From e332ca24b76e53a89918a97653e986d67546f8af Mon Sep 17 00:00:00 2001 From: Stefan Maron Date: Fri, 31 Jan 2025 15:08:57 +0100 Subject: [PATCH 1/2] Add functionality to the Crypto Module to create P12 certs containing the PK --- .../X509CertificateWrapper.cs | 14 +++++++++++ .../X509CertificateWrapper.csproj | 7 ++++++ .../src/RSAImpl.Codeunit.al | 6 +++++ .../src/X509Certificate2.Codeunit.al | 12 +++++++++ .../src/X509Certificate2Impl.Codeunit.al | 25 +++++++++++++++++++ .../App/Cryptography Management/src/dotnet.al | 4 +++ 6 files changed, 68 insertions(+) create mode 100644 src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.cs create mode 100644 src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.csproj diff --git a/src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.cs b/src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.cs new file mode 100644 index 0000000000..2d4bf2c1c6 --- /dev/null +++ b/src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.cs @@ -0,0 +1,14 @@ +using System; +using System.Security.Cryptography.X509Certificates; +using System.Runtime.Versioning; + +namespace X509CertificateWrapper +{ + public class X509CertificateWrapper + { + public static string CreateBase64FromPem(string certPem, string keyPem, string password) + { + return Convert.ToBase64String(X509Certificate2.CreateFromPem(certPem, keyPem).Export(X509ContentType.Pkcs12, password)); + } + } +} \ No newline at end of file diff --git a/src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.csproj b/src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.csproj new file mode 100644 index 0000000000..d364a52239 --- /dev/null +++ b/src/System Application/App/Cryptography Management/src/DotNetReadOnlySpanWrapper/X509CertificateWrapper.csproj @@ -0,0 +1,7 @@ + + + net8.0 + enable + enable + + diff --git a/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al b/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al index 4449b0330c..d4ebeeffca 100644 --- a/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al +++ b/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al @@ -247,4 +247,10 @@ codeunit 1476 "RSA Impl." implements "Signature Algorithm v2" DotNetRSASignaturePadding := DotNetRSASignaturePadding.Pss; end; end; + + [NonDebuggable] + internal procedure ExportRSAPrivateKeyPem(): Text + begin + exit(DotNetRSA.ExportRSAPrivateKeyPem()); + end; } \ No newline at end of file diff --git a/src/System Application/App/Cryptography Management/src/X509Certificate2.Codeunit.al b/src/System Application/App/Cryptography Management/src/X509Certificate2.Codeunit.al index 6abf32b3c9..23c190bbdd 100644 --- a/src/System Application/App/Cryptography Management/src/X509Certificate2.Codeunit.al +++ b/src/System Application/App/Cryptography Management/src/X509Certificate2.Codeunit.al @@ -334,4 +334,16 @@ codeunit 1286 X509Certificate2 begin X509Certificate2Impl.GetCertificateSerialNumberAsASCII(CertBase64Value, Password, SerialNumberASCII); end; + + /// + /// Creates a new instance of X509Certificate2 from the specified Base64 encoded certificate value. The certificate is exported as Base64 encoded string. + /// + /// The Base64 encoded certificate in PEM format. + /// The private key in XML format. + /// The password to protect the private key. + /// The Base64 encoded certificate including the private key. + procedure CreateFromPemAndExportAsBase64(CertificateBase64: Text; PrivateKeyXmlString: SecretText; Password: SecretText) CertBase64Value: Text + begin + exit(X509Certificate2Impl.CreateFromPemAndExportAsBase64(CertificateBase64, PrivateKeyXmlString, Password)); + end; } \ No newline at end of file diff --git a/src/System Application/App/Cryptography Management/src/X509Certificate2Impl.Codeunit.al b/src/System Application/App/Cryptography Management/src/X509Certificate2Impl.Codeunit.al index 0471d88dba..bce8b36f7f 100644 --- a/src/System Application/App/Cryptography Management/src/X509Certificate2Impl.Codeunit.al +++ b/src/System Application/App/Cryptography Management/src/X509Certificate2Impl.Codeunit.al @@ -213,4 +213,29 @@ codeunit 1285 "X509Certificate2 Impl." exit(SerialNumberASCII); end; + + [NonDebuggable] + procedure CreateFromPemAndExportAsBase64(CertBase64: Text; PrivateKeyXmlString: SecretText; Password: SecretText): Text + var + RSA: Codeunit "RSA Impl."; + X509CertificateWrapper: DotNet X509CertificateWrapper; + BeginCertTok: Label '-----BEGIN CERTIFICATE-----', Locked = true; + EndCertTok: Label '-----END CERTIFICATE-----', Locked = true; + begin + if CertBase64 = '' then + exit; + + if PrivateKeyXmlString.IsEmpty() then + exit; + + if Password.IsEmpty() then + exit; + + if not CertBase64.StartsWith(BeginCertTok) then + CertBase64 := BeginCertTok + CertBase64 + EndCertTok; + + RSA.FromSecretXmlString(PrivateKeyXmlString); + + exit(X509CertificateWrapper.CreateBase64FromPem(CertBase64, RSA.ExportRSAPrivateKeyPem(), Password.Unwrap())); + end; } \ No newline at end of file diff --git a/src/System Application/App/Cryptography Management/src/dotnet.al b/src/System Application/App/Cryptography Management/src/dotnet.al index 17b0cc2735..2258c4c78d 100644 --- a/src/System Application/App/Cryptography Management/src/dotnet.al +++ b/src/System Application/App/Cryptography Management/src/dotnet.al @@ -50,5 +50,9 @@ dotnet } + assembly("X509CertificateWrapper") + { + type(X509CertificateWrapper.X509CertificateWrapper; X509CertificateWrapper) { } + } } From 5ae2fc6514f60b73791bfe5965a92f50cb337f47 Mon Sep 17 00:00:00 2001 From: Stefan Maron Date: Fri, 31 Jan 2025 15:56:10 +0100 Subject: [PATCH 2/2] Better use secret text --- .../App/Cryptography Management/src/RSAImpl.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al b/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al index d4ebeeffca..0fbe583f0e 100644 --- a/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al +++ b/src/System Application/App/Cryptography Management/src/RSAImpl.Codeunit.al @@ -249,7 +249,7 @@ codeunit 1476 "RSA Impl." implements "Signature Algorithm v2" end; [NonDebuggable] - internal procedure ExportRSAPrivateKeyPem(): Text + internal procedure ExportRSAPrivateKeyPem(): SecretText begin exit(DotNetRSA.ExportRSAPrivateKeyPem()); end;