From 23c84e8c765c1a08d59dfc25a6442d7edfc41fcd Mon Sep 17 00:00:00 2001 From: Moritz Basel Date: Fri, 2 Nov 2018 16:03:03 +0100 Subject: [PATCH 1/7] Remov cast of externalAccountBinding --- src/ACMESharp/Protocol/AcmeProtocolClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ACMESharp/Protocol/AcmeProtocolClient.cs b/src/ACMESharp/Protocol/AcmeProtocolClient.cs index a6aae80..c1bc950 100644 --- a/src/ACMESharp/Protocol/AcmeProtocolClient.cs +++ b/src/ACMESharp/Protocol/AcmeProtocolClient.cs @@ -191,7 +191,7 @@ public async Task CreateAccountAsync(IEnumerable contact { Contact = contacts, TermsOfServiceAgreed = termsOfServiceAgreed, - ExternalAccountBinding = (JwsSignedPayload)externalAccountBinding, + ExternalAccountBinding = externalAccountBinding, }; var resp = await SendAcmeAsync( new Uri(_http.BaseAddress, Directory.NewAccount), @@ -943,4 +943,4 @@ public void Dispose() } #endregion } -} \ No newline at end of file +} From 2c28e9270f0732c402b10fbbe091d569c5503f4a Mon Sep 17 00:00:00 2001 From: WouterTinus Date: Sat, 9 Feb 2019 07:23:56 +0100 Subject: [PATCH 2/7] Fix error for Buypass account registration --- src/ACMESharp/HTTP/Link.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ACMESharp/HTTP/Link.cs b/src/ACMESharp/HTTP/Link.cs index 4426d1a..184ab3c 100644 --- a/src/ACMESharp/HTTP/Link.cs +++ b/src/ACMESharp/HTTP/Link.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.RegularExpressions; namespace ACMESharp.HTTP @@ -19,7 +19,7 @@ public class Link /// /// Regex pattern to match and extract the components of an HTTP related link header. /// - public static readonly Regex LinkHeaderRegex = new Regex("<(.+)>;rel=\"(.+)\""); + public static readonly Regex LinkHeaderRegex = new Regex("<(.+)>;[ ]?rel=\"(.+)\""); public const string LinkHeaderFormat = "<{0}>;rel={1}"; From ed3097e901f6f49bfcde27b62ba0695262da00ac Mon Sep 17 00:00:00 2001 From: WouterTinus Date: Sat, 9 Feb 2019 08:15:50 +0100 Subject: [PATCH 3/7] Revocation support --- .../Controllers/AcmeController.cs | 27 ++++++++++++++++++- .../Controllers/AcmeDirectoryController.cs | 4 ++- src/ACMESharp/Protocol/AcmeProtocolClient.cs | 26 ++++++++++++++++++ .../Messages/RevokeCertificateRequest.cs | 22 +++++++++++++++ .../AcmeControllerTests.cs | 18 +++++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs diff --git a/src/ACMESharp.MockServer/Controllers/AcmeController.cs b/src/ACMESharp.MockServer/Controllers/AcmeController.cs index 0a53339..e7c2050 100644 --- a/src/ACMESharp.MockServer/Controllers/AcmeController.cs +++ b/src/ACMESharp.MockServer/Controllers/AcmeController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -392,6 +392,30 @@ public ActionResult GetCertificate(string certKey) return dbCert.Pem; } + // "revoke-cert": "https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6" + [HttpPost("revoke-cert")] + public ActionResult Revoke(string acctId, + [FromBody]JwsSignedPayload signedPayload) + { + if (!int.TryParse(acctId, out var acctIdNum)) + return NotFound(); + + var ph = ExtractProtectedHeader(signedPayload); + + ValidateNonce(ph); + + var acct = _repo.GetAccountByKid(ph.Kid); + if (acct == null) + throw new Exception("could not resolve account"); + + ValidateAccount(acct, signedPayload); + + // TODO: do stuff? + + GenerateNonce(); + return true; + } + // "https://acme-staging-v02.api.letsencrypt.org/acme/authz/740KRMwcT0UrLXdUKOlgMnfNbzpSQtRaWjbyA1UgIJ4", /* // OK @@ -525,6 +549,7 @@ public ActionResult AnswerChallenge(string authzKey, string challenge return dbChlng.Payload; } + T ExtractPayload(JwsSignedPayload signedPayload) { var payloadBytes = CryptoHelper.Base64.UrlDecode(signedPayload.Payload); diff --git a/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs b/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs index 6b27fe1..2c4b586 100644 --- a/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs +++ b/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs @@ -29,7 +29,9 @@ public ActionResult GetDirectory() values: null, protocol: Request.Scheme), NewAuthz = null, KeyChange = null, - RevokeCert = null, + RevokeCert = Url.Action(nameof(AcmeController.Revoke), + controller: AcmeController.ControllerRoute, + values: null, protocol: Request.Scheme), Meta = new DirectoryMeta { diff --git a/src/ACMESharp/Protocol/AcmeProtocolClient.cs b/src/ACMESharp/Protocol/AcmeProtocolClient.cs index a6aae80..a5b5324 100644 --- a/src/ACMESharp/Protocol/AcmeProtocolClient.cs +++ b/src/ACMESharp/Protocol/AcmeProtocolClient.cs @@ -615,6 +615,32 @@ public async Task GetOrderCertificateAsync( } } + /// + /// Revoke certificate + /// + /// + /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6 + /// + public async Task RevokeCertificateAsync( + byte[] derEncodedCertificate, + CancellationToken cancel = default(CancellationToken)) + { + var message = new RevokeCertificateRequest + { + Certificate = CryptoHelper.Base64.UrlEncode(derEncodedCertificate), + }; + var resp = await SendAcmeAsync( + new Uri(_http.BaseAddress, Directory.RevokeCert), + method: HttpMethod.Post, + message: message, + expectedStatuses: new[] { HttpStatusCode.OK }, + cancel: cancel); + + // If OK is returned, we're all done. Otherwise general + // exception handling will kick in + return true; + } + /// /// Generic fetch routine to retrieve raw bytes from a URL associated /// with an ACME endpoint. diff --git a/src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs b/src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs new file mode 100644 index 0000000..0671ed9 --- /dev/null +++ b/src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; + +namespace ACMESharp.Protocol.Messages +{ + /// + /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6 + /// + public class RevokeCertificateRequest + { + [JsonProperty("certificate", Required = Required.Always)] + [Required] + public string Certificate { get; set; } + + // Possible reasons specified here + // https://tools.ietf.org/html/rfc5280#section-5.3.1 + // Not sure where best to create an enum and how to handle the (optional) + // serialization of this property, so leaving it out for now + //[JsonProperty("reason")] + //public int Reason { get; set; } + } +} \ No newline at end of file diff --git a/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs b/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs index 85be4d3..795a4d3 100644 --- a/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs +++ b/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs @@ -388,6 +388,24 @@ public async Task FinalizeOrder() } + [TestMethod] + public async Task RevokeCertificate() + { + using (var http = _server.CreateClient()) + { + var dir = await GetDir(); + var signer = new Crypto.JOSE.Impl.RSJwsTool(); + signer.Init(); + using (var acme = new AcmeProtocolClient(http, dir, + signer: signer)) + { + await acme.GetNonceAsync(); + // TODO go through the motions as the FinalizeOrder test? + await acme.RevokeCertificateAsync(new byte[] { }); + } + } + } + private async Task GetDir() { From 7cead7d4382c49702a4fff47cf0933782f32ace2 Mon Sep 17 00:00:00 2001 From: WouterTinus Date: Sat, 9 Feb 2019 11:55:23 +0100 Subject: [PATCH 4/7] Add ability to decode TLS-ALPN-01 challenges --- .../Authorizations/AuthorizationDecoder.cs | 21 +++++++++++++++++-- .../TlsAlpn01ChallengeValidationDetails.cs | 16 ++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/ACMESharp/Authorizations/TlsAlpn01ChallengeValidationDetails.cs diff --git a/src/ACMESharp/Authorizations/AuthorizationDecoder.cs b/src/ACMESharp/Authorizations/AuthorizationDecoder.cs index b672558..603b91c 100644 --- a/src/ACMESharp/Authorizations/AuthorizationDecoder.cs +++ b/src/ACMESharp/Authorizations/AuthorizationDecoder.cs @@ -26,10 +26,12 @@ public static IChallengeValidationDetails DecodeChallengeValidation( switch (challengeType) { - case "dns-01": + case Dns01ChallengeValidationDetails.Dns01ChallengeType: return ResolveChallengeForDns01(authz, challenge, signer); - case "http-01": + case Http01ChallengeValidationDetails.Http01ChallengeType: return ResolveChallengeForHttp01(authz, challenge, signer); + case TlsAlpn01ChallengeValidationDetails.TlsAlpn01ChallengeType: + return ResolveChallengeForTlsAlpn01(authz, challenge, signer); } throw new NotImplementedException( @@ -78,5 +80,20 @@ public static Http01ChallengeValidationDetails ResolveChallengeForHttp01( HttpResourceValue = keyAuthz, }; } + + /// + /// + /// + /// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05 + /// + public static TlsAlpn01ChallengeValidationDetails ResolveChallengeForTlsAlpn01( + Authorization authz, Challenge challenge, IJwsTool signer) + { + var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, challenge.Token); + return new TlsAlpn01ChallengeValidationDetails + { + TokenValue = keyAuthz, + }; + } } } \ No newline at end of file diff --git a/src/ACMESharp/Authorizations/TlsAlpn01ChallengeValidationDetails.cs b/src/ACMESharp/Authorizations/TlsAlpn01ChallengeValidationDetails.cs new file mode 100644 index 0000000..acad99e --- /dev/null +++ b/src/ACMESharp/Authorizations/TlsAlpn01ChallengeValidationDetails.cs @@ -0,0 +1,16 @@ +namespace ACMESharp.Authorizations +{ + /// + /// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05 + /// + public class TlsAlpn01ChallengeValidationDetails : IChallengeValidationDetails + { + public const string TlsAlpn01ChallengeType = "tls-alpn-01"; + public const string AlpnExtensionName = "acme-tls/1"; + public const string AcmeIdentifierExtension = "acmeIdentifier"; + + public string ChallengeType => TlsAlpn01ChallengeType; + + public string TokenValue { get; set; } + } +} \ No newline at end of file From fa67abb3180a376d10070dc95ecdcb24793503e8 Mon Sep 17 00:00:00 2001 From: WouterTinus Date: Sat, 9 Feb 2019 11:56:47 +0100 Subject: [PATCH 5/7] Revert "Revocation support" This reverts commit ed3097e901f6f49bfcde27b62ba0695262da00ac. --- .../Controllers/AcmeController.cs | 27 +------------------ .../Controllers/AcmeDirectoryController.cs | 4 +-- src/ACMESharp/Protocol/AcmeProtocolClient.cs | 26 ------------------ .../Messages/RevokeCertificateRequest.cs | 22 --------------- .../AcmeControllerTests.cs | 18 ------------- 5 files changed, 2 insertions(+), 95 deletions(-) delete mode 100644 src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs diff --git a/src/ACMESharp.MockServer/Controllers/AcmeController.cs b/src/ACMESharp.MockServer/Controllers/AcmeController.cs index e7c2050..0a53339 100644 --- a/src/ACMESharp.MockServer/Controllers/AcmeController.cs +++ b/src/ACMESharp.MockServer/Controllers/AcmeController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -392,30 +392,6 @@ public ActionResult GetCertificate(string certKey) return dbCert.Pem; } - // "revoke-cert": "https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6" - [HttpPost("revoke-cert")] - public ActionResult Revoke(string acctId, - [FromBody]JwsSignedPayload signedPayload) - { - if (!int.TryParse(acctId, out var acctIdNum)) - return NotFound(); - - var ph = ExtractProtectedHeader(signedPayload); - - ValidateNonce(ph); - - var acct = _repo.GetAccountByKid(ph.Kid); - if (acct == null) - throw new Exception("could not resolve account"); - - ValidateAccount(acct, signedPayload); - - // TODO: do stuff? - - GenerateNonce(); - return true; - } - // "https://acme-staging-v02.api.letsencrypt.org/acme/authz/740KRMwcT0UrLXdUKOlgMnfNbzpSQtRaWjbyA1UgIJ4", /* // OK @@ -549,7 +525,6 @@ public ActionResult AnswerChallenge(string authzKey, string challenge return dbChlng.Payload; } - T ExtractPayload(JwsSignedPayload signedPayload) { var payloadBytes = CryptoHelper.Base64.UrlDecode(signedPayload.Payload); diff --git a/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs b/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs index 2c4b586..6b27fe1 100644 --- a/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs +++ b/src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs @@ -29,9 +29,7 @@ public ActionResult GetDirectory() values: null, protocol: Request.Scheme), NewAuthz = null, KeyChange = null, - RevokeCert = Url.Action(nameof(AcmeController.Revoke), - controller: AcmeController.ControllerRoute, - values: null, protocol: Request.Scheme), + RevokeCert = null, Meta = new DirectoryMeta { diff --git a/src/ACMESharp/Protocol/AcmeProtocolClient.cs b/src/ACMESharp/Protocol/AcmeProtocolClient.cs index a5b5324..a6aae80 100644 --- a/src/ACMESharp/Protocol/AcmeProtocolClient.cs +++ b/src/ACMESharp/Protocol/AcmeProtocolClient.cs @@ -615,32 +615,6 @@ public async Task GetOrderCertificateAsync( } } - /// - /// Revoke certificate - /// - /// - /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6 - /// - public async Task RevokeCertificateAsync( - byte[] derEncodedCertificate, - CancellationToken cancel = default(CancellationToken)) - { - var message = new RevokeCertificateRequest - { - Certificate = CryptoHelper.Base64.UrlEncode(derEncodedCertificate), - }; - var resp = await SendAcmeAsync( - new Uri(_http.BaseAddress, Directory.RevokeCert), - method: HttpMethod.Post, - message: message, - expectedStatuses: new[] { HttpStatusCode.OK }, - cancel: cancel); - - // If OK is returned, we're all done. Otherwise general - // exception handling will kick in - return true; - } - /// /// Generic fetch routine to retrieve raw bytes from a URL associated /// with an ACME endpoint. diff --git a/src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs b/src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs deleted file mode 100644 index 0671ed9..0000000 --- a/src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; - -namespace ACMESharp.Protocol.Messages -{ - /// - /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6 - /// - public class RevokeCertificateRequest - { - [JsonProperty("certificate", Required = Required.Always)] - [Required] - public string Certificate { get; set; } - - // Possible reasons specified here - // https://tools.ietf.org/html/rfc5280#section-5.3.1 - // Not sure where best to create an enum and how to handle the (optional) - // serialization of this property, so leaving it out for now - //[JsonProperty("reason")] - //public int Reason { get; set; } - } -} \ No newline at end of file diff --git a/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs b/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs index 795a4d3..85be4d3 100644 --- a/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs +++ b/test/ACMESharp.MockServer.UnitTests/AcmeControllerTests.cs @@ -388,24 +388,6 @@ public async Task FinalizeOrder() } - [TestMethod] - public async Task RevokeCertificate() - { - using (var http = _server.CreateClient()) - { - var dir = await GetDir(); - var signer = new Crypto.JOSE.Impl.RSJwsTool(); - signer.Init(); - using (var acme = new AcmeProtocolClient(http, dir, - signer: signer)) - { - await acme.GetNonceAsync(); - // TODO go through the motions as the FinalizeOrder test? - await acme.RevokeCertificateAsync(new byte[] { }); - } - } - } - private async Task GetDir() { From 92733cb34210128b2b247eb14d3667c6295eed33 Mon Sep 17 00:00:00 2001 From: WouterTinus Date: Tue, 12 Feb 2019 20:24:23 +0100 Subject: [PATCH 6/7] Lock down the ordering of the fields in the JSON --- src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs | 22 ++++++++++++++++----- src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs | 22 +++++++++++++++------ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs b/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs index 61265f2..8fb9714 100644 --- a/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs +++ b/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs @@ -110,6 +110,22 @@ public void Import(string exported) // } // } + // As per RFC 7638 Section 3, these are the *required* elements of the + // JWK and are sorted in lexicographic order to produce a canonical form + class ESJwk + { + [JsonProperty(Order = 1)] + public string crv; + + [JsonProperty(Order = 2)] + public string kty = "EC"; + + [JsonProperty(Order = 3)] + public string x; + + [JsonProperty(Order = 4)] + public string y; + } public object ExportJwk(bool canonical = false) { @@ -119,13 +135,9 @@ public object ExportJwk(bool canonical = false) if (_jwk == null) { var keyParams = _dsa.ExportParameters(false); - _jwk = new + _jwk = new ESJwk { - // As per RFC 7638 Section 3, these are the *required* elements of the - // JWK and are sorted in lexicographic order to produce a canonical form - crv = CurveName, - kty = "EC", // https://tools.ietf.org/html/rfc7518#section-6.2 x = CryptoHelper.Base64.UrlEncode(keyParams.Q.X), y = CryptoHelper.Base64.UrlEncode(keyParams.Q.Y), }; diff --git a/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs b/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs index 856f00d..961db67 100644 --- a/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs +++ b/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Security.Cryptography; using Newtonsoft.Json; @@ -87,6 +87,20 @@ public void Import(string exported) // } // } + // As per RFC 7638 Section 3, these are the *required* elements of the + // JWK and are sorted in lexicographic order to produce a canonical form + class RSJwk + { + [JsonProperty(Order = 1)] + public string e; + + [JsonProperty(Order = 2)] + public string kty = "RSA"; + + [JsonProperty(Order = 3)] + public string n; + } + public object ExportJwk(bool canonical = false) { // Note, we only produce a canonical form of the JWK @@ -95,13 +109,9 @@ public object ExportJwk(bool canonical = false) if (_jwk == null) { var keyParams = _rsa.ExportParameters(false); - _jwk = new + _jwk = new RSJwk { - // As per RFC 7638 Section 3, these are the *required* elements of the - // JWK and are sorted in lexicographic order to produce a canonical form - e = CryptoHelper.Base64.UrlEncode(keyParams.Exponent), - kty = "RSA", // https://tools.ietf.org/html/rfc7518#section-6.3 n = CryptoHelper.Base64.UrlEncode(keyParams.Modulus), }; } From 1d3ce2151280dc94913a2b90f682ef46639820f3 Mon Sep 17 00:00:00 2001 From: WouterTinus Date: Tue, 12 Feb 2019 20:51:37 +0100 Subject: [PATCH 7/7] Add explicit ordering for JSON - #17 --- src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs | 2 +- src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs | 2 +- test/ACMESharp.UnitTests/JwsTests.cs | 45 +++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs b/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs index 8fb9714..07dbe89 100644 --- a/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs +++ b/src/ACMESharp/Crypto/JOSE/Impl/ESJwsTool.cs @@ -13,7 +13,7 @@ public class ESJwsTool : IJwsTool { private HashAlgorithmName _shaName; private ECDsa _dsa; - private object _jwk; + private ESJwk _jwk; /// /// Specifies the size in bits of the SHA-2 hash function to use. diff --git a/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs b/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs index 961db67..57ca334 100644 --- a/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs +++ b/src/ACMESharp/Crypto/JOSE/Impl/RSJwsTool.cs @@ -13,7 +13,7 @@ public class RSJwsTool : IJwsTool { private HashAlgorithm _sha; private RSACryptoServiceProvider _rsa; - private object _jwk; + private RSJwk _jwk; /// /// Specifies the size in bits of the SHA-2 hash function to use. diff --git a/test/ACMESharp.UnitTests/JwsTests.cs b/test/ACMESharp.UnitTests/JwsTests.cs index d1e3454..f35a416 100644 --- a/test/ACMESharp.UnitTests/JwsTests.cs +++ b/test/ACMESharp.UnitTests/JwsTests.cs @@ -1,7 +1,9 @@ using System; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using ACMESharp.Crypto; +using ACMESharp.Crypto.JOSE.Impl; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace ACMESharp.UnitTests @@ -305,5 +307,48 @@ public void TestRfc7515Example_A_2_1() string sigB64uActual = CryptoHelper.Base64.UrlEncode(sigActual); Assert.AreEqual(sigB64uExpected, sigB64uActual); } + + [TestMethod] + public void SerDesEC() + { + var rng = RandomNumberGenerator.Create(); + for (var i = 0; i < 1000; i++) { + var original = new ESJwsTool(); // Default for ISigner + original.Init(); + var rawX = new byte[8034]; + rng.GetBytes(rawX); + var sigX = original.Sign(rawX); + + var exported = original.Export(); + var copy = new ESJwsTool(); + copy.Init(); + copy.Import(exported); + var verified = copy.Verify(rawX, sigX); + + Assert.AreEqual(true, verified); + } + } + + [TestMethod] + public void SerDesRSA() + { + var rng = RandomNumberGenerator.Create(); + for (var i = 0; i < 1000; i++) + { + var original = new RSJwsTool(); // Default for ISigner + original.Init(); + var rawX = new byte[8034]; + rng.GetBytes(rawX); + var sigX = original.Sign(rawX); + + var exported = original.Export(); + var copy = new RSJwsTool(); + copy.Init(); + copy.Import(exported); + var verified = copy.Verify(rawX, sigX); + + Assert.AreEqual(true, verified); + } + } } } \ No newline at end of file