├── tools
├── push.cmd
└── build.cmd
├── .gitignore
├── src
├── ACME.Protocol.Model
│ ├── Model
│ │ ├── IVersioned.cs
│ │ ├── AccountStatus.cs
│ │ ├── ChallengeStatus.cs
│ │ ├── AuthorizationStatus.cs
│ │ ├── ChallengeTypes.cs
│ │ ├── Exceptions
│ │ │ ├── ConcurrencyException.cs
│ │ │ ├── BadNonceException.cs
│ │ │ ├── BadSignatureAlgorithmException.cs
│ │ │ ├── NotInitializedException.cs
│ │ │ ├── AcmeException.cs
│ │ │ └── MalformedRequestException.cs
│ │ ├── GuidString.cs
│ │ ├── Nonce.cs
│ │ ├── AuthorizationStatusExtensions.cs
│ │ ├── CryptoString.cs
│ │ ├── OrderStatus.cs
│ │ ├── Jwk.cs
│ │ ├── Extensions
│ │ │ └── SerializationInfoExtension.cs
│ │ ├── Identifier.cs
│ │ ├── Account.cs
│ │ ├── AcmeError.cs
│ │ ├── Challenge.cs
│ │ ├── Authorization.cs
│ │ └── Order.cs
│ ├── HttpModel
│ │ ├── Requests
│ │ │ ├── FinalizeOrder.cs
│ │ │ ├── AcmePayload.cs
│ │ │ ├── Identifier.cs
│ │ │ ├── CreateOrGetAccountRequest.cs
│ │ │ ├── CreateOrderRequest.cs
│ │ │ ├── AcmeHeader.cs
│ │ │ └── AcmeRawPostRequest.cs
│ │ ├── OrdersList.cs
│ │ ├── DirectoryMetadata.cs
│ │ ├── Identifier.cs
│ │ ├── Directory.cs
│ │ ├── Converters
│ │ │ └── JwkConverter.cs
│ │ ├── Account.cs
│ │ ├── AcmeError.cs
│ │ ├── Authorization.cs
│ │ ├── Challenge.cs
│ │ ├── EnumMappings.cs
│ │ └── Order.cs
│ └── ACME.Protocol.Model.csproj
├── ACME.Protocol
│ ├── AcmeProtocolOptions.cs
│ ├── GlobalSuppressions.cs
│ ├── Services
│ │ ├── DefaultAuthorizationFactory.cs
│ │ ├── DefaultNonceService.cs
│ │ ├── DefaultChallangeValidatorFactory.cs
│ │ ├── Http01ChallangeValidator.cs
│ │ ├── TokenChallengeValidator.cs
│ │ ├── Dns01ChallangeValidator.cs
│ │ ├── DefaultAccountService.cs
│ │ └── DefaultOrderService.cs
│ ├── ACME.Protocol.csproj
│ ├── Workers
│ │ ├── IssuanceWorker.cs
│ │ └── ValidationWorker.cs
│ └── RequestServices
│ │ ├── DefaultRequestProvider.cs
│ │ └── DefaultRequestValidationService.cs
├── ACME.Protocol.Abstractions
│ ├── Services
│ │ ├── IChallangeValidatorFactory.cs
│ │ ├── IAuthorizationFactory.cs
│ │ ├── INonceService.cs
│ │ ├── IChallengeValidator.cs
│ │ ├── IAccountService.cs
│ │ └── IOrderService.cs
│ ├── Workers
│ │ ├── IIssuanceWorker.cs
│ │ └── IValidationWorker.cs
│ ├── IssuanceServices
│ │ ├── ICsrValidator.cs
│ │ └── ICertificateIssuer.cs
│ ├── Storage
│ │ ├── INonceStore.cs
│ │ ├── IAccountStore.cs
│ │ └── IOrderStore.cs
│ ├── RequestServices
│ │ ├── IAcmeRequestProvider.cs
│ │ └── IRequestValidationService.cs
│ └── ACME.Protocol.Abstractions.csproj
├── ACME.Server
│ ├── Configuration
│ │ ├── TOSOptions.cs
│ │ ├── ACMEServerOptions.cs
│ │ └── BackgroundServiceOptions.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Middleware
│ │ ├── AcmeMiddlewareExtensions.cs
│ │ └── AcmeMiddleware.cs
│ ├── Controllers
│ │ ├── NonceController.cs
│ │ ├── DirectoryController.cs
│ │ ├── AccountController.cs
│ │ └── OrderController.cs
│ ├── ModelBinding
│ │ ├── AcmeHeaderBinder.cs
│ │ ├── AcmePayloadBinder.cs
│ │ └── ModelBindingProvider.cs
│ ├── Filters
│ │ ├── AcmeIndexLinkFilter.cs
│ │ ├── ValidateAcmeRequestFilter.cs
│ │ ├── AcmeExceptionFilter.cs
│ │ ├── AcmeLocationFilter.cs
│ │ └── AddNextNonceFilter.cs
│ ├── ACME.Server.csproj
│ ├── BackgroundServices
│ │ ├── HostedIssuanceService.cs
│ │ ├── HostedValidationService.cs
│ │ └── TimedHostedService.cs
│ └── Extensions
│ │ └── ServiceCollectionExtensions.cs
└── ACME.Storage.FileStore
│ ├── JsonDefaults.cs
│ ├── Extensions
│ └── ServiceCollectionExtensions.cs
│ ├── Configuration
│ └── FileStoreOptions.cs
│ ├── ACME.Storage.FileStore.csproj
│ ├── NonceStore.cs
│ ├── AccountStore.cs
│ ├── StoreBase.cs
│ └── OrderStore.cs
├── NuGet.Config
├── Directory.Build.props
├── test
├── ACME.Protocol.Model.Tests
│ ├── HttpModel-Initialization
│ │ ├── Order.cs
│ │ ├── Identifier.cs
│ │ ├── Account.cs
│ │ ├── AcmeError.cs
│ │ ├── Authorization.cs
│ │ └── Challenge.cs
│ ├── Model-Initialization
│ │ ├── Nonce.cs
│ │ ├── GuidString.cs
│ │ ├── CryptoString.cs
│ │ ├── AcmeError.cs
│ │ ├── Account.cs
│ │ └── Identifier.cs
│ ├── ACME.Protocol.Model.Tests.csproj
│ └── StaticTestData.cs
└── ACME.Server.Tests
│ └── ACME.Server.Tests.csproj
├── .github
└── FUNDING.yml
├── .editorconfig
├── LICENSE
├── README.md
└── ACME-Server.sln
/tools/push.cmd:
--------------------------------------------------------------------------------
1 | dotnet nuget push
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vs
2 | **/obj/
3 | **/bin/
4 | /dist
5 |
--------------------------------------------------------------------------------
/tools/build.cmd:
--------------------------------------------------------------------------------
1 | del /S/Q .\dist\*
2 | dotnet build -c Release
3 | dotnet pack -c Release -o .\dist\
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/IVersioned.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.Model
2 | {
3 | public interface IVersioned
4 | {
5 | long Version { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/ACME.Protocol/AcmeProtocolOptions.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol
2 | {
3 | public class AcmeProtocolOptions
4 | {
5 | public bool AllowCNSuffix { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/AccountStatus.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.Model
2 | {
3 | public enum AccountStatus
4 | {
5 | Valid,
6 | Deactivated,
7 | Revoked
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1.0.0-alpha6
4 | Enable
5 |
6 | Thomas Glatzer; GitHub Contributors
7 |
8 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/ChallengeStatus.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.Model
2 | {
3 | public enum ChallengeStatus
4 | {
5 | Pending,
6 | Processing,
7 | Valid,
8 | Invalid
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/HttpModel-Initialization/Order.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace TGIT.ACME.Protocol.Model.Tests.HttpModel_Initialization
6 | {
7 | class Order
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Services/IChallangeValidatorFactory.cs:
--------------------------------------------------------------------------------
1 | using TGIT.ACME.Protocol.Model;
2 |
3 | namespace TGIT.ACME.Protocol.Services
4 | {
5 | public interface IChallangeValidatorFactory
6 | {
7 | IChallengeValidator GetValidator(Challenge challenge);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Workers/IIssuanceWorker.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace TGIT.ACME.Protocol.Workers
5 | {
6 | public interface IIssuanceWorker
7 | {
8 | Task RunAsync(CancellationToken cancellationToken);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Workers/IValidationWorker.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace TGIT.ACME.Protocol.Workers
5 | {
6 | public interface IValidationWorker
7 | {
8 | Task RunAsync(CancellationToken cancellationToken);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Services/IAuthorizationFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using TGIT.ACME.Protocol.Model;
3 |
4 | namespace TGIT.ACME.Protocol.Services
5 | {
6 | public interface IAuthorizationFactory
7 | {
8 | void CreateAuthorizations(Order order);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Requests/FinalizeOrder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace TGIT.ACME.Protocol.HttpModel.Requests
6 | {
7 | public class FinalizeOrderRequest
8 | {
9 | public string? Csr { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/AuthorizationStatus.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace TGIT.ACME.Protocol.Model
4 | {
5 | public enum AuthorizationStatus
6 | {
7 | Pending,
8 | Valid,
9 | Invalid,
10 | Revoked,
11 | Deactivated,
12 | Expired
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/ACME.Server/Configuration/TOSOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TGIT.ACME.Server.Configuration
4 | {
5 | public class TOSOptions
6 | {
7 | public bool RequireAgreement { get; set; }
8 | public string? Url { get; set; }
9 |
10 | public DateTime? LastUpdate { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Requests/AcmePayload.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.HttpModel.Requests
2 | {
3 | public class AcmePayload
4 | {
5 | public AcmePayload(TPayload value)
6 | {
7 | Value = value;
8 | }
9 |
10 | public TPayload Value { get; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/ChallengeTypes.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.Model
2 | {
3 | public static class ChallengeTypes
4 | {
5 | public const string Http01 = "http-01";
6 | public const string Dns01 = "dns-01";
7 |
8 | public static readonly string[] AllTypes = new[] { Http01, Dns01 };
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Services/INonceService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using TGIT.ACME.Protocol.Model;
4 |
5 | namespace TGIT.ACME.Protocol.Services
6 | {
7 | public interface INonceService
8 | {
9 | Task CreateNonceAsync(CancellationToken cancellationToken);
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/Exceptions/ConcurrencyException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TGIT.ACME.Protocol.Model.Exceptions
4 | {
5 | public class ConcurrencyException : InvalidOperationException
6 | {
7 | public ConcurrencyException()
8 | : base($"Object has been changed since loading")
9 | { }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Server/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "ACME.Server": {
4 | "commandName": "Project",
5 | "launchBrowser": false,
6 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
7 | "environmentVariables": {
8 | "ASPNETCORE_ENVIRONMENT": "Development"
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Requests/Identifier.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.HttpModel.Requests
2 | {
3 | ///
4 | /// Defines an identifier as used in orders or authorizations
5 | ///
6 | public class Identifier
7 | {
8 | public string? Type { get; set; }
9 | public string? Value { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/Exceptions/BadNonceException.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.Model.Exceptions
2 | {
3 | public class BadNonceException : AcmeException
4 | {
5 | private const string Detail = "The nonce could not be accepted.";
6 |
7 | public BadNonceException() : base(Detail) { }
8 |
9 | public override string ErrorType => "badNonce";
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Server/Configuration/ACMEServerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Server.Configuration
2 | {
3 | public class ACMEServerOptions
4 | {
5 | public BackgroundServiceOptions HostedWorkers { get; set; } = new BackgroundServiceOptions();
6 |
7 | public string? WebsiteUrl { get; set; }
8 |
9 | public TOSOptions TOS { get; set; } = new TOSOptions();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Requests/CreateOrGetAccountRequest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace TGIT.ACME.Protocol.HttpModel.Requests
4 | {
5 | public class CreateOrGetAccount
6 | {
7 | public List? Contact { get; set; }
8 |
9 | public bool TermsOfServiceAgreed { get; set; }
10 | public bool OnlyReturnExisting { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/IssuanceServices/ICsrValidator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using TGIT.ACME.Protocol.Model;
4 |
5 | namespace TGIT.ACME.Protocol.IssuanceServices
6 | {
7 | public interface ICsrValidator
8 | {
9 | Task<(bool isValid, AcmeError? error)> ValidateCsrAsync(Order order, string csr, CancellationToken cancellationToken);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/IssuanceServices/ICertificateIssuer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using TGIT.ACME.Protocol.Model;
4 |
5 | namespace TGIT.ACME.Protocol.IssuanceServices
6 | {
7 | public interface ICertificateIssuer
8 | {
9 | Task<(byte[]? certificate, AcmeError? error)> IssueCertificate(string csr, CancellationToken cancellationToken);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Services/IChallengeValidator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using TGIT.ACME.Protocol.Model;
4 |
5 | namespace TGIT.ACME.Protocol.Services
6 | {
7 | public interface IChallengeValidator
8 | {
9 | Task<(bool IsValid, AcmeError? error)> ValidateChallengeAsync(Challenge challenge, Account account, CancellationToken cancellationToken);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Requests/CreateOrderRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TGIT.ACME.Protocol.HttpModel.Requests
5 | {
6 | public class CreateOrderRequest
7 | {
8 | public List? Identifiers { get; set; }
9 |
10 | public DateTimeOffset? NotBefore { get; set; }
11 | public DateTimeOffset? NotAfter { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Storage/INonceStore.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using TGIT.ACME.Protocol.Model;
4 |
5 | namespace TGIT.ACME.Protocol.Storage
6 | {
7 | public interface INonceStore
8 | {
9 | Task SaveNonceAsync(Nonce nonce, CancellationToken cancellationToken);
10 | Task TryRemoveNonceAsync(Nonce nonce, CancellationToken cancellationToken);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/Exceptions/BadSignatureAlgorithmException.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.Model.Exceptions
2 | {
3 | public class BadSignatureAlgorithmException : AcmeException
4 | {
5 | private const string Detail = "The ALG is not supported.";
6 |
7 | public BadSignatureAlgorithmException() : base(Detail) { }
8 |
9 | public override string ErrorType => "badSignatureAlgorithm";
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Server/Configuration/BackgroundServiceOptions.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Server.Configuration
2 | {
3 | public class BackgroundServiceOptions
4 | {
5 | public bool EnableValidationService { get; set; } = true;
6 | public bool EnableIssuanceService { get; set; } = true;
7 |
8 | public int ValidationCheckInterval { get; set; } = 60;
9 | public int IssuanceCheckInterval { get; set; } = 60;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/Model-Initialization/Nonce.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace TGIT.ACME.Protocol.Model.Tests.Model_Initialization
4 | {
5 | public class Nonce
6 | {
7 | [Fact]
8 | public void Ctor_Populates_All_Properties()
9 | {
10 | var token = "ABC";
11 | var sut = new Model.Nonce(token);
12 |
13 | Assert.Equal(token, sut.Token);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Storage/IAccountStore.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using TGIT.ACME.Protocol.Model;
4 |
5 | namespace TGIT.ACME.Protocol.Storage
6 | {
7 | public interface IAccountStore
8 | {
9 | Task SaveAccountAsync(Account account, CancellationToken cancellationToken);
10 | Task LoadAccountAsync(string accountId, CancellationToken cancellationToken);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ACME.Server/Middleware/AcmeMiddlewareExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using TGIT.ACME.Server.Middleware;
3 |
4 | namespace Microsoft.AspNetCore.Builder
5 | {
6 | public static class AcmeMiddlewareExtensions
7 | {
8 | public static IApplicationBuilder UseAcmeServer(this IApplicationBuilder builder)
9 | {
10 | return builder.UseMiddleware();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/RequestServices/IAcmeRequestProvider.cs:
--------------------------------------------------------------------------------
1 | using TGIT.ACME.Protocol.HttpModel.Requests;
2 |
3 | namespace TGIT.ACME.Protocol.RequestServices
4 | {
5 | public interface IAcmeRequestProvider
6 | {
7 | void Initialize(AcmeRawPostRequest rawPostRequest);
8 |
9 | AcmeRawPostRequest GetRequest();
10 |
11 | AcmeHeader GetHeader();
12 |
13 | TPayload GetPayload();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/GuidString.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.IdentityModel.Tokens;
2 | using System;
3 |
4 | namespace TGIT.ACME.Protocol.Model
5 | {
6 | public class GuidString
7 | {
8 | private GuidString()
9 | {
10 | Value = Base64UrlEncoder.Encode(Guid.NewGuid().ToByteArray());
11 | }
12 |
13 | private string Value { get; }
14 |
15 | public static string NewValue() => new GuidString().Value;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/RequestServices/IRequestValidationService.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using TGIT.ACME.Protocol.HttpModel.Requests;
5 |
6 | namespace TGIT.ACME.Protocol.RequestServices
7 | {
8 | public interface IRequestValidationService
9 | {
10 | Task ValidateRequestAsync(AcmeRawPostRequest request, AcmeHeader header,
11 | string requestUrl, CancellationToken cancellationToken);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/Exceptions/NotInitializedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace TGIT.ACME.Protocol.Model.Exceptions
5 | {
6 | public class NotInitializedException : InvalidOperationException
7 | {
8 | public NotInitializedException([CallerMemberName]string caller = null!)
9 | :base($"{caller} has been accessed before being initialized.")
10 | {
11 |
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/Model-Initialization/GuidString.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace TGIT.ACME.Protocol.Model.Tests.Model_Initialization
4 | {
5 | public class GuidString
6 | {
7 | [Fact]
8 | public void GuidString_Seems_Filled()
9 | {
10 | var sut = Model.GuidString.NewValue();
11 |
12 | Assert.False(string.IsNullOrWhiteSpace(sut));
13 | Assert.Equal(22, sut.Length);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/Model-Initialization/CryptoString.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace TGIT.ACME.Protocol.Model.Tests.Model_Initialization
4 | {
5 | public class CryptoString
6 | {
7 | [Fact]
8 | public void CryptoString_Seems_Filled()
9 | {
10 | var sut = Model.CryptoString.NewValue(48);
11 |
12 | Assert.False(string.IsNullOrWhiteSpace(sut));
13 | Assert.Equal(64, sut.Length);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/OrdersList.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace TGIT.ACME.Protocol.HttpModel
5 | {
6 | ///
7 | /// Represents a list of order urls
8 | ///
9 | public class OrdersList
10 | {
11 | public OrdersList(IEnumerable orders)
12 | {
13 | Orders = orders.ToList();
14 | }
15 |
16 | public List Orders { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ACME.Protocol/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project.
3 | // Project-level suppressions either have no target or are given
4 | // a specific target and scoped to a namespace, type, member, etc.
5 |
6 | using System.Diagnostics.CodeAnalysis;
7 |
8 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:TGIT.ACME.Protocol.Model.Order.Certificate")]
9 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/Nonce.cs:
--------------------------------------------------------------------------------
1 | using TGIT.ACME.Protocol.Model.Exceptions;
2 |
3 | namespace TGIT.ACME.Protocol.Model
4 | {
5 | public class Nonce
6 | {
7 | private string? _token;
8 |
9 | private Nonce() { }
10 |
11 | public Nonce(string token)
12 | {
13 | Token = token;
14 | }
15 |
16 | public string Token {
17 | get => _token ?? throw new NotInitializedException();
18 | private set => _token = value;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/DirectoryMetadata.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.HttpModel
2 | {
3 | ///
4 | /// Describes the HTTP-Response-Model for ACME DirectoryMetadata
5 | /// https://tools.ietf.org/html/rfc8555#section-7.1.1
6 | ///
7 | public class DirectoryMetadata
8 | {
9 | public string? TermsOfService { get; set; }
10 | public string? Website { get; set; }
11 | public string? CAAIdentities { get; set; }
12 | public bool? ExternalAccountRequired { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/ACME.Server.Tests/ACME.Server.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Identifier.cs:
--------------------------------------------------------------------------------
1 | namespace TGIT.ACME.Protocol.HttpModel
2 | {
3 | ///
4 | /// Defines an identifier as used in orders or authorizations
5 | ///
6 | public class Identifier
7 | {
8 | public Identifier(Model.Identifier model)
9 | {
10 | if (model is null)
11 | throw new System.ArgumentNullException(nameof(model));
12 |
13 | Type = model.Type;
14 | Value = model.Value;
15 | }
16 |
17 | public string Type { get; }
18 | public string Value { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/Exceptions/AcmeException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TGIT.ACME.Protocol.Model.Exceptions
4 | {
5 | public abstract class AcmeException : Exception
6 | {
7 | protected AcmeException(string message)
8 | : base(message) { }
9 |
10 | public string UrnBase { get; protected set; } = "urn:ietf:params:acme:error";
11 | public abstract string ErrorType { get; }
12 |
13 | public virtual HttpModel.AcmeError GetHttpError()
14 | {
15 | return new HttpModel.AcmeError($"{UrnBase}:{ErrorType}", Message);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ACME.Server/Controllers/NonceController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.AspNetCore.Mvc;
3 | using TGIT.ACME.Server.Filters;
4 |
5 | namespace TGIT.ACME.Server.Controllers
6 | {
7 | [ApiController]
8 | [AddNextNonce]
9 | public class NonceController : ControllerBase
10 | {
11 | [Route("/new-nonce", Name = "NewNonce")]
12 | [HttpGet, HttpHead]
13 | public ActionResult GetNewNonce()
14 | {
15 | if (HttpMethods.IsGet(HttpContext.Request.Method))
16 | return NoContent();
17 | else
18 | return Ok();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Storage/IOrderStore.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using TGIT.ACME.Protocol.Model;
5 |
6 | namespace TGIT.ACME.Protocol.Storage
7 | {
8 | public interface IOrderStore
9 | {
10 | Task LoadOrderAsync(string orderId, CancellationToken cancellationToken);
11 |
12 | Task SaveOrderAsync(Order order, CancellationToken cancellationToken);
13 |
14 | Task> GetValidatableOrders(CancellationToken cancellationToken);
15 | Task> GetFinalizableOrders(CancellationToken cancellationToken);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/AuthorizationStatusExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace TGIT.ACME.Protocol.Model
4 | {
5 | public static class AuthorizationStatusExtensions
6 | {
7 | private static readonly AuthorizationStatus[] _invalidStatus = new[]
8 | {
9 | AuthorizationStatus.Invalid,
10 | AuthorizationStatus.Deactivated,
11 | AuthorizationStatus.Expired,
12 | AuthorizationStatus.Revoked
13 | };
14 |
15 | public static bool IsInvalid(this AuthorizationStatus status)
16 | {
17 | return _invalidStatus.Contains(status);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/HttpModel-Initialization/Identifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Xunit;
5 |
6 | namespace TGIT.ACME.Protocol.Model.Tests.HttpModel_Initialization
7 | {
8 | public class Identifier
9 | {
10 | [Fact]
11 | public void Ctor_Intializes_All_Properties()
12 | {
13 | var identifier = new Model.Identifier("dns", "www.example.com");
14 | var sut = new HttpModel.Identifier(identifier);
15 |
16 | Assert.Equal(identifier.Type, sut.Type);
17 | Assert.Equal(identifier.Value, sut.Value);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/Model/CryptoString.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.IdentityModel.Tokens;
2 |
3 | namespace TGIT.ACME.Protocol.Model
4 | {
5 | public class CryptoString
6 | {
7 | private CryptoString(int byteCount)
8 | {
9 | var bytes = new byte[byteCount];
10 |
11 | using (var cryptoRng = System.Security.Cryptography.RandomNumberGenerator.Create())
12 | cryptoRng.GetBytes(bytes);
13 |
14 | Value = Base64UrlEncoder.Encode(bytes);
15 | }
16 |
17 | private string Value { get; }
18 | public static string NewValue(int byteCount = 48) => new CryptoString(byteCount).Value;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [glatzert]
4 | #patreon: # Replace with a single Patreon username
5 | #open_collective: # Replace with a single Open Collective username
6 | #ko_fi: # Replace with a single Ko-fi username
7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | #liberapay: # Replace with a single Liberapay username
10 | #issuehunt: # Replace with a single IssueHunt username
11 | #otechie: # Replace with a single Otechie username
12 | #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Directory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TGIT.ACME.Protocol.HttpModel
4 | {
5 | ///
6 | /// Describes the HTTP-Response-Model for an ACME Directory
7 | /// https://tools.ietf.org/html/rfc8555#section-7.1.1
8 | ///
9 | public class Directory
10 | {
11 | public string? NewNonce { get; set; }
12 | public string? NewAccount { get; set; }
13 | public string? NewOrder { get; set; }
14 | public string? NewAuthz { get; set; }
15 | public string? RevokeCert { get; set; }
16 | public string? KeyChange { get; set; }
17 |
18 | public DirectoryMetadata? Meta { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/ACME.Storage.FileStore/JsonDefaults.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Converters;
3 |
4 | namespace TGIT.ACME.Storage.FileStore
5 | {
6 | internal static class JsonDefaults
7 | {
8 | static JsonDefaults()
9 | {
10 | var settings = new JsonSerializerSettings
11 | {
12 | PreserveReferencesHandling = PreserveReferencesHandling.All,
13 | NullValueHandling = NullValueHandling.Include,
14 | };
15 |
16 | settings.Converters.Add(new StringEnumConverter());
17 | Settings = settings;
18 | }
19 |
20 | public static readonly JsonSerializerSettings Settings;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/ACME.Protocol.Model.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/ACME.Protocol.Model.Tests/StaticTestData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace TGIT.ACME.Protocol.Model.Tests
6 | {
7 | static class StaticTestData
8 | {
9 | public static readonly string JwkJson = @"{ ""kty"" : ""RSA"", ""kid"" : ""cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df"", ""n"" : ""pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w"", ""e"" : ""AQAB"" }";
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Abstractions/Services/IAccountService.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using TGIT.ACME.Protocol.Model;
5 |
6 | namespace TGIT.ACME.Protocol.Services
7 | {
8 | public interface IAccountService
9 | {
10 | Task CreateAccountAsync(Jwk jwk, List? contact,
11 | bool termsOfServiceAgreed, CancellationToken cancellationToken);
12 |
13 | Task FindAccountAsync(Jwk jwk, CancellationToken cancellationToken);
14 |
15 | Task LoadAcountAsync(string accountId, CancellationToken cancellationToken);
16 |
17 | Task FromRequestAsync(CancellationToken cancellationToken);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/ACME.Protocol.Model/HttpModel/Converters/JwkConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 | using TGIT.ACME.Protocol.Model;
5 |
6 | namespace TGIT.ACME.Protocol.HttpModel.Converters
7 | {
8 | public class JwkConverter : JsonConverter
9 | {
10 | public override Jwk Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
11 | {
12 | var jwkJson = JsonSerializer.Deserialize