├── src ├── examples │ ├── ACMEKestrel │ │ ├── wwwroot │ │ │ ├── js │ │ │ │ ├── site.min.js │ │ │ │ └── site.js │ │ │ ├── favicon.ico │ │ │ ├── lib │ │ │ │ ├── bootstrap │ │ │ │ │ ├── dist │ │ │ │ │ │ ├── fonts │ │ │ │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ │ │ │ └── js │ │ │ │ │ │ │ └── npm.js │ │ │ │ │ ├── .bower.json │ │ │ │ │ └── LICENSE │ │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ │ ├── .bower.json │ │ │ │ │ └── LICENSE.txt │ │ │ │ ├── jquery │ │ │ │ │ ├── .bower.json │ │ │ │ │ └── LICENSE.txt │ │ │ │ └── jquery-validation │ │ │ │ │ ├── .bower.json │ │ │ │ │ └── LICENSE.md │ │ │ └── css │ │ │ │ ├── site.min.css │ │ │ │ └── site.css │ │ ├── Pages │ │ │ ├── _ViewStart.cshtml │ │ │ ├── _ViewImports.cshtml │ │ │ ├── Privacy.cshtml │ │ │ ├── About.cshtml │ │ │ ├── Privacy.cshtml.cs │ │ │ ├── Index.cshtml.cs │ │ │ ├── Contact.cshtml.cs │ │ │ ├── About.cshtml.cs │ │ │ ├── Contact.cshtml │ │ │ ├── Error.cshtml.cs │ │ │ ├── Error.cshtml │ │ │ └── Shared │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ └── _CookieConsentPartial.cshtml │ │ ├── appsettings.json │ │ ├── appsettings.Development.json │ │ ├── Crypto │ │ │ ├── ArchiveFormat.cs │ │ │ └── EncodingFormat.cs │ │ ├── ACMEKestrel.csproj │ │ ├── AcmeOptions.cs │ │ ├── AcmeState.cs │ │ ├── AcmeExtensions.cs │ │ ├── Program.cs │ │ ├── AcmeHttp01ChallengeHandler.cs │ │ └── Startup.cs │ ├── ACMEBlazor │ │ ├── global.json │ │ ├── Pages │ │ │ ├── _ViewImports.cshtml │ │ │ ├── Index.cshtml │ │ │ ├── Counter.cshtml │ │ │ ├── FetchData.cshtml │ │ │ ├── TryBlazorDB.cshtml │ │ │ └── Orders.cshtml │ │ ├── wwwroot │ │ │ ├── css │ │ │ │ ├── open-iconic │ │ │ │ │ ├── font │ │ │ │ │ │ └── fonts │ │ │ │ │ │ │ ├── open-iconic.eot │ │ │ │ │ │ │ ├── open-iconic.otf │ │ │ │ │ │ │ ├── open-iconic.ttf │ │ │ │ │ │ │ └── open-iconic.woff │ │ │ │ │ └── ICON-LICENSE │ │ │ │ └── site.css │ │ │ ├── index.html │ │ │ └── sample-data │ │ │ │ └── weather.json │ │ ├── _ViewImports.cshtml │ │ ├── Shared │ │ │ ├── MainLayout.cshtml │ │ │ ├── SurveyPrompt.cshtml │ │ │ └── NavMenu.cshtml │ │ ├── App.cshtml │ │ ├── Storage │ │ │ ├── Context.cs │ │ │ ├── BlazorOrder.cs │ │ │ ├── BlazorAccount.cs │ │ │ └── BlazorAuthorization.cs │ │ ├── Services │ │ │ └── Repository.cs │ │ ├── linker.xml │ │ ├── BlazorStorageExtensions.cs │ │ ├── AppState.cs │ │ ├── ACMEBlazor.csproj │ │ ├── PkiJwsTool.cs │ │ └── Program.cs │ ├── ACMEForms │ │ ├── App.config │ │ ├── Properties │ │ │ ├── Settings.settings │ │ │ ├── Settings.Designer.cs │ │ │ └── AssemblyInfo.cs │ │ ├── AppState.cs │ │ ├── Storage │ │ │ ├── DbAuthz.cs │ │ │ ├── DbOrder.cs │ │ │ ├── DbAccount.cs │ │ │ └── Repository.cs │ │ ├── Program.cs │ │ └── packages.config │ ├── Examples.Common │ │ ├── Examples.Common.csproj │ │ └── DnsUtil.cs │ ├── Examples.Common.PKI │ │ ├── Examples.Common.PKI.csproj │ │ ├── ExamplesAccountKey.cs │ │ └── PkiJwsTool.cs │ ├── ACMETerm │ │ └── ACMETerm.csproj │ ├── ACMECLI │ │ ├── ACMECLI.csproj │ │ └── HttpUtil.cs │ ├── ACMEBlazor.sln │ └── README.md ├── ACMESharp.MockServer │ ├── appsettings.json │ ├── Storage │ │ ├── DbNonce.cs │ │ ├── DbAccount.cs │ │ ├── DbChallenge.cs │ │ ├── DbAuthorization.cs │ │ ├── DbOrder.cs │ │ ├── DbCertificate.cs │ │ └── IRepository.cs │ ├── appsettings.Development.json │ ├── INonceManager.cs │ ├── ProtectedHeader.cs │ ├── ACMESharp.MockServer.csproj │ ├── Program.cs │ ├── RepoNonceManager.cs │ ├── Controllers │ │ ├── ValuesController.cs │ │ └── AcmeDirectoryController.cs │ └── Startup.cs ├── ACMESharp │ ├── Authorizations │ │ ├── IChallengeValidationDetails.cs │ │ ├── TlsAlpn01ChallengeValidationDetails.cs │ │ ├── Dns01ChallengeValidationDetails.cs │ │ └── Http01ChallengeValidationDetails.cs │ ├── _Assembly.cs │ ├── AcmeAuthorization.cs │ ├── Protocol │ │ ├── Resources │ │ │ ├── Identifier.cs │ │ │ ├── Problem.cs │ │ │ ├── RevokeReason.cs │ │ │ ├── Authorization.cs │ │ │ ├── ProblemType.cs │ │ │ ├── Account.cs │ │ │ ├── Order.cs │ │ │ ├── Challenge.cs │ │ │ └── ServiceDirectory.cs │ │ ├── Messages │ │ │ ├── CheckAccountRequest.cs │ │ │ ├── DeactivateAccountRequest.cs │ │ │ ├── DeactivateAuthorizationRequest.cs │ │ │ ├── FinalizeOrderRequest.cs │ │ │ ├── KeyChangeRequest.cs │ │ │ ├── RevokeCertificateRequest.cs │ │ │ ├── CreateOrderRequest.cs │ │ │ ├── CreateAccountRequest.cs │ │ │ └── UpdateAccountRequest.cs │ │ ├── OrderDetails.cs │ │ ├── AccountDetails.cs │ │ ├── AcmeProtocolException.cs │ │ └── Constants.cs │ ├── AcmeOrder.cs │ ├── Crypto │ │ ├── JOSE │ │ │ ├── IJwsTool.cs │ │ │ ├── JwsSignedPayload.cs │ │ │ └── JwsHeaders.cs │ │ ├── CryptoHelper.cs │ │ ├── EcTool.cs │ │ ├── Base64Tool.cs │ │ └── RsaTool.cs │ ├── Logging │ │ └── NullLogger.cs │ ├── ACMESharp.csproj │ ├── HTTP │ │ └── Link.cs │ ├── README.md │ └── AcmeAccount.cs ├── PKISharp.SimplePKI │ ├── PkiHashAlgorithm.cs │ ├── PkiEncodingFormat.cs │ ├── PkiArchiveFormat.cs │ ├── PkiAsymmetricAlgorithm.cs │ ├── PKISharp.SimplePKI.csproj │ ├── PkiCertificateExtension.cs │ └── Util │ │ └── Base64Tool.cs └── ACMESharp.DotNetCore │ ├── EcToolExtensions.cs │ ├── ACMESharp.DotNetCore.csproj │ └── RsaToolExtensions.cs ├── .gitattributes ├── docs ├── acmesharp-art.vsdx ├── acmesharp-greys.png ├── acmesharp-logo-color.png ├── pkisharp-logo-color.png └── pkisharp-logo-greys.png ├── tools ├── appveyor-connect.template.rdp ├── test-report │ ├── example.out │ ├── example.xml │ ├── example.xsl │ ├── sample-test-results.ps1 │ └── trx2md.ps1 ├── chocoInstallDotNetCore21Rc1.ps1 └── appveyor-connect.ps1 ├── test ├── ACMESharp.IntegrationTests │ ├── run-test-class.ps1 │ ├── config │ │ ├── template-R53Helper.json │ │ └── template-S3Helper.json │ ├── _Assembly.cs │ ├── XunitDebugging │ │ ├── MsBuild.cs │ │ ├── import.targets │ │ ├── ArgParser.cs │ │ └── DictionaryExtensions.cs │ ├── Constants.cs │ ├── AwsFixture.cs │ ├── ACMESharp.IntegrationTests.csproj │ ├── ClientsFixture.cs │ ├── StateFixture.cs │ └── S3Helper.cs ├── ACMESharp.Testing.Xunit │ ├── ACMESharp.Testing.Xunit.csproj │ ├── TestCollectionDependencyAttribute.cs │ ├── TestDependencyAttribute.cs │ └── TestOrderAttribute.cs ├── PKISharp.SimplePKI.UnitTests │ ├── OpenSsl.cs │ └── PKISharp.SimplePKI.UnitTests.csproj ├── ACMESharp.UnitTests │ ├── ACMESharp.UnitTests.csproj │ └── JwsHelperTests.cs └── ACMESharp.MockServer.UnitTests │ └── ACMESharp.MockServer.UnitTests.csproj ├── version.props ├── .vscode ├── tasks.json └── settings.json ├── azure-pipelines.yml ├── LICENSE └── .editorconfig /src/examples/ACMEKestrel/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | ## enforce common line ending 3 | * text=auto -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.300" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/acmesharp-art.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/docs/acmesharp-art.vsdx -------------------------------------------------------------------------------- /docs/acmesharp-greys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/docs/acmesharp-greys.png -------------------------------------------------------------------------------- /docs/acmesharp-logo-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/docs/acmesharp-logo-color.png -------------------------------------------------------------------------------- /docs/pkisharp-logo-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/docs/pkisharp-logo-color.png -------------------------------------------------------------------------------- /docs/pkisharp-logo-greys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/docs/pkisharp-logo-greys.png -------------------------------------------------------------------------------- /tools/appveyor-connect.template.rdp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/tools/appveyor-connect.template.rdp -------------------------------------------------------------------------------- /tools/test-report/example.out: -------------------------------------------------------------------------------- 1 | 2 | Article - My Article 3 | Authors (99): 4 | - Mr. Foo 5 | - Mr. Bar -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEKestrel/wwwroot/favicon.ico -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/run-test-class.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/test/ACMESharp.IntegrationTests/run-test-class.ps1 -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using ACMEKestrel 2 | @namespace ACMEKestrel.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @layout MainLayout 2 | @using ACMESharp.Protocol; 3 | @using ACMESharp.Protocol.Resources; 4 | @using ACMESharp.Authorizations; 5 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 |

Hello, world!

4 | 5 | Welcome to your new app. 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEBlazor/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/config/template-R53Helper.json: -------------------------------------------------------------------------------- 1 | { 2 | "AwsProfileName": "acmesharp-tests", 3 | "AwsRegion": "us-east-1", 4 | "HostedZoneId": "@@TEST_HOSTED_ZONE_ID@@" 5 | } 6 | -------------------------------------------------------------------------------- /src/ACMESharp/Authorizations/IChallengeValidationDetails.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Authorizations 2 | { 3 | public interface IChallengeValidationDetails 4 | { 5 | string ChallengeType { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/DbNonce.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.MockServer.Storage 2 | { 3 | public class DbNonce 4 | { 5 | public int Id { get; set; } 6 | 7 | public string Nonce { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

-------------------------------------------------------------------------------- /src/ACMESharp.MockServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/examples/ACMEForms/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKISharp/ACMESharpCore/HEAD/src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/ACMESharp/_Assembly.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | // Expose private members to "friend" testing assemblies 4 | [assembly: InternalsVisibleTo("ACMESharp.UnitTests")] 5 | [assembly: InternalsVisibleTo("ACMESharp.IntegrationTests")] -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/PkiHashAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace PKISharp.SimplePKI 2 | { 3 | public enum PkiHashAlgorithm 4 | { 5 | Unknown = 0, 6 | 7 | Sha256 = 10, 8 | Sha384 = 11, 9 | Sha512 = 12, 10 | } 11 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/About.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AboutModel 3 | @{ 4 | ViewData["Title"] = "About"; 5 | } 6 |

@ViewData["Title"]

7 |

@Model.Message

8 | 9 |

Use this area to provide additional information.

10 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Crypto/ArchiveFormat.cs: -------------------------------------------------------------------------------- 1 | namespace ACMEKestrel.Crypto 2 | { 3 | public enum ArchiveFormat 4 | { 5 | /// 6 | /// The PCKS#12 (.PFX) format. 7 | /// 8 | PKCS12 = 3, 9 | } 10 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Blazor.Layouts 3 | @using Microsoft.AspNetCore.Blazor.Routing 4 | @using Blazor.Extensions 5 | @using ACMEBlazor 6 | @using ACMEBlazor.Shared 7 | @using ACMEBlazor.Storage 8 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your Javascript code. 5 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/INonceManager.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.MockServer 2 | { 3 | public interface INonceManager 4 | { 5 | string GenerateNonce(); 6 | 7 | bool ValidateNonce(string nonce); 8 | 9 | bool PeekNonce(string nonce); 10 | } 11 | } -------------------------------------------------------------------------------- /src/examples/ACMEForms/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/config/template-S3Helper.json: -------------------------------------------------------------------------------- 1 | { 2 | "AwsProfileName": "acmesharp-tests", 3 | "AwsRegion": "us-east-1", 4 | "BucketName": "@@TEST_BUCKET_NAME@@" 5 | 6 | // This seems to break things with a "BadRequest" response 7 | //,"CannedAcl": "PublicRead" 8 | } 9 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /tools/test-report/example.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | My Article 5 | 6 | Mr. Foo 7 | Mr. Bar 8 | 9 | This is my article text. 10 |
11 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/DbAccount.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol; 2 | 3 | namespace ACMESharp.MockServer.Storage 4 | { 5 | public class DbAccount 6 | { 7 | public int Id { get; set; } 8 | 9 | public string Jwk { get; set; } 10 | 11 | public AccountDetails Details { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ACMESharp/AcmeAuthorization.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol.Resources; 2 | 3 | namespace ACMESharp 4 | { 5 | public class AcmeAuthorization 6 | { 7 | public string DetailsUrl { get; set; } 8 | 9 | public string FetchError { get; set; } 10 | 11 | public Authorization Details { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/DbChallenge.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol.Resources; 2 | 3 | namespace ACMESharp.MockServer.Storage 4 | { 5 | public class DbChallenge 6 | { 7 | public int Id { get; set; } 8 | 9 | public int AuthorizationId { get; set; } 10 | 11 | public Challenge Payload { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Pages/Counter.cshtml: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 |

Counter

4 | 5 |

Current count: @currentCount

6 | 7 | 8 | 9 | @functions { 10 | int currentCount = 0; 11 | 12 | void IncrementCount() 13 | { 14 | currentCount++; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(APPVEYOR_BUILD_NUMBER) 4 | 0 5 | 2.2.0.$(BuildNumber) 6 | 9 | 10 | -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/PkiEncodingFormat.cs: -------------------------------------------------------------------------------- 1 | namespace PKISharp.SimplePKI 2 | { 3 | public enum PkiEncodingFormat 4 | { 5 | Unknown = 0, 6 | 7 | /// 8 | /// DER binary encoding. 9 | /// 10 | Der = 1, 11 | 12 | /// 13 | /// PEM text encoding. 14 | /// 15 | Pem = 2, 16 | } 17 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Shared/MainLayout.cshtml: -------------------------------------------------------------------------------- 1 | @inherits BlazorLayoutComponent 2 | 3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 | -------------------------------------------------------------------------------- /src/examples/Examples.Common/Examples.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/DbAuthorization.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol.Resources; 2 | 3 | namespace ACMESharp.MockServer.Storage 4 | { 5 | public class DbAuthorization 6 | { 7 | public int Id { get; set; } 8 | 9 | public int OrderId { get; set; } 10 | 11 | public string Url { get; set; } 12 | 13 | public Authorization Payload { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/PkiArchiveFormat.cs: -------------------------------------------------------------------------------- 1 | namespace PKISharp.SimplePKI 2 | { 3 | public enum PkiArchiveFormat 4 | { 5 | Unknown = 0, 6 | 7 | /// 8 | /// The PKCS#12 (.PFX) format. 9 | /// 10 | Pkcs12 = 3, 11 | 12 | /// 13 | /// PEM-encoded archive format. 14 | /// 15 | Pem = 4, 16 | } 17 | } -------------------------------------------------------------------------------- /src/examples/ACMEForms/AppState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Protocol; 7 | 8 | namespace ACMEForms 9 | { 10 | public class AppState 11 | { 12 | public AccountDetails Account { get; set; } 13 | 14 | public OrderDetails[] Orders { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/src/ACMESharp.CLI/ACMESharp.CLI.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/DbOrder.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol; 2 | using ACMESharp.Protocol.Resources; 3 | 4 | namespace ACMESharp.MockServer.Storage 5 | { 6 | public class DbOrder 7 | { 8 | public int Id { get; set; } 9 | 10 | public int AccountId { get; set; } 11 | 12 | public string Url { get; set; } 13 | 14 | public OrderDetails Details { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace ACMEKestrel.Pages 9 | { 10 | public class PrivacyModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace ACMEKestrel.Pages 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/examples/Examples.Common.PKI/Examples.Common.PKI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/ProtectedHeader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ACMESharp.MockServer 4 | { 5 | public class ProtectedHeader 6 | { 7 | public string Alg { get; set; } 8 | 9 | public string Kid { get; set; } 10 | 11 | public string Url { get; set; } 12 | 13 | public string Nonce { get; set; } 14 | 15 | public Dictionary Jwk { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/App.cshtml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | @functions { 8 | 9 | protected override async Task OnInitAsync() 10 | { 11 | Console.WriteLine("App is starting up..."); 12 | 13 | await Task.CompletedTask; 14 | } 15 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Storage/Context.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ACMESharp.Protocol; 6 | using BlazorDB; 7 | 8 | namespace ACMEBlazor.Storage 9 | { 10 | public class Context : StorageContext 11 | { 12 | public StorageSet Accounts { get; set; } 13 | public StorageSet Orders { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Contact.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace ACMEKestrel.Pages 8 | { 9 | public class ContactModel : PageModel 10 | { 11 | public string Message { get; set; } 12 | 13 | public void OnGet() 14 | { 15 | Message = "Your contact page."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/_Assembly.cs: -------------------------------------------------------------------------------- 1 | 2 | using ACMESharp.Testing.Xunit; 3 | using Xunit; 4 | 5 | [assembly: TestCaseOrderer(TestOrderer.TypeName, TestOrderer.AssemblyName)] 6 | [assembly: TestCollectionOrderer(TestOrderer.TypeName, TestOrderer.AssemblyName)] 7 | 8 | [assembly: CollectionBehavior( 9 | CollectionBehavior.CollectionPerClass 10 | //CollectionBehavior.CollectionPerAssembly 11 | //,MaxParallelThreads = n 12 | ,DisableTestParallelization = true 13 | )] 14 | -------------------------------------------------------------------------------- /test/ACMESharp.Testing.Xunit/ACMESharp.Testing.Xunit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ACMEBlazor 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/Identifier.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Protocol.Resources 5 | { 6 | public class Identifier 7 | { 8 | [JsonProperty("type", Required = Required.Always)] 9 | [Required] 10 | public string Type { get; set; } 11 | 12 | [JsonProperty("value", Required = Required.Always)] 13 | [Required] 14 | public string Value { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/About.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace ACMEKestrel.Pages 8 | { 9 | public class AboutModel : PageModel 10 | { 11 | public string Message { get; set; } 12 | 13 | public void OnGet() 14 | { 15 | Message = "Your application description page."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET Core 2 | # Build and test ASP.NET Core projects targeting .NET Core. 3 | # Add steps that run tests, create a NuGet package, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'Ubuntu-16.04' 11 | 12 | variables: 13 | buildConfiguration: 'Release' 14 | 15 | steps: 16 | - script: dotnet build --configuration $(buildConfiguration) 17 | displayName: 'dotnet build $(buildConfiguration)' 18 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Crypto/EncodingFormat.cs: -------------------------------------------------------------------------------- 1 | namespace ACMEKestrel.Crypto 2 | { 3 | public enum EncodingFormat 4 | { 5 | /// 6 | /// Format encoding suitable for human-friendly printing. 7 | /// 8 | PRINT = 0, 9 | 10 | /// 11 | /// PEM text encoding. 12 | /// 13 | PEM = 1, 14 | 15 | /// 16 | /// DER binary encoding. 17 | /// 18 | DER = 2, 19 | } 20 | } -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/XunitDebugging/MsBuild.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace ACMESharp.IntegrationTests.Debugging 4 | { 5 | internal static class MsBuild 6 | { 7 | public static string MsBuildName { get; } 8 | 9 | static MsBuild() 10 | { 11 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 12 | MsBuildName = "MSBuild.exe"; 13 | else 14 | MsBuildName = "msbuild"; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Services/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ACMESharp.Protocol; 6 | 7 | namespace ACMEBlazor.Services 8 | { 9 | public interface IRepository 10 | { 11 | //Task GetAccount(); 12 | } 13 | 14 | public class Repository : IRepository 15 | { 16 | //public async Task GetAccount() 17 | //{ 18 | // return null; 19 | //} 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/CheckAccountRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ACMESharp.Crypto.JOSE; 3 | using Newtonsoft.Json; 4 | 5 | namespace ACMESharp.Protocol.Messages 6 | { 7 | /// 8 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 9 | /// 10 | public class CheckAccountRequest 11 | { 12 | [JsonProperty("onlyReturnExisting", NullValueHandling=NullValueHandling.Ignore)] 13 | public bool OnlyReturnExisting { get => true; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/DeactivateAccountRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ACMESharp.Crypto.JOSE; 3 | using Newtonsoft.Json; 4 | 5 | namespace ACMESharp.Protocol.Messages 6 | { 7 | /// 8 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 9 | /// 10 | public class DeactivateAccountRequest 11 | { 12 | [JsonProperty("status", NullValueHandling=NullValueHandling.Ignore)] 13 | public string Status { get => "deactivated"; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/DeactivateAuthorizationRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ACMESharp.Crypto.JOSE; 3 | using Newtonsoft.Json; 4 | 5 | namespace ACMESharp.Protocol.Messages 6 | { 7 | /// 8 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.5.2 9 | /// 10 | public class DeactivateAuthorizationRequest 11 | { 12 | [JsonProperty("status", NullValueHandling=NullValueHandling.Ignore)] 13 | public string Status { get => "deactivated"; } 14 | } 15 | } -------------------------------------------------------------------------------- /test/ACMESharp.Testing.Xunit/TestCollectionDependencyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ACMESharp.Testing.Xunit 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public sealed class TestCollectionDependencyAttribute : Attribute 7 | { 8 | public TestCollectionDependencyAttribute(Type @class) 9 | { 10 | Class = @class; 11 | } 12 | 13 | /// 14 | /// The test class (Collection) that is a dependency. 15 | /// 16 | public Type Class { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /tools/test-report/example.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Article - <b></b> 8 | Authors (): 9 | 10 | 11 | 12 | - 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/DbCertificate.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol.Resources; 2 | 3 | namespace ACMESharp.MockServer.Storage 4 | { 5 | public class DbCertificate 6 | { 7 | public int Id { get; set; } 8 | 9 | public int OrderId { get; set; } 10 | 11 | public string CertKey { get; set; } 12 | 13 | public RevokeReason? RevokedReason {get; set; } 14 | 15 | public string Pem { get; set; } 16 | 17 | public byte[] Native { get; set; } 18 | 19 | public string Thumbprint { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/FinalizeOrderRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ACMESharp.Crypto.JOSE; 3 | using ACMESharp.Protocol.Resources; 4 | using Newtonsoft.Json; 5 | 6 | namespace ACMESharp.Protocol.Messages 7 | { 8 | /// 9 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 10 | /// 11 | public class FinalizeOrderRequest 12 | { 13 | [JsonProperty("csr", Required = Required.Always)] 14 | [Required] 15 | public string Csr { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ContactModel 3 | @{ 4 | ViewData["Title"] = "Contact"; 5 | } 6 |

@ViewData["Title"]

7 |

@Model.Message

8 | 9 |
10 | One Microsoft Way
11 | Redmond, WA 98052-6399
12 | P: 13 | 425.555.0100 14 |
15 | 16 |
17 | Support: Support@example.com
18 | Marketing: Marketing@example.com 19 |
20 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 4 | "version": "3.2.9", 5 | "_release": "3.2.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v3.2.9", 9 | "commit": "a91f5401898e125f10771c5f5f0909d8c4c82396" 10 | }, 11 | "_source": "https://github.com/aspnet/jquery-validation-unobtrusive.git", 12 | "_target": "^3.2.9", 13 | "_originalSource": "jquery-validation-unobtrusive", 14 | "_direct": true 15 | } -------------------------------------------------------------------------------- /src/examples/ACMETerm/ACMETerm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | LATEST 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/ACMESharp/AcmeOrder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ACMESharp.Protocol.Resources; 4 | 5 | namespace ACMESharp 6 | { 7 | public class AcmeOrder 8 | { 9 | public string OrderUrl { get; set; } 10 | 11 | public string Status { get; set; } 12 | 13 | public DateTime Expires { get; set; } 14 | 15 | public string[] DnsIdentifiers { get; set; } 16 | 17 | public IEnumerable Authorizations { get; set; } 18 | 19 | public string FinalizeUrl { get; set; } 20 | 21 | public string CertificateUrl { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/Problem.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Protocol.Resources 5 | { 6 | public class Problem 7 | { 8 | public const string StandardProblemTypeNamespace = "urn:ietf:params:acme:error:"; 9 | 10 | [JsonProperty("type", Required = Required.Always)] 11 | [Required] 12 | public string Type { get; set; } 13 | 14 | [JsonProperty("detail")] 15 | public string Detail { get; set; } 16 | 17 | [JsonProperty("status")] 18 | public int? Status { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Shared/SurveyPrompt.cshtml: -------------------------------------------------------------------------------- 1 | 13 | 14 | @functions { 15 | [Parameter] 16 | string Title { get; set; } // Demonstrates how a parent component can supply parameters 17 | } 18 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/linker.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/RevokeReason.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Protocol.Resources 2 | { 3 | /// 4 | /// Reasons for revocation 5 | /// https://tools.ietf.org/html/rfc5280#section-5.3.1 6 | /// 7 | public enum RevokeReason 8 | { 9 | Unspecified = 0, 10 | KeyCompromise = 1, 11 | CaCompromise = 2, 12 | AffiliationChanged = 3, 13 | Superseded = 4, 14 | CessationOfOperation = 5, 15 | CertificateHold = 6, 16 | /*Value 7 is not used*/ 17 | RemoveFromCrl = 8, 18 | PrivilegeWithdrawn = 9, 19 | AaCompromise = 10 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/KeyChangeRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Protocol.Messages 5 | { 6 | /// 7 | /// Based on: 8 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.3.5 9 | /// 10 | public class KeyChangeRequest 11 | { 12 | [JsonProperty("account", Required = Required.Always)] 13 | [Required] 14 | public string Account { get; set; } 15 | 16 | [JsonProperty("oldKey", Required = Required.Always)] 17 | [Required] 18 | public object OldKey { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/ACMESharp.MockServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/ACMESharp/Authorizations/TlsAlpn01ChallengeValidationDetails.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Authorizations 2 | { 3 | /// 4 | /// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05 5 | /// 6 | public class TlsAlpn01ChallengeValidationDetails : IChallengeValidationDetails 7 | { 8 | public const string TlsAlpn01ChallengeType = "tls-alpn-01"; 9 | public const string AlpnExtensionName = "acme-tls/1"; 10 | public const string AcmeIdentifierExtension = "acmeIdentifier"; 11 | 12 | public string ChallengeType => TlsAlpn01ChallengeType; 13 | 14 | public string TokenValue { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.IntegrationTests 2 | { 3 | public class Constants 4 | { 5 | /// https://letsencrypt.org/docs/staging-environment/ 6 | /// https://letsencrypt.status.io/ 7 | 8 | public const string LetsEncryptStagingEndpoint = "https://acme-staging.api.letsencrypt.org/"; 9 | public const string LetsEncryptV2StagingEndpoint = "https://acme-staging-v02.api.letsencrypt.org/"; 10 | public const string LetsEncryptEndpoint = "https://acme-v01.api.letsencrypt.org/"; 11 | public const string LetsEncryptV2Endpoint = "https://acme-v02.api.letsencrypt.org/"; 12 | } 13 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/RevokeCertificateRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Newtonsoft.Json; 3 | using ACMESharp.Protocol.Resources; 4 | 5 | namespace ACMESharp.Protocol.Messages 6 | { 7 | /// 8 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-7.6 9 | /// 10 | public class RevokeCertificateRequest 11 | { 12 | [JsonProperty("certificate", Required = Required.Always)] 13 | [Required] 14 | public string Certificate { get; set; } 15 | 16 | [JsonProperty("reason")] 17 | public RevokeReason Reason { get; set; } = RevokeReason.Unspecified; 18 | } 19 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/OrderDetails.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol.Resources; 2 | 3 | namespace ACMESharp.Protocol 4 | { 5 | /// 6 | /// An aggregation of Order details including resource payload and ancillary, 7 | /// associated data. 8 | /// 9 | /// 10 | /// This represents a superset of details that are included in responses 11 | /// to several ACME operations regarding an ACME Order, such as 12 | /// Order creation and finalization. 13 | /// 14 | public class OrderDetails 15 | { 16 | public Order Payload { get; set; } 17 | 18 | public string OrderUrl { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /test/PKISharp.SimplePKI.UnitTests/OpenSsl.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | 4 | namespace PKISharp.SimplePKI.UnitTests 5 | { 6 | public static class OpenSsl 7 | { 8 | public const string OpenSslLightPath = @"C:\Program Files\OpenSSL\bin\openssl.exe"; 9 | 10 | public static Process Start(string arguments) 11 | { 12 | if (File.Exists(OpenSslLightPath)) 13 | { 14 | return Process.Start(OpenSslLightPath, arguments); 15 | } 16 | else 17 | { 18 | return Process.Start("openssl", arguments); 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /test/PKISharp.SimplePKI.UnitTests/PKISharp.SimplePKI.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/ACMEKestrel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/ACMESharp.Testing.Xunit/TestDependencyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ACMESharp.Testing.Xunit 4 | { 5 | [AttributeUsage(AttributeTargets.Method)] 6 | public sealed class TestDependencyAttribute : Attribute 7 | { 8 | /// the name of the test method (Fact) 9 | /// that is a dependency. 10 | public TestDependencyAttribute(string methodName) 11 | { 12 | MethodName = methodName; 13 | } 14 | 15 | /// 16 | /// The name of the test method (Fact) that is a dependency. 17 | /// 18 | public string MethodName { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/PkiAsymmetricAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace PKISharp.SimplePKI 2 | { 3 | public enum PkiAsymmetricAlgorithm 4 | { 5 | Unknown = 0, 6 | 7 | /// 8 | /// RSA (Rivest–Shamir–Adleman) is one of the first public-key cryptosystems 9 | /// and is widely used for secure data transmission. 10 | /// 11 | Rsa = 1, 12 | 13 | /// 14 | /// The Elliptic Curve Digital Signature Algorithm (ECDSA) offers a variant 15 | /// of the Digital Signature Algorithm (DSA) which uses elliptic curve 16 | /// cryptography. 17 | /// 18 | Ecdsa = 2, 19 | } 20 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "3.3.1", 16 | "_release": "3.3.1", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "3.3.1", 20 | "commit": "9e8ec3d10fad04748176144f108d7355662ae75e" 21 | }, 22 | "_source": "https://github.com/jquery/jquery-dist.git", 23 | "_target": "^3.3.1", 24 | "_originalSource": "jquery", 25 | "_direct": true 26 | } -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/JOSE/IJwsTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ACMESharp.Crypto.JOSE 5 | { 6 | /// 7 | /// Defines the interface for a tool that provides the required 8 | /// JOSE Web Signature (JWS) functions as used by the ACME protocol. 9 | /// 10 | public interface IJwsTool : IDisposable 11 | { 12 | string JwsAlg 13 | { get; } 14 | 15 | void Init(); 16 | 17 | string Export(); 18 | 19 | void Import(string exported); 20 | 21 | object ExportJwk(bool canonical = false); 22 | 23 | byte[] Sign(byte[] raw); 24 | 25 | bool Verify(byte[] raw, byte[] sig); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Storage/BlazorOrder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Protocol; 7 | using ACMESharp.Protocol.Resources; 8 | 9 | namespace ACMEBlazor.Storage 10 | { 11 | public class BlazorOrder 12 | { 13 | public int Id { get; set; } 14 | 15 | public string DnsNames { get; set; } 16 | 17 | [Required] 18 | public BlazorAccount Account { get; set; } 19 | 20 | [Required] 21 | public OrderDetails Details { get; set; } 22 | 23 | public BlazorAuthorization[] Authorizations { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2018-05-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing", 6 | "temperatureF": 33 7 | }, 8 | { 9 | "date": "2018-05-07", 10 | "temperatureC": 14, 11 | "summary": "Bracing", 12 | "temperatureF": 57 13 | }, 14 | { 15 | "date": "2018-05-08", 16 | "temperatureC": -13, 17 | "summary": "Freezing", 18 | "temperatureF": 9 19 | }, 20 | { 21 | "date": "2018-05-09", 22 | "temperatureC": -16, 23 | "summary": "Balmy", 24 | "temperatureF": 4 25 | }, 26 | { 27 | "date": "2018-05-10", 28 | "temperatureC": -2, 29 | "summary": "Chilly", 30 | "temperatureF": 29 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/JOSE/JwsSignedPayload.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace ACMESharp.Crypto.JOSE 4 | { 5 | public class JwsSignedPayload 6 | { 7 | [JsonProperty("header", NullValueHandling = NullValueHandling.Ignore)] 8 | public object Header 9 | { get; set; } 10 | 11 | [JsonProperty("protected", NullValueHandling = NullValueHandling.Ignore)] 12 | public string Protected 13 | { get; set; } 14 | 15 | [JsonProperty("payload", Required = Required.Always)] 16 | public string Payload 17 | { get; set; } 18 | 19 | [JsonProperty("signature", Required = Required.Always)] 20 | public string Signature 21 | { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/examples/ACMEForms/Storage/DbAuthz.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Authorizations; 7 | using ACMESharp.Protocol.Resources; 8 | 9 | namespace ACMEForms.Storage 10 | { 11 | public class DbAuthz 12 | { 13 | public int Id { get; set; } 14 | 15 | public string Url { get; set; } 16 | 17 | public Authorization Details { get; set; } 18 | 19 | public Dns01ChallengeValidationDetails DnsChallenge { get; set; } 20 | 21 | public Http01ChallengeValidationDetails HttpChallenge { get; set; } 22 | 23 | public Challenge[] MiscChallenges { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace ACMESharp.MockServer 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | namespace ACMEKestrel.Pages 10 | { 11 | public class ErrorModel : PageModel 12 | { 13 | public string RequestId { get; set; } 14 | 15 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 16 | 17 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 18 | public void OnGet() 19 | { 20 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/examples/ACMEForms/Storage/DbOrder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Protocol; 7 | using ACMESharp.Protocol.Resources; 8 | 9 | namespace ACMEForms.Storage 10 | { 11 | public class DbOrder 12 | { 13 | public int Id { get; set; } 14 | 15 | /// 16 | /// We cache the first Order URL we get because subsequent 17 | /// refreshes of the Order don't return it in the response. 18 | /// 19 | public string FirstOrderUrl { get; set; } 20 | 21 | public OrderDetails Details { get; set; } 22 | 23 | public DbAuthz[] Authorizations { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/AccountDetails.cs: -------------------------------------------------------------------------------- 1 | using ACMESharp.Protocol.Resources; 2 | 3 | namespace ACMESharp.Protocol 4 | { 5 | /// 6 | /// An aggregation of Account details including resource payload and ancillary, 7 | /// associated data. 8 | /// 9 | /// 10 | /// This represents a superset of details that are included in responses 11 | /// to several ACME operations regarding an ACME Account, such as Account 12 | /// registration, update, key rotation and deactivation. 13 | /// 14 | public class AccountDetails 15 | { 16 | public Account Payload { get; set; } 17 | 18 | public string Kid { get; set; } 19 | 20 | public string TosLink { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /src/ACMESharp/Authorizations/Dns01ChallengeValidationDetails.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Authorizations 2 | { 3 | /// 4 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-8.4 5 | /// 6 | public class Dns01ChallengeValidationDetails : IChallengeValidationDetails 7 | { 8 | public const string Dns01ChallengeType = "dns-01"; 9 | public const string DnsRecordNamePrefix = "_acme-challenge"; 10 | public const string DnsRecordTypeDefault = "TXT"; 11 | 12 | public string ChallengeType => Dns01ChallengeType; 13 | 14 | public string DnsRecordName { get; set; } 15 | 16 | public string DnsRecordType { get; set; } 17 | 18 | public string DnsRecordValue { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Storage/BlazorAccount.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Protocol; 7 | 8 | namespace ACMEBlazor.Storage 9 | { 10 | public class BlazorAccount 11 | { 12 | public int Id { get; set; } 13 | 14 | public string Contacts { get; set; } 15 | 16 | [Required] 17 | public string[] ContactEmails { get; set; } 18 | 19 | [Required] 20 | public DateTime? TosAgreed { get; set; } 21 | 22 | [Required] 23 | public string SignerExport { get; set; } 24 | 25 | [Required] 26 | public AccountDetails Details { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/ACMESharp.UnitTests/ACMESharp.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/BlazorStorageExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Blazor.Extensions; 6 | using Microsoft.AspNetCore.Blazor.Browser.Interop; 7 | 8 | namespace ACMEBlazor 9 | { 10 | // Temporary fix until this is merged in: 11 | // https://github.com/BlazorExtensions/Storage/pull/5 12 | public static class BlazorStorageExtensions 13 | { 14 | public const string LENGTH_METHOD = "Blazor.Extensions.Storage.Length"; 15 | public const string LOCAL_STORAGE = "localStorage"; 16 | 17 | public static int GetLength(this LocalStorage local) 18 | { 19 | return RegisteredFunction.Invoke(LENGTH_METHOD, LOCAL_STORAGE); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/examples/ACMEForms/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | using ACMEForms.Storage; 7 | using ACMESharp.Protocol; 8 | using LiteDB; 9 | 10 | namespace ACMEForms 11 | { 12 | static class Program 13 | { 14 | public static Repository Repo { get; private set; } 15 | 16 | /// 17 | /// The main entry point for the application. 18 | /// 19 | [STAThread] 20 | static void Main() 21 | { 22 | Repo = Repository.GetInstance(); 23 | 24 | Application.EnableVisualStyles(); 25 | Application.SetCompatibleTextRenderingDefault(false); 26 | Application.Run(new MainForm()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/AppState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ACMEBlazor.Storage; 6 | 7 | namespace ACMEBlazor 8 | { 9 | public class AppState 10 | { 11 | public static readonly string AccountKey = $"{nameof(ACMEBlazor)}-{nameof(BlazorAccount)}"; 12 | public static readonly string OrderKey = $"{nameof(ACMEBlazor)}-{nameof(BlazorOrder)}:"; 13 | 14 | public static readonly char[] LineSeps = "\r\n".ToCharArray(); 15 | 16 | public static readonly BlazorAccount[] EmptyAccounts = new BlazorAccount[0]; 17 | public static readonly BlazorOrder[] EmptyOrders = new BlazorOrder[0]; 18 | 19 | public BlazorAccount Account { get; set; } 20 | 21 | public BlazorOrder[] Orders { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Storage/BlazorAuthorization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Authorizations; 7 | using ACMESharp.Protocol.Resources; 8 | 9 | namespace ACMEBlazor.Storage 10 | { 11 | public class BlazorAuthorization 12 | { 13 | public int Id { get; set; } 14 | 15 | //[Required] 16 | //public BlazorOrder Order { get; set; } 17 | 18 | [Required] 19 | public string Url { get; set; } 20 | 21 | [Required] 22 | public Authorization Details { get; set; } 23 | 24 | public Dns01ChallengeValidationDetails DnsChallengeDetails { get; set; } 25 | 26 | public Http01ChallengeValidationDetails HttpChallengeDetails { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/CreateOrderRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ACMESharp.Crypto.JOSE; 3 | using ACMESharp.Protocol.Resources; 4 | using Newtonsoft.Json; 5 | 6 | namespace ACMESharp.Protocol.Messages 7 | { 8 | /// 9 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4 10 | /// 11 | public class CreateOrderRequest 12 | { 13 | [JsonProperty("identifiers", Required = Required.Always)] 14 | [Required, MinLength(1)] 15 | public Identifier[] Identifiers { get; set; } 16 | 17 | [JsonProperty("notBefore", NullValueHandling = NullValueHandling.Ignore)] 18 | public string NotBefore { get; set; } 19 | 20 | [JsonProperty("notAfter", NullValueHandling = NullValueHandling.Ignore)] 21 | public string NotAfter { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/examples/ACMECLI/ACMECLI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | LATEST 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "ACMECLI", 4 | "Algor", 5 | "CNAME", 6 | "Canonicalize", 7 | "ECDSA", 8 | "PKCS", 9 | "UPSERT", 10 | "Xunit", 11 | "acmesharp", 12 | "acmesharpcore", 13 | "authz", 14 | "bkkr", 15 | "blockrdp", 16 | "chlng", 17 | "choco", 18 | "dd'T'HH", 19 | "deserializes", 20 | "dpkg", 21 | "finalizer", 22 | "integtests", 23 | "ldconfig", 24 | "mailto", 25 | "mkdir", 26 | "msbuild", 27 | "myapp", 28 | "mycertificate", 29 | "nist", 30 | "nupkg", 31 | "openssl", 32 | "popd", 33 | "pushd", 34 | "pwsh", 35 | "requ", 36 | "stateful", 37 | "uncomment", 38 | "wasapl", 39 | "webclient", 40 | "zyborg" 41 | ] 42 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/Authorization.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Protocol.Resources 5 | { 6 | /// 7 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.4 8 | /// 9 | public class Authorization 10 | { 11 | [JsonProperty("identifier", Required = Required.Always)] 12 | [Required] 13 | public Identifier Identifier { get; set; } 14 | 15 | [JsonProperty("status", Required = Required.Always)] 16 | [Required] 17 | public string Status { get; set; } 18 | 19 | [JsonProperty("expires")] 20 | public string Expires { get; set; } 21 | 22 | [JsonProperty("challenges")] 23 | [Required] 24 | public Challenge[] Challenges { get; set; } 25 | 26 | [JsonProperty("wildcard")] 27 | public bool? Wildcard { get; set; } 28 | } 29 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to Development environment will display more detailed information about the error that occurred. 20 |

21 |

22 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 23 |

24 | -------------------------------------------------------------------------------- /src/ACMESharp/Authorizations/Http01ChallengeValidationDetails.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Authorizations 2 | { 3 | /// 4 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-8.3 5 | /// 6 | public class Http01ChallengeValidationDetails : IChallengeValidationDetails 7 | { 8 | public const string Http01ChallengeType = "http-01"; 9 | // URL template: 10 | // "http://{domain}/.well-known/acme-challenge/{token}" 11 | public const string HttpPathPrefix = ".well-known/acme-challenge"; 12 | public const string HttpResourceContentTypeDefault = "application/octet-stream"; 13 | 14 | public string ChallengeType => Http01ChallengeType; 15 | 16 | public string HttpResourceUrl { get; set; } 17 | 18 | public string HttpResourcePath { get; set; } 19 | 20 | public string HttpResourceContentType { get; set; } 21 | 22 | public string HttpResourceValue { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/ProblemType.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Protocol.Resources 2 | { 3 | /// 4 | /// Defines standard ACME errors. 5 | /// 6 | /// 7 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-18#section-6.7 8 | /// 9 | public enum ProblemType 10 | { 11 | Unknown = 0, 12 | 13 | AccountDoesNotExist, 14 | AlreadyRevoked, 15 | BadCSR, 16 | BadNonce, 17 | BadRevocationReason, 18 | BadSignatureAlgorithm, 19 | Caa, 20 | Compound, 21 | Connection, 22 | Dns, 23 | ExternalAccountRequired, 24 | IncorrectResponse, 25 | InvalidContact, 26 | Malformed, 27 | RateLimited, 28 | RejectedIdentifier, 29 | ServerInternal, 30 | Tls, 31 | Unauthorized, 32 | UnsupportedContact, 33 | UnsupportedIdentifier, 34 | UserActionRequired, 35 | } 36 | } -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/AwsFixture.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.IntegrationTests 5 | { 6 | public class AwsFixture 7 | { 8 | public AwsFixture() 9 | { 10 | var thisAsmLocation = Path.GetDirectoryName(typeof(AwsFixture).Assembly.Location); 11 | var jsonPathBase = Path.Combine(thisAsmLocation, @"config/_IGNORE/"); 12 | 13 | R53 = JsonConvert.DeserializeObject( 14 | File.ReadAllText(jsonPathBase + "R53Helper.json")); 15 | S3 = JsonConvert.DeserializeObject( 16 | File.ReadAllText(jsonPathBase + "S3Helper.json")); 17 | 18 | // For testing this makes it easier to repeat tests 19 | // that use the same DNS names and need to be refreshed 20 | R53.DnsRecordTtl = 60; 21 | } 22 | 23 | public R53Helper R53 { get; } 24 | 25 | public S3Helper S3 { get; } 26 | } 27 | } -------------------------------------------------------------------------------- /src/ACMESharp/Logging/NullLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace ACMESharp.Logging 5 | { 6 | /// 7 | /// Implementation of the logger interface that does nothing. 8 | /// 9 | /// 10 | /// Provides a default, do-nothing implementation. 11 | /// 12 | public class NullLogger : ILogger 13 | { 14 | public static readonly NullLogger Instance = new NullLogger(); 15 | 16 | public bool IsEnabled(LogLevel logLevel) => false; 17 | 18 | public IDisposable BeginScope(TState state) => new NullLoggerScope(); 19 | 20 | public void Log(LogLevel logLevel, EventId eventId, TState state, 21 | Exception exception, Func formatter) 22 | { } 23 | 24 | public class NullLoggerScope : IDisposable 25 | { 26 | public void Dispose() 27 | { } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/examples/ACMEForms/Storage/DbAccount.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Protocol; 7 | 8 | namespace ACMEForms.Storage 9 | { 10 | public class DbAccount 11 | { 12 | public static readonly IEnumerable> WellKnownAcmeServers = 13 | new (string key, string label)[] 14 | { 15 | ("https://acme-staging-v02.api.letsencrypt.org/", "Let's Encrypt v2 STAGE"), 16 | ("https://acme-v02.api.letsencrypt.org/", "Let's Encrypt v2"), 17 | (string.Empty, "(CUSTOM)"), 18 | }.Select(x => new KeyValuePair(x.key, x.label)); 19 | 20 | public string AcmeServerEndpoint { get; set; } 21 | 22 | public int Id { get; set; } 23 | 24 | public string JwsSigner { get; set; } 25 | 26 | public AccountDetails Details { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | body { 4 | padding-top: 50px; 5 | padding-bottom: 20px; 6 | } 7 | 8 | /* Wrapping element */ 9 | /* Set some basic padding to keep content from hitting the edges */ 10 | .body-content { 11 | padding-left: 15px; 12 | padding-right: 15px; 13 | } 14 | 15 | /* Carousel */ 16 | .carousel-caption p { 17 | font-size: 20px; 18 | line-height: 1.4; 19 | } 20 | 21 | /* Make .svg files in the carousel display properly in older browsers */ 22 | .carousel-inner .item img[src$=".svg"] { 23 | width: 100%; 24 | } 25 | 26 | /* QR code generator */ 27 | #qrCode { 28 | margin: 15px; 29 | } 30 | 31 | /* Hide/rearrange for smaller screens */ 32 | @media screen and (max-width: 767px) { 33 | /* Hide captions */ 34 | .carousel-caption { 35 | display: none; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/ACMESharp.MockServer.UnitTests/ACMESharp.MockServer.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/ACMESharp.Testing.Xunit/TestOrderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ACMESharp.Testing.Xunit 4 | { 5 | /// 6 | /// Declares a relative test ordering weight. Can be applied to individual 7 | /// test methods (test facts) or classes (test collections). 8 | /// 9 | /// 10 | /// To be used only for non-unit-tests such as integration tests 11 | /// where the exact order of individual tests is significant due to dependencies 12 | /// between tests. 13 | /// 14 | /// To use this attribute, you must use the custom 15 | /// test ordering support. 16 | /// 17 | /// 18 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] 19 | public sealed class TestOrderAttribute : Attribute 20 | { 21 | public TestOrderAttribute(int order, string group = null) 22 | { 23 | Order = order; 24 | Group = group; 25 | } 26 | 27 | public int Order { get; } 28 | 29 | public string Group { get; } 30 | } 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 PKISharp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "https://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jquery-validation/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.17.0", 31 | "_release": "1.17.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.17.0", 35 | "commit": "fc9b12d3bfaa2d0c04605855b896edb2934c0772" 36 | }, 37 | "_source": "https://github.com/jzaefferer/jquery-validation.git", 38 | "_target": "^1.17.0", 39 | "_originalSource": "jquery-validation", 40 | "_direct": true 41 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/RepoNonceManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ACMESharp.MockServer.Storage; 3 | 4 | namespace ACMESharp.MockServer 5 | { 6 | public class RepoNonceManager : INonceManager 7 | { 8 | private IRepository _repo; 9 | 10 | public RepoNonceManager(IRepository repo) 11 | { 12 | _repo = repo; 13 | } 14 | public string GenerateNonce() 15 | { 16 | var nonce = new DbNonce 17 | { 18 | Nonce = Guid.NewGuid().ToString(), 19 | }; 20 | _repo.SaveNonce(nonce); 21 | return nonce.Nonce; 22 | } 23 | 24 | public bool PeekNonce(string nonce) 25 | { 26 | var dbNonce = _repo.GetNonceByValue(nonce); 27 | return dbNonce != null; 28 | } 29 | 30 | public bool ValidateNonce(string nonce) 31 | { 32 | var dbNonce = _repo.GetNonceByValue(nonce); 33 | if (dbNonce == null) 34 | return false; 35 | 36 | _repo.RemoveNonce(dbNonce); 37 | return true; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/CreateAccountRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using ACMESharp.Crypto.JOSE; 4 | using Newtonsoft.Json; 5 | 6 | namespace ACMESharp.Protocol.Messages 7 | { 8 | /// 9 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 10 | /// 11 | public class CreateAccountRequest 12 | { 13 | [JsonProperty("contact", Required = Required.Always)] 14 | [Required, MinLength(1)] 15 | public IEnumerable Contact { get; set; } 16 | 17 | [JsonProperty("termsOfServiceAgreed", NullValueHandling=NullValueHandling.Ignore)] 18 | public bool? TermsOfServiceAgreed { get; set; } 19 | 20 | [JsonProperty("onlyReturnExisting", NullValueHandling=NullValueHandling.Ignore)] 21 | public bool? OnlyReturnExisting { get; set; } 22 | 23 | [JsonProperty("externalAccountBinding", NullValueHandling=NullValueHandling.Ignore)] 24 | public object ExternalAccountBinding { get; set; } 25 | //public JwsSignedPayload ExternalAccountBinding { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using Newtonsoft.Json; 5 | 6 | namespace ACMESharp.Protocol.Resources 7 | { 8 | /// 9 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.2 10 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 11 | /// 12 | public class Account 13 | { 14 | public string Id { get; set; } 15 | 16 | public object Key { get; set; } 17 | 18 | public string[] Contact { get; set; } 19 | 20 | public string Status { get; set; } 21 | 22 | public bool? TermsOfServiceAgreed { get; set; } 23 | 24 | public string Orders { get; set; } 25 | 26 | // TODO: are these standard or specific to LE? 27 | // "agreement": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf", 28 | // "initialIp": "50.235.30.49", 29 | // "createdAt": "2018-05-02T22:23:30Z", 30 | public string InitialIp { get; set; } 31 | public string CreatedAt { get; set; } 32 | public string Agreement { get; set; } 33 | } 34 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ACMEBlazor", "ACMEBlazor\ACMEBlazor.csproj", "{B94A1327-02ED-4214-8B57-7F5A5AE022D2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B94A1327-02ED-4214-8B57-7F5A5AE022D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B94A1327-02ED-4214-8B57-7F5A5AE022D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B94A1327-02ED-4214-8B57-7F5A5AE022D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B94A1327-02ED-4214-8B57-7F5A5AE022D2}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {15827651-06A5-40F4-9947-18338AB228A8} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/examples/Examples.Common.PKI/ExamplesAccountKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using ACMESharp.Crypto.JOSE; 5 | 6 | namespace Examples.Common.PKI 7 | { 8 | public class ExamplesAccountKey 9 | { 10 | public string KeyType { get; set; } 11 | public string KeyExport { get; set; } 12 | 13 | public IJwsTool GenerateTool() 14 | { 15 | if (KeyType.StartsWith("ES")) 16 | { 17 | var tool = new ACMESharp.Crypto.JOSE.Impl.ESJwsTool(); 18 | tool.HashSize = int.Parse(KeyType.Substring(2)); 19 | tool.Init(); 20 | tool.Import(KeyExport); 21 | return tool; 22 | } 23 | 24 | if (KeyType.StartsWith("RS")) 25 | { 26 | var tool = new ACMESharp.Crypto.JOSE.Impl.RSJwsTool(); 27 | tool.HashSize = int.Parse(KeyType.Substring(2)); 28 | tool.Init(); 29 | tool.Import(KeyExport); 30 | return tool; 31 | } 32 | 33 | throw new Exception($"Unknown or unsupported KeyType [{KeyType}]"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace ACMESharp.MockServer.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | [ApiController] 11 | public class ValuesController : ControllerBase 12 | { 13 | // GET api/values 14 | [HttpGet] 15 | public ActionResult> Get() 16 | { 17 | return new string[] { "value1", "value2" }; 18 | } 19 | 20 | // GET api/values/5 21 | [HttpGet("{id}")] 22 | public ActionResult Get(int id) 23 | { 24 | return "value"; 25 | } 26 | 27 | // POST api/values 28 | [HttpPost] 29 | public void Post([FromBody] string value) 30 | { 31 | } 32 | 33 | // PUT api/values/5 34 | [HttpPut("{id}")] 35 | public void Put(int id, [FromBody] string value) 36 | { 37 | } 38 | 39 | // DELETE api/values/5 40 | [HttpDelete("{id}")] 41 | public void Delete(int id) 42 | { 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/examples/ACMEForms/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ACMEForms.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/XunitDebugging/import.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <_XunitTargetFrameworksLines Include="$(TargetFramework)" /> 6 | 7 | 8 | <_XunitTargetFrameworksLines Include="$(TargetFrameworks)" /> 9 | 10 | 11 | 12 | 13 | 14 | <_XunitInfoLines Include="OutputPath: $(OutputPath)"/> 15 | <_XunitInfoLines Include="AssemblyName: $(AssemblyName)"/> 16 | <_XunitInfoLines Include="TargetFileName: $(TargetFileName)"/> 17 | <_XunitInfoLines Include="TargetFrameworkIdentifier: $(TargetFrameworkIdentifier)"/> 18 | <_XunitInfoLines Include="TargetFrameworkVersion: $(TargetFrameworkVersion)"/> 19 | <_XunitInfoLines Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)"/> 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/XunitDebugging/ArgParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ACMESharp.IntegrationTests.Debugging 5 | { 6 | public static class ArgParser 7 | { 8 | // Simple argument parser that doesn't do much validation, since we will rely on the inner 9 | // runners to do the argument validation. 10 | public static Dictionary> Parse(string[] args) 11 | { 12 | var result = new Dictionary>(StringComparer.OrdinalIgnoreCase); 13 | var idx = 0; 14 | 15 | while (idx < args.Length) 16 | { 17 | var arg = args[idx++]; 18 | if (!arg.StartsWith("-")) 19 | throw new ArgumentException($"Unexpected parameter: {arg}"); 20 | 21 | if (!result.TryGetValue(arg, out var values)) 22 | { 23 | values = new List(); 24 | result.Add(arg, values); 25 | } 26 | 27 | if (idx < args.Length && !args[idx].StartsWith("-")) 28 | values.Add(args[idx++]); 29 | else 30 | values.Add(null); 31 | } 32 | 33 | return result; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /tools/test-report/sample-test-results.ps1: -------------------------------------------------------------------------------- 1 | 2 | #$xslFile = "$PSScriptRoot\example.xsl" 3 | #$xmlFile = "$PSScriptRoot\example.xml" 4 | #$outFile = "$PSScriptRoot\example.out" 5 | 6 | $xslFile = "$PSScriptRoot\trx2md.xsl" 7 | $xmlFile = "$PSScriptRoot\sample-test-results.trx" 8 | $outFile = "$PSScriptRoot\sample-test.results.md" 9 | 10 | class TrxFn { 11 | [double]DiffSeconds([datetime]$from, [datetime]$till) { 12 | return ($till - $from).TotalSeconds 13 | } 14 | } 15 | 16 | 17 | if (-not $script:xslt) { 18 | $script:urlr = [System.Xml.XmlUrlResolver]::new() 19 | $script:opts = [System.Xml.Xsl.XsltSettings]::new() 20 | #$script:opts.EnableScript = $true 21 | $script:xslt = [System.Xml.Xsl.XslCompiledTransform]::new() 22 | try { 23 | $script:xslt.Load($xslFile, $script:opts, $script:urlr) 24 | } 25 | catch { 26 | $Error[0] 27 | return 28 | } 29 | } 30 | 31 | $script:list = [System.Xml.Xsl.XsltArgumentList]::new() 32 | $script:list.AddExtensionObject("urn:trxfn", [TrxFn]::new()) 33 | $script:wrtr = [System.IO.StreamWriter]::new($outFile) 34 | try { 35 | $script:xslt.Transform( 36 | [string]$xmlFile, 37 | [System.Xml.Xsl.XsltArgumentList]$script:list, 38 | [System.IO.TextWriter]$script:wrtr) 39 | } 40 | finally { 41 | $script:wrtr.Dispose() 42 | } 43 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Messages/UpdateAccountRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using ACMESharp.Crypto.JOSE; 4 | using Newtonsoft.Json; 5 | 6 | namespace ACMESharp.Protocol.Messages 7 | { 8 | /// 9 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 10 | /// 11 | public class UpdateAccountRequest 12 | { 13 | /// 14 | /// The list of contact URLs. Although a request to create a brand new account 15 | /// requires this value, when used in a request to lookup an existing account 16 | /// this property can be omitted. 17 | /// 18 | [JsonProperty("contact", NullValueHandling=NullValueHandling.Ignore)] 19 | public IEnumerable Contact { get; set; } 20 | 21 | [JsonProperty("termsOfServiceAgreed", NullValueHandling=NullValueHandling.Ignore)] 22 | public bool? TermsOfServiceAgreed { get; set; } 23 | 24 | [JsonProperty("externalAccountBinding", NullValueHandling=NullValueHandling.Ignore)] 25 | public object ExternalAccountBinding { get; set; } 26 | //public JwsSignedPayload ExternalAccountBinding { get; set; } 27 | 28 | [JsonProperty("status", NullValueHandling=NullValueHandling.Ignore)] 29 | public string Status { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /tools/chocoInstallDotNetCore21Rc1.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop'; 2 | 3 | ipmo "$env:ChocolateyInstall\helpers\chocolateyInstaller.psm1" 4 | 5 | $packageName= 'dotnetcore-sdk' 6 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" 7 | #$url = 'https://download.microsoft.com/download/D/7/8/D788D3CD-44C4-487D-829B-413E914FB1C3/dotnet-sdk-2.1.300-preview1-008174-win-x86.exe' 8 | #$checksum = 'bd8a9145f651026cfa1ca7c264c2e05b3740afc0b5f8ac5572409a95836d8f87e1a8c460eb985182501f679b721a97fd174b7690ab8cdc5e43c8155ee8af94b5' 9 | $url64 = 'https://download.microsoft.com/download/B/1/9/B19A2F87-F00F-420C-B4B9-A0BA4403F754/dotnet-sdk-2.1.300-rc1-008673-win-x64.exe' 10 | $checksum64 = '7256aca2c02827028213ce06ceb5414231b01bbc509d0d57d5258106760c0fa5621a9d5f629fca3f34d6c45523a133206561d7188a0cb4817d4d5cc6c172d6f0' 11 | 12 | $packageArgs = @{ 13 | packageName = $packageName 14 | unzipLocation = $toolsDir 15 | fileType = 'EXE' 16 | url = $url 17 | url64bit = $url64 18 | 19 | silentArgs = "/install /quiet /norestart /log `"$env:TEMP\$($packageName)\$($packageName).MsiInstall.log`"" 20 | validExitCodes= @(0, 3010, 1641) 21 | 22 | softwareName = 'dotnet-core*' 23 | checksum = $checksum 24 | checksumType = 'SHA512' 25 | checksum64 = $checksum64 26 | } 27 | 28 | Install-ChocolateyPackage @packageArgs 29 | -------------------------------------------------------------------------------- /src/examples/ACMECLI/HttpUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace ACMECLI 7 | { 8 | public static class HttpUtil 9 | { 10 | private static HttpClient _Client; 11 | 12 | public static HttpClient Client 13 | { 14 | get 15 | { 16 | if (_Client == null) 17 | { 18 | lock(typeof(HttpUtil)) 19 | { 20 | if (_Client == null) 21 | { 22 | _Client = new HttpClient(); 23 | } 24 | } 25 | } 26 | return _Client; 27 | } 28 | } 29 | 30 | public static async Task GetStringAsync(string url) 31 | { 32 | var resp = await Client.GetAsync(url); 33 | 34 | if (resp.StatusCode != HttpStatusCode.OK) 35 | { 36 | if (resp.StatusCode == HttpStatusCode.NotFound) 37 | return null; 38 | throw new Exception("HTTP request error: " 39 | + $"({resp.StatusCode}) {await resp.Content.ReadAsStringAsync()}"); 40 | } 41 | 42 | return await resp.Content.ReadAsStringAsync(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/CryptoHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Security.Cryptography; 4 | using System.Security.Cryptography.X509Certificates; 5 | using Newtonsoft.Json; 6 | using System.Collections.Generic; 7 | 8 | namespace ACMESharp.Crypto 9 | { 10 | /// 11 | /// For the most compatibility with LE, see: 12 | /// https://letsencrypt.org/docs/integration-guide/#supported-key-algorithms 13 | /// We should support: 14 | /// * RSA Keys (2048-4096 bits) 15 | /// * ECDSA Keys (P-256, P-384) 16 | /// 17 | /// Thats' for both account keys and cert keys. 18 | /// 19 | public static class CryptoHelper 20 | { 21 | /// 22 | /// Returns a singleton instance of cryptographic tool 23 | /// for URL-safe Base64 encoding. 24 | /// 25 | public static Base64Tool Base64 { get; } = new Base64Tool(); 26 | 27 | /// 28 | /// Returns a singleton instance of cryptographic tool 29 | /// for working with RSA keys and algorithms. 30 | /// 31 | public static RsaTool Rsa { get; } = new RsaTool(); 32 | 33 | /// 34 | /// Returns a singleton instance of cryptographic tool 35 | /// for working with EC keys and algorithms. 36 | /// 37 | public static EcTool Ec { get; } = new EcTool(); 38 | } 39 | } -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/EcTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace ACMESharp.Crypto 5 | { 6 | /// 7 | /// Collection of convenient crypto operations working 8 | /// with Elliptic Curve keys and algorithms. 9 | /// 10 | public class EcTool 11 | { 12 | public ECDsa GenerateAlgorithm(int curveSize) 13 | { 14 | ECCurve curve; 15 | switch (curveSize) 16 | { 17 | case 256: 18 | curve = ECCurve.NamedCurves.nistP256; 19 | break; 20 | case 384: 21 | curve = ECCurve.NamedCurves.nistP384; 22 | break; 23 | default: 24 | throw new ArgumentOutOfRangeException("only 256 and 384 curves are supported"); 25 | } 26 | 27 | return ECDsa.Create(curve); 28 | } 29 | 30 | public ECDsa GenerateAlgorithm(string ecKeys) 31 | { 32 | var dsa = ECDsa.Create(); 33 | dsa.FromXmlString(ecKeys); 34 | return dsa; 35 | } 36 | 37 | public string GenerateKeys(int curveSize) 38 | { 39 | return GenerateKeys(GenerateAlgorithm(curveSize)); 40 | } 41 | 42 | public string GenerateKeys(ECDsa ec) 43 | { 44 | return ec.ToXmlString(true); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Pages/FetchData.cshtml: -------------------------------------------------------------------------------- 1 | @page "/fetchdata" 2 | @inject HttpClient Http 3 | 4 |

Weather forecast

5 | 6 |

This component demonstrates fetching data from the server.

7 | 8 | @if (forecasts == null) 9 | { 10 |

Loading...

11 | } 12 | else 13 | { 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | @foreach (var forecast in forecasts) 25 | { 26 | 27 | 28 | 29 | 30 | 31 | 32 | } 33 | 34 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
35 | } 36 | 37 | @functions { 38 | WeatherForecast[] forecasts; 39 | 40 | protected override async Task OnInitAsync() 41 | { 42 | forecasts = await Http.GetJsonAsync("sample-data/weather.json"); 43 | } 44 | 45 | class WeatherForecast 46 | { 47 | public DateTime Date { get; set; } 48 | public int TemperatureC { get; set; } 49 | public int TemperatureF { get; set; } 50 | public string Summary { get; set; } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/examples/README.md: -------------------------------------------------------------------------------- 1 | # README - ACMESharp Core Examples 2 | 3 | This folder provides a collection of example ACME clients that internally use the ACMESharp v2.x 4 | client library. The idea is to provide range of clients that target different execution 5 | environments and different approaches to collecting input and data from users and storage 6 | systems to provide guidance and solution ideas to others who may wish to build more complete 7 | client solutions atop ACMESharp. 8 | 9 | As these examples are worked through and evolved they also will influence and help steer 10 | the direction of the ACMESharp client library itself. 11 | 12 | The examples and their current state are as follows: 13 | 14 | | Project | Status | Notes 15 | |-|-|-| 16 | | ACMECLI | Works! | A console-based CLI app. See [full details](./ACMECLI/README.md). 17 | | ACMEKestrel | Works! | Provide sample middleware and services to automatically obtain ACME certs for a Kestrel-based (ASP.NET Core) app. 18 | | ACMEBlazor | In-progress | Provides a simple, browser-only client based on the [Blazor](https://blazor.net) project. 19 | | ACMETerm | In-progress | Provides a terminal-based GUI app based on the [gui.cs](https://github.com/migueldeicaza/gui.cs) project. 20 | | ACMEForms | In-progress | Provides a WinForms client, testing ACMESharp support on earlier versions of .NET Framework. 21 | | ACMEAvalon | Investigating | Provides a cross-platform, WPF-like GUI client, based on the [Avalonia](http://avaloniaui.net/) project. 22 | 23 | -------------------------------------------------------------------------------- /src/ACMESharp.DotNetCore/EcToolExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace ACMESharp.Crypto 7 | { 8 | public static class EcToolExtensions 9 | { 10 | /// 11 | /// Returns a DER-encoded PKCS#10 Certificate Signing Request for the given ECDsa parametes 12 | /// and the given hash algorithm. 13 | /// 14 | public static byte[] GenerateCsr(this EcTool tool, IEnumerable dnsNames, 15 | ECDsa dsa, HashAlgorithmName? hashAlgor = null) 16 | { 17 | if (hashAlgor == null) 18 | hashAlgor = HashAlgorithmName.SHA256; 19 | 20 | string firstName = null; 21 | var sanBuilder = new SubjectAlternativeNameBuilder(); 22 | foreach (var n in dnsNames) 23 | { 24 | sanBuilder.AddDnsName(n); 25 | if (firstName == null) 26 | firstName = n; 27 | } 28 | if (firstName == null) 29 | throw new ArgumentException("Must specify at least one name"); 30 | 31 | var dn = new X500DistinguishedName($"CN={firstName}"); 32 | var csr = new CertificateRequest(dn, dsa, hashAlgor.Value); 33 | csr.CertificateExtensions.Add(sanBuilder.Build()); 34 | 35 | return csr.CreateSigningRequest(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/Order.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using ACMESharp.Protocol.Resources; 3 | using Newtonsoft.Json; 4 | 5 | namespace ACMESharp.Protocol.Resources 6 | { 7 | /// 8 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.3 9 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 10 | /// 11 | public class Order 12 | { 13 | [JsonProperty("status")] 14 | [Required] 15 | public string Status { get; set; } 16 | 17 | [JsonProperty("expires")] 18 | public string Expires { get; set; } 19 | 20 | [JsonProperty("notBefore")] 21 | public string NotBefore { get; set; } 22 | 23 | [JsonProperty("notAfter")] 24 | public string NotAfter { get; set; } 25 | 26 | [JsonProperty("identifiers")] 27 | [Required, MinLength(1)] 28 | public Identifier[] Identifiers { get; set; } 29 | 30 | [JsonProperty("authorizations")] 31 | [Required, MinLength(1)] 32 | public string[] Authorizations { get; set; } 33 | 34 | [JsonProperty("finalize")] 35 | [Required] 36 | public string Finalize { get; set; } 37 | 38 | [JsonProperty("certificate", NullValueHandling = NullValueHandling.Ignore)] 39 | public string Certificate { get; set; } 40 | 41 | [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] 42 | public Problem Error { get; set; } 43 | } 44 | } -------------------------------------------------------------------------------- /src/examples/ACMEForms/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ACMEForms")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ACMEForms")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c691a2f9-266d-403b-ad2a-822706bb1639")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/ACMEBlazor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | dotnet 6 | blazor serve 7 | 7.3 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | $(IncludeRazorContentInPack) 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Storage/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ACMESharp.MockServer.Storage 4 | { 5 | public interface IRepository 6 | { 7 | void SaveNonce(DbNonce nonce); 8 | void RemoveNonce(DbNonce nonce); 9 | DbNonce GetNonce(int id); 10 | DbNonce GetNonceByValue(string nonce); 11 | 12 | void SaveAccount(DbAccount acct); 13 | DbAccount GetAccount(int id); 14 | DbAccount GetAccountByJwk(string jwk); 15 | DbAccount GetAccountByKid(string kid); 16 | 17 | void SaveOrder(DbOrder order); 18 | DbOrder GetOrder(int id); 19 | DbOrder GetOrderByUrl(string url); 20 | IEnumerable GetOrdersByAccountId(int id); 21 | 22 | void SaveAuthorization(DbAuthorization authz); 23 | DbAuthorization GetAuthorization(int id); 24 | DbAuthorization GetAuthorizationByUrl(string url); 25 | IEnumerable GetAuthorizationsByOrderId(int id); 26 | 27 | void SaveChallenge(DbChallenge chlng); 28 | DbChallenge GetChallenge(int id); 29 | DbChallenge GetChallengeByUrl(string url); 30 | IEnumerable GetChallengesByAuthorizationId(int id); 31 | 32 | void SaveCertificate(DbCertificate cert); 33 | DbCertificate GetCertificate(int id); 34 | DbCertificate GetCertificateByKey(string key); 35 | DbCertificate GetCertificateByThumbprint(string thumbprint); 36 | DbCertificate GetCertificateByNative(byte[] certDer); 37 | IEnumerable GetCertificatesByOrderId(int id); 38 | } 39 | } -------------------------------------------------------------------------------- /src/ACMESharp/ACMESharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | $(NoWarn);CS1591 7 | 8 | 9 | 13 | 14 | ACMESharpCore 15 | ACME v2 protocol client library for .NET Standard 16 | Copyright (C) Eugene Bekker. 17 | https://github.com/PKISharp/ACMESharpCore/blob/master/LICENSE 18 | https://github.com/PKISharp/ACMESharpCore/ 19 | https://raw.githubusercontent.com/PKISharp/ACMESharpCore/master/docs/acmesharp-logo-color.png 20 | pki;ssl;tls;security;certificates;letsencrypt;acme;acmesharp 21 | https://github.com/PKISharp/ACMESharpCore.git 22 | git 23 | https://github.com/PKISharp/ACMESharpCore/graphs/contributors 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/examples/ACMEForms/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/PKISharp.SimplePKI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | $(NoWarn);CS1591 6 | 7 | 8 | 12 | 13 | PKISharp.SimplePKI 14 | Simple collection of PKI certificate management primitives for .NET Standard 15 | Copyright (C) Eugene Bekker. 16 | https://github.com/PKISharp/ACMESharpCore/blob/master/LICENSE 17 | https://github.com/PKISharp/ACMESharpCore/ 18 | https://raw.githubusercontent.com/PKISharp/ACMESharpCore/master/docs/pkisharp-logo-color.png 19 | pki;ssl;tls;security;certificates;letsencrypt;acme;acmesharp 20 | https://github.com/PKISharp/ACMESharpCore.git 21 | git 22 | 23 | 24 | 25 | $(APPVEYOR_BUILD_NUMBER) 26 | 0 27 | 2.2.0.$(BuildNumber) 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/AcmeOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ACMESharp.Authorizations; 4 | using ACMESharp.Protocol.Resources; 5 | 6 | namespace ACMEKestrel 7 | { 8 | public class AcmeOptions 9 | { 10 | public const string LetsEncryptV2StagingEndpoint = "https://acme-staging-v02.api.letsencrypt.org/"; 11 | 12 | public const string LetsEncryptV2Endpoint = "https://acme-v02.api.letsencrypt.org/"; 13 | 14 | public const int DefaultRsaKeySize = 2048; 15 | public const int DefaultEcKeySize = 256; 16 | 17 | public string AcmeRootDir { get; set; } = "_acmesharp"; 18 | 19 | public string CaUrl { get; set; } = LetsEncryptV2Endpoint; 20 | 21 | public string AccountKeyAlgor { get; set; } = "ec"; 22 | 23 | public int? AccountKeySize { get; set; } 24 | 25 | public IEnumerable AccountContactEmails { get; set; } 26 | 27 | public bool AcceptTermsOfService { get; set; } 28 | 29 | public IEnumerable DnsNames { get; set; } 30 | 31 | public string ChallengeType { get; } = AcmeState.Http01ChallengeType; 32 | 33 | public Func ChallengeHandler { get; set; } 34 | = AcmeHttp01ChallengeHandler.AddChallengeHandling; 35 | 36 | public bool TestChallenges { get; } 37 | 38 | public string CertificateKeyAlgor { get; set; } = "ec"; 39 | 40 | public int? CertificateKeySize { get; set; } 41 | 42 | public int WaitForAuthorizations { get; set; } = 60; 43 | 44 | public int WaitForCertificate { get; set; } = 60; 45 | } 46 | } -------------------------------------------------------------------------------- /src/examples/ACMEForms/Storage/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ACMESharp.Protocol; 7 | using LiteDB; 8 | 9 | namespace ACMEForms.Storage 10 | { 11 | public class Repository 12 | { 13 | public const string DefaultDbName = "acmeforms.db"; 14 | 15 | public string DbName { get; private set; } 16 | 17 | public static Repository GetInstance(string dbName = DefaultDbName) 18 | { 19 | return new Repository 20 | { 21 | DbName = dbName, 22 | }; 23 | } 24 | 25 | public DbAccount GetAccount() 26 | { 27 | using (var db = new LiteDatabase(DbName)) 28 | { 29 | return db.GetCollection() 30 | .FindAll() 31 | .FirstOrDefault(); 32 | } 33 | } 34 | 35 | public void SaveAccount(DbAccount acct) 36 | { 37 | using (var db = new LiteDatabase(DbName)) 38 | { 39 | db.GetCollection().Upsert(acct); 40 | } 41 | } 42 | 43 | public IEnumerable GetOrders() 44 | { 45 | using (var db = new LiteDatabase(DbName)) 46 | { 47 | return db.GetCollection() 48 | .FindAll(); 49 | } 50 | } 51 | 52 | public void Saveorder(DbOrder order) 53 | { 54 | using (var db = new LiteDatabase(DbName)) 55 | { 56 | db.GetCollection().Upsert(order); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ACMESharp.DotNetCore/ACMESharp.DotNetCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | true 6 | $(NoWarn);CS1591 7 | 8 | 9 | 13 | 14 | ACMESharpCore.Crypto 15 | ACMESharp support library for .NET Core 16 | Copyright (C) Eugene Bekker. 17 | https://github.com/PKISharp/ACMESharpCore/blob/master/LICENSE 18 | https://github.com/PKISharp/ACMESharpCore/ 19 | https://raw.githubusercontent.com/PKISharp/ACMESharpCore/master/docs/acmesharp-logo-color.png 20 | pki;ssl;tls;security;certificates;letsencrypt;acme;acmesharp 21 | https://github.com/PKISharp/ACMESharpCore.git 22 | git 23 | https://github.com/PKISharp/ACMESharpCore/graphs/contributors 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/ACMESharp.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | ACMESharp.IntegrationTests.DebugMain 8 | 7.3 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | PreserveNewest 34 | 35 | 36 | 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/ACMESharp.DotNetCore/RsaToolExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace ACMESharp.Crypto 7 | { 8 | public static class RsaToolExtensions 9 | { 10 | public static RSA GenerateAlgorithm(this RsaTool tool, int keyBitLength) 11 | { 12 | return RSA.Create(keyBitLength); 13 | } 14 | 15 | public static string GenerateKeys(this RsaTool tool, int keyBitLength) 16 | { 17 | var rsa = GenerateAlgorithm(tool, keyBitLength); 18 | return tool.GenerateKeys(rsa); 19 | } 20 | 21 | /// 22 | /// Returns a DER-encoded PKCS#10 Certificate Signing Request for the given RSA parametes 23 | /// and the given hash algorithm. 24 | /// 25 | public static byte[] GenerateCsr(this RsaTool tool, IEnumerable dnsNames, 26 | RSA rsa, HashAlgorithmName? hashAlgor = null) 27 | { 28 | if (hashAlgor == null) 29 | hashAlgor = HashAlgorithmName.SHA256; 30 | 31 | string firstName = null; 32 | var sanBuilder = new SubjectAlternativeNameBuilder(); 33 | foreach (var n in dnsNames) 34 | { 35 | sanBuilder.AddDnsName(n); 36 | if (firstName == null) 37 | firstName = n; 38 | } 39 | if (firstName == null) 40 | throw new ArgumentException("Must specify at least one name"); 41 | 42 | var dn = new X500DistinguishedName($"CN={firstName}"); 43 | var csr = new CertificateRequest(dn, 44 | rsa, hashAlgor.Value, RSASignaturePadding.Pkcs1); 45 | csr.CertificateExtensions.Add(sanBuilder.Build()); 46 | 47 | return csr.CreateSigningRequest(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/ACMESharp/HTTP/Link.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace ACMESharp.HTTP 5 | { 6 | /// 7 | /// Represents a Link header value that represents the well-defined HTTP response 8 | /// entity header 9 | /// Link 10 | /// and more fully specified in 11 | /// RFC 5988 Section 5. 12 | /// 13 | /// 14 | /// This class only implements a subset of the mechanics and nuances of the Link header 15 | /// field as necessary for implementing the ACME protocol. 16 | /// 17 | public class Link 18 | { 19 | /// 20 | /// Regex pattern to match and extract the components of an HTTP related link header. 21 | /// 22 | public static readonly Regex LinkHeaderRegex = new Regex("<(.+)>;[ ]?rel=\"(.+)\""); 23 | 24 | public const string LinkHeaderFormat = "<{0}>;rel={1}"; 25 | 26 | public Link(string value) 27 | { 28 | Value = value; 29 | 30 | var m = LinkHeaderRegex.Match(value); 31 | if (!m.Success) 32 | throw new ArgumentException("Invalid Link header format", nameof(value)); 33 | 34 | Uri = m.Groups[1].Value; 35 | Relation = m.Groups[2].Value; 36 | } 37 | 38 | public Link(string uri, string rel) 39 | { 40 | // This will parse the URI to make sure it's well-formed and throw if not 41 | new Uri(uri); 42 | 43 | Uri = uri; 44 | Relation = rel; 45 | Value = string.Format(LinkHeaderFormat, uri, rel); 46 | } 47 | 48 | public string Value { get; private set; } 49 | 50 | public string Uri { get; private set; } 51 | 52 | public string Relation { get; private set; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/Challenge.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Protocol.Resources 5 | { 6 | public class Challenge 7 | { 8 | [JsonProperty("type")] 9 | public string Type { get; set; } 10 | 11 | [JsonProperty("url")] 12 | public string Url { get; set; } 13 | 14 | [JsonProperty("status")] 15 | public string Status { get; set; } 16 | 17 | /// 18 | /// The time at which the server validated this challenge, 19 | /// encoded in the format specified in RFC 3339 [RFC3339]. 20 | /// This field is REQUIRED if the "status" field is "valid". 21 | /// 22 | [JsonProperty("validated")] 23 | public string Validated { get; set; } 24 | 25 | /// 26 | /// Details of successful validation. 27 | /// 28 | /// 29 | /// TODO: This does not appear to be documented in the latest ACMEv2 draft 30 | /// but experimentation shows a record such as this: 31 | /// 32 | /// "validationRecord": [ 33 | /// { 34 | /// "hostname": "foo.example.com" 35 | /// } 36 | /// ] 37 | /// 38 | /// There also does not appear to be any indication of the "validated 39 | /// 40 | [JsonProperty("validationRecord")] 41 | public object[] ValidationRecord { get; set; } 42 | 43 | /// 44 | /// Error that occurred while the server was validating the challenge, 45 | /// if any, structured as a problem document [RFC7807]. Multiple 46 | /// errors can be indicated by using subproblems Section 6.6.1. 47 | /// 48 | [JsonProperty("error")] 49 | public object Error { get; set; } 50 | 51 | [JsonProperty("token")] 52 | public string Token { get; set; } 53 | } 54 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/AcmeState.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Security.Cryptography.X509Certificates; 3 | using ACMESharp.Authorizations; 4 | using ACMESharp.Protocol; 5 | using ACMESharp.Protocol.Resources; 6 | using Examples.Common.PKI; 7 | 8 | namespace ACMEKestrel 9 | { 10 | public class AcmeState 11 | { 12 | public static AcmeState Instance { get; } = new AcmeState(); 13 | 14 | public const string PendingStatus = "pending"; 15 | public const string ValidStatus = "valid"; 16 | public const string InvalidStatus = "invalid"; 17 | 18 | public const string Http01ChallengeType = "http-01"; 19 | public const string Dns01ChallengeType = "dns-01"; 20 | 21 | public string RootDir { get; set; } 22 | 23 | public string ServiceDirectoryFile { get; set; } 24 | 25 | public ServiceDirectory ServiceDirectory { get; set; } 26 | 27 | public string TermsOfServiceFile { get; set; } 28 | 29 | public string AccountFile { get; set; } 30 | 31 | public AccountDetails Account { get; set; } 32 | 33 | public string AccountKeyFile { get; set; } 34 | 35 | public ExamplesAccountKey AccountKey { get; set; } 36 | 37 | public string OrderFile { get; set; } 38 | 39 | public OrderDetails Order { get; set; } 40 | 41 | public string AuthorizationsFile { get; set; } 42 | 43 | public Dictionary Authorizations { get; set; } 44 | 45 | public string CertificateKeysFile { get; set; } 46 | 47 | public string CertificateRequestFile { get; set; } 48 | 49 | public string CertificateChainFile { get; set; } 50 | 51 | public string CertificateFile { get; set; } 52 | 53 | public X509Certificate2 Certificate { get; set; } 54 | 55 | public IDictionary Http01Responses { get; set; } 56 | = new Dictionary(); 57 | } 58 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Pages/Shared/_CookieConsentPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Http.Features 2 | 3 | @{ 4 | var consentFeature = Context.Features.Get(); 5 | var showBanner = !consentFeature?.CanTrack ?? false; 6 | var cookieString = consentFeature?.CreateConsentCookie(); 7 | } 8 | 9 | @if (showBanner) 10 | { 11 | 33 | 41 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/AcmeProtocolException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using ACMESharp.Protocol.Resources; 4 | 5 | namespace ACMESharp.Protocol 6 | { 7 | public class AcmeProtocolException : Exception 8 | { 9 | private Problem _problem; 10 | 11 | public AcmeProtocolException(Problem problem = null) 12 | { 13 | Init(problem); 14 | } 15 | 16 | public AcmeProtocolException(string message, Problem problem = null) 17 | : base(message) 18 | { 19 | Init(problem); 20 | } 21 | 22 | public AcmeProtocolException(string message, Exception innerException, Problem problem = null) 23 | : base(message, innerException) 24 | { 25 | Init(problem); 26 | } 27 | 28 | protected AcmeProtocolException(SerializationInfo info, StreamingContext context, Problem problem = null) 29 | : base(info, context) 30 | { 31 | Init(problem); 32 | } 33 | 34 | private void Init(Problem problem = null) 35 | { 36 | _problem = problem; 37 | var problemType = _problem?.Type; 38 | if (!string.IsNullOrEmpty(problemType)) 39 | { 40 | if (problemType.StartsWith(Problem.StandardProblemTypeNamespace)) 41 | { 42 | if (Enum.TryParse( 43 | problemType.Substring(Problem.StandardProblemTypeNamespace.Length), 44 | true, 45 | out ProblemType pt)) 46 | { 47 | ProblemType = pt; 48 | }; 49 | } 50 | } 51 | } 52 | 53 | public ProblemType ProblemType { get; private set; } = ProblemType.Unknown; 54 | 55 | public string ProblemTypeRaw => _problem?.Type; 56 | 57 | public string ProblemDetail => _problem?.Detail; 58 | 59 | public int ProblemStatus => _problem?.Status ?? -1; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tools/appveyor-connect.ps1: -------------------------------------------------------------------------------- 1 | 2 | $tmp = "$PSScriptRoot\_TMP" 3 | md $tmp -Force | Out-Null 4 | 5 | $rdpTemplate = "$PSScriptRoot\appveyor-connect.template.rdp" 6 | $rdpLastServer = "$tmp\lastServer.txt" 7 | $rdpProfile = "$tmp\appveyor.rdp" 8 | 9 | #$avDetails = @' 10 | # Server: 74.205.54.20:33931 11 | # Username: appveyor 12 | # Password: %KFGJzhKUlcD$jX 13 | #'@ 14 | $avDetails = Get-Clipboard -TextFormatType Text 15 | if (-not $avDetails -or -not "$avDetails".Trim().Length) { 16 | Write-Warning "Copy the connection details from AV console to the clipboard and TRY AGAIN!" 17 | return 18 | } 19 | 20 | $hostport = "" 21 | $username = "" 22 | $password = "" 23 | 24 | if ("$avDetails" -imatch "server:\s*([^\s]+)" ) { $hostport = $Matches[1] } 25 | if ("$avDetails" -imatch "username:\s*([^\s]+)") { $username = $Matches[1] } 26 | if ("$avDetails" -imatch "password:\s*([^\s]+)") { $password = $Matches[1] } 27 | 28 | Write-Output @" 29 | Extracted Connection Details: 30 | Server: $hostport 31 | Username: $username 32 | Password: $password 33 | "@ 34 | 35 | if (-not $hostport -or -not $username -or -not $password) { 36 | Write-Warning "Unable to extract all the connection details" 37 | return 38 | } 39 | 40 | if (Test-Path -PathType Leaf $rdpLastServer) { 41 | $lastServer = [System.IO.File]::ReadAllText($rdpLastServer) 42 | Write-Warning "Detected LAST SERVER [$lastServer]" 43 | if ($lastServer) { 44 | Write-Warning "Deleting Credentials for LAST SERVER [$lastServer]" 45 | cmdkey /delete:`"$lastServer`" 46 | } 47 | } 48 | 49 | [System.IO.File]::WriteAllText($rdpLastServer, $hostport) 50 | 51 | $rdpDetails = [System.IO.File]::ReadAllText($rdpTemplate) 52 | $rdpDetails = $rdpDetails.Replace("@@SERVER@@", $hostport) 53 | [System.IO.File]::WriteAllText($rdpProfile, $rdpDetails) 54 | 55 | 56 | Write-Warning "Adding Credentials for [$hostport]" 57 | cmdkey /generic:`"$hostport`" /user:`"$username`" /pass:`"$password`" 58 | 59 | Write-Output "COPYING PASSWORD TO CLIPBOARD" 60 | Set-Clipboard -Value $password 61 | 62 | mstsc $rdpProfile 63 | -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/PkiCertificateExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Org.BouncyCastle.Asn1; 6 | using Org.BouncyCastle.Asn1.X509; 7 | 8 | namespace PKISharp.SimplePKI 9 | { 10 | public class PkiCertificateExtension : IComparable 11 | { 12 | internal PkiCertificateExtension() 13 | { } 14 | 15 | internal DerObjectIdentifier Identifier { get; set; } 16 | 17 | internal bool IsCritical { get; set; } 18 | 19 | internal Asn1Encodable Value { get; set; } 20 | // internal X509Extension Value { get; set; } 21 | 22 | public int CompareTo(object obj) 23 | { 24 | var that = obj as PkiCertificateExtension; 25 | if (that == null) 26 | return -1; 27 | 28 | var thisVal = this.Identifier.ToString() 29 | + this.IsCritical 30 | + Convert.ToBase64String(this.Value.GetDerEncoded()); 31 | var thatVal = that.Identifier.ToString() 32 | + that.IsCritical 33 | + Convert.ToBase64String(that.Value.GetDerEncoded()); 34 | return thisVal.CompareTo(thatVal); 35 | } 36 | 37 | public static PkiCertificateExtension CreateDnsSubjectAlternativeNames(IEnumerable dnsNames) 38 | { 39 | // Based on: 40 | // https://boredwookie.net/blog/bouncy-castle-add-a-subject-alternative-name-when-creating-a-cer 41 | 42 | var gnames = new List( 43 | dnsNames.Select(x => new GeneralName(GeneralName.DnsName, x))); 44 | 45 | var altNames = new GeneralNames(gnames.ToArray()); 46 | 47 | return new PkiCertificateExtension 48 | { 49 | Identifier = X509Extensions.SubjectAlternativeName, 50 | IsCritical = false, 51 | Value = altNames, 52 | //Value = new X509Extension(false, new DerOctetString(altNames)), 53 | }; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Pages/TryBlazorDB.cshtml: -------------------------------------------------------------------------------- 1 | @page "/tryblazordb" 2 | 3 | @inject Context Context 4 | 5 |

Try BlazorDB

6 | 7 |

8 | 9 | @*

Current count: @currentCount

*@ 10 | 11 | 12 | 13 | 14 | 15 | @functions 16 | { 17 | //int currentCount = 0; 18 | 19 | string _accountList; 20 | 21 | void ListAccounts() 22 | { 23 | var l = ""; 24 | foreach (var acct in Context.Accounts) 25 | { 26 | l += $"{acct.Id}:{acct.Details.Kid}:{string.Join(",", acct.Details.Payload.Contact)}\r\n"; 27 | } 28 | _accountList = l; 29 | } 30 | 31 | void CreateAccount() 32 | { 33 | var acct = new BlazorAccount 34 | { 35 | Details = new AccountDetails 36 | { 37 | Kid = Guid.NewGuid().ToString(), //"https://acme-staging-v02.api.letsencrypt.org/acme/acct/6440557", 38 | TosLink = "https://www.google.com/", 39 | Payload = new ACMESharp.Protocol.Resources.Account 40 | { 41 | Id = "6440557", 42 | Contact = new[] 43 | { 44 | "mailto:john.doe@mailinator.com", 45 | "mailto:jane.doe@mailinator.com", 46 | }, 47 | Status = "StillGoing!", 48 | Key = new 49 | { 50 | kty = "EC", 51 | crv = "P-256", 52 | x = "4yLZoZQdYHrypdiZudPDL649wBDmR4YjG2DHxX7MZEU", 53 | y = "rUvtY-AnB4eihRyFaqwwbDGM6ZNtVjFuepPweFuDp3c", 54 | }, 55 | }, 56 | }, 57 | }; 58 | 59 | Context.Accounts.Add(acct); 60 | Context.SaveChanges(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/JOSE/JwsHeaders.cs: -------------------------------------------------------------------------------- 1 | namespace ACMESharp.Crypto.JOSE 2 | { 3 | /// 4 | /// Contains the well-defined headers elements as specified in RFC7515. 5 | /// 6 | public static class JwsHeaders 7 | { 8 | /// 9 | /// RFC7515 4.1.1. "alg" (Algorithm) Header Parameter 10 | /// 11 | public const string ALG = "alg"; 12 | /// 13 | /// RFC7515 4.1.2. "jku" (JWK Set URL) Header Parameter 14 | /// 15 | public const string JKU = "jku"; 16 | /// 17 | /// RFC7515 4.1.3. "jwk" (JSON Web Key) Header Parameter 18 | /// 19 | public const string JWK = "jwk"; 20 | /// 21 | /// RFC7515 4.1.4. "kid" (Key ID) Header Parameter 22 | /// 23 | public const string KID = "kid"; 24 | /// 25 | /// RFC7515 4.1.5. "x5u" (X.509 URL) Header Parameter 26 | /// 27 | public const string X5U = "x5u"; 28 | /// 29 | /// RFC7515 4.1.6. "x5c" (X.509 Certificate Chain) Header Parameter 30 | /// 31 | public const string X5C = "x5c"; 32 | /// 33 | /// RFC7515 4.1.7. "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter 34 | /// 35 | public const string X5T = "x5t"; 36 | /// 37 | /// RFC7515 4.1.8. "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Header Parameter 38 | /// 39 | public const string X5TS56 = "x5t#256"; 40 | /// 41 | /// RFC7515 4.1.9. "typ" (Type) Header Parameter 42 | /// 43 | public const string TYP = "typ"; 44 | /// 45 | /// RFC7515 4.1.10. "cty" (Content Type) Header Parameter 46 | /// 47 | public const string CTY = "cty"; 48 | /// 49 | /// RFC7515 4.1.11. "crit" (Critical) Header Parameter 50 | /// 51 | public const string CRIT = "crit"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/AcmeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Server.Kestrel.Core; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ACMEKestrel 11 | { 12 | public static class AcmeExtensions 13 | { 14 | public static IWebHostBuilder AddAcmeServices(this IWebHostBuilder builder, 15 | IEnumerable dnsNames, 16 | IEnumerable contactEmails = null, 17 | bool acceptTos = false, 18 | string rootDir = null) 19 | { 20 | var acmeOptions = new AcmeOptions 21 | { 22 | AccountContactEmails = contactEmails, 23 | AcceptTermsOfService = acceptTos, 24 | DnsNames = dnsNames, 25 | }; 26 | if (rootDir != null) 27 | acmeOptions.AcmeRootDir = rootDir; 28 | return AddAcmeServices(builder, acmeOptions); 29 | } 30 | 31 | public static IWebHostBuilder AddAcmeServices(this IWebHostBuilder builder, AcmeOptions acmeOptions) 32 | { 33 | builder.ConfigureServices(services => { 34 | services.AddSingleton(acmeOptions); 35 | services.AddSingleton(AcmeState.Instance); 36 | services.AddHostedService(); 37 | }); 38 | builder.UseKestrel((context, options) => options.ConfigureHttpsDefaults(configOpts => { 39 | configOpts.ServerCertificateSelector = (cc, x) => { 40 | return AcmeState.Instance.Certificate; 41 | }; 42 | })); 43 | return builder; 44 | } 45 | 46 | public static IApplicationBuilder UseAcmeChallengeHandler(this IApplicationBuilder app) 47 | { 48 | app.Map($"/{AcmeHttp01ChallengeHandler.AcmeHttp01PathPrefix}", appBuilder => { 49 | appBuilder.Run(AcmeHttp01ChallengeHandler.HandleHttp01ChallengeRequest); 50 | }); 51 | 52 | return app; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/examples/Examples.Common.PKI/PkiJwsTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Threading.Tasks; 7 | using ACMESharp.Crypto; 8 | using ACMESharp.Crypto.JOSE; 9 | using Org.BouncyCastle.Crypto.Parameters; 10 | using PKISharp.SimplePKI; 11 | 12 | namespace Examples.Common.PKI 13 | { 14 | public class PkiJwsTool : IJwsTool 15 | { 16 | private PkiKeyPair _keys; 17 | 18 | private object _jwk; 19 | 20 | public PkiJwsTool(int bits) 21 | { 22 | Bits = bits; 23 | } 24 | 25 | public int Bits { get; } 26 | 27 | public string JwsAlg => $"ES{Bits}"; 28 | 29 | public PkiKeyPair KeyPair => _keys; 30 | 31 | public void Init() 32 | { 33 | _jwk = null; 34 | _keys = PkiKeyPair.GenerateEcdsaKeyPair(Bits); 35 | } 36 | 37 | public void Dispose() 38 | { 39 | _keys = null; 40 | _jwk = null; 41 | } 42 | 43 | public string Export() 44 | { 45 | using (var ms = new MemoryStream()) 46 | { 47 | _keys.Save(ms); 48 | return Convert.ToBase64String(ms.ToArray()); 49 | } 50 | } 51 | 52 | public void Import(string exported) 53 | { 54 | _jwk = null; 55 | using (var ms = new MemoryStream(Convert.FromBase64String(exported))) 56 | { 57 | _keys = PkiKeyPair.Load(ms); 58 | } 59 | } 60 | 61 | public object ExportJwk(bool canonical = false) 62 | { 63 | // Note, we only produce a canonical form of the JWK 64 | // for export therefore we ignore the canonical param 65 | 66 | if (_jwk == null) 67 | { 68 | _jwk = _keys.ExportJwk(); 69 | } 70 | 71 | return _jwk; 72 | } 73 | 74 | public byte[] Sign(byte[] raw) 75 | { 76 | return _keys.Sign(raw); 77 | } 78 | 79 | public bool Verify(byte[] raw, byte[] sig) 80 | { 81 | return _keys.Verify(raw, sig); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/ClientsFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using ACMESharp.Protocol; 4 | using DnsClient; 5 | 6 | namespace ACMESharp.IntegrationTests 7 | { 8 | /// 9 | /// Used to share an ACME client and related state 10 | /// across multiple test methods of a test class. 11 | /// 12 | public class ClientsFixture : IDisposable 13 | { 14 | public ClientsFixture() 15 | { 16 | Dns = new LookupClient(); 17 | Dns.UseCache = false; 18 | } 19 | 20 | public Uri BaseAddress { get; set; } 21 | 22 | public HttpClient Http { get; set; } 23 | 24 | public AcmeProtocolClient Acme { get; set; } 25 | 26 | public LookupClient Dns { get; set; } 27 | 28 | 29 | #region IDisposable Support 30 | private bool disposedValue = false; // To detect redundant calls 31 | 32 | protected virtual void Dispose(bool disposing) 33 | { 34 | if (!disposedValue) 35 | { 36 | if (disposing) 37 | { 38 | Acme?.Dispose(); 39 | Acme = null; 40 | Http?.Dispose(); 41 | Http = null; 42 | } 43 | 44 | // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. 45 | // TODO: set large fields to null. 46 | 47 | disposedValue = true; 48 | } 49 | } 50 | 51 | // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. 52 | // ~HttpClientFixture() { 53 | // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 54 | // Dispose(false); 55 | // } 56 | 57 | // This code added to correctly implement the disposable pattern. 58 | public void Dispose() 59 | { 60 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 61 | Dispose(true); 62 | // TODO: uncomment the following line if the finalizer is overridden above. 63 | // GC.SuppressFinalize(this); 64 | } 65 | #endregion 66 | } 67 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/PkiJwsTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Threading.Tasks; 7 | using ACMESharp.Crypto; 8 | using ACMESharp.Crypto.JOSE; 9 | using Org.BouncyCastle.Crypto.Parameters; 10 | using PKISharp.SimplePKI; 11 | 12 | namespace ACMEBlazor 13 | { 14 | public class PkiJwsTool : IJwsTool 15 | { 16 | private PkiKeyPair _keys; 17 | 18 | private object _jwk; 19 | 20 | public PkiJwsTool(int bits) 21 | { 22 | Bits = bits; 23 | } 24 | 25 | public int Bits { get; } 26 | 27 | public string JwsAlg => $"ES{Bits}"; 28 | 29 | public PkiKeyPair KeyPair => _keys; 30 | 31 | public void Init() 32 | { 33 | _jwk = null; 34 | _keys = PkiKeyPair.GenerateEcdsaKeyPair(Bits); 35 | } 36 | 37 | public void Dispose() 38 | { 39 | _keys = null; 40 | _jwk = null; 41 | } 42 | 43 | public string Export() 44 | { 45 | using (var ms = new MemoryStream()) 46 | { 47 | _keys.Save(ms); 48 | return Convert.ToBase64String(ms.ToArray()); 49 | } 50 | } 51 | 52 | public void Import(string exported) 53 | { 54 | _jwk = null; 55 | using (var ms = new MemoryStream(Convert.FromBase64String(exported))) 56 | { 57 | _keys = PkiKeyPair.Load(ms); 58 | } 59 | } 60 | 61 | public byte[] Sign(byte[] raw) 62 | { 63 | return _keys.Sign(raw); 64 | } 65 | 66 | public bool Verify(byte[] raw, byte[] sig) 67 | { 68 | return _keys.Verify(raw, sig); 69 | } 70 | 71 | public object ExportJwk(bool canonical = false) 72 | { 73 | // Note, we only produce a canonical form of the JWK 74 | // for export therefore we ignore the canonical param 75 | 76 | if (_jwk == null) // Use a cached JWK export if we have it 77 | { 78 | _jwk = _keys.ExportJwk(); 79 | } 80 | 81 | return _jwk; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tools/test-report/trx2md.ps1: -------------------------------------------------------------------------------- 1 | 2 | [CmdletBinding()] 3 | param( 4 | [Parameter(Mandatory)] 5 | [string]$trxFile, 6 | [string]$mdFile=$null, 7 | [string]$xslFile=$null 8 | ) 9 | 10 | if ($trxFile -notmatch '^[/\\]') { 11 | $trxFile = [System.IO.Path]::Combine($PWD, $trxFile) 12 | Write-Verbose "Resolving TRX file relative to current directory: $trxFile" 13 | } 14 | 15 | if (-not $mdFile) { 16 | $mdFile = $trxFile 17 | if ([System.IO.Path]::GetExtension($trxFile) -ieq '.trx') { 18 | $mdFile = $trxFile -ireplace '.trx$','' 19 | } 20 | $mdFile += '.md' 21 | Write-Verbose "Resolving default MD file: $mdFile" 22 | } 23 | elseif ($mdFile -notmatch '^[/\\]') { 24 | $mdFile = [System.IO.Path]::Combine($PWD, $mdFile) 25 | Write-Verbose "Resolving MD file relative to current directory: $mdFile" 26 | } 27 | 28 | if (-not $xslFile) { 29 | $xslFile = "$PSScriptRoot/trx2md.xsl" 30 | Write-Verbose "Resolving default XSL file: $xslFile" 31 | } 32 | elseif ($xslFile -notmatch '^[/\\]') { 33 | $xslFile = [System.IO.Path]::Combine($PWD, $xslFile) 34 | Write-Verbose "Resolving XSL file relative to current directory: $xslFile" 35 | 36 | } 37 | 38 | class TrxFn { 39 | [double]DiffSeconds([datetime]$from, [datetime]$till) { 40 | return ($till - $from).TotalSeconds 41 | } 42 | } 43 | 44 | 45 | if (-not $script:xslt) { 46 | $script:urlr = [System.Xml.XmlUrlResolver]::new() 47 | $script:opts = [System.Xml.Xsl.XsltSettings]::new() 48 | #$script:opts.EnableScript = $true 49 | $script:xslt = [System.Xml.Xsl.XslCompiledTransform]::new() 50 | try { 51 | $script:xslt.Load($xslFile, $script:opts, $script:urlr) 52 | } 53 | catch { 54 | Write-Error $Error[0] 55 | return 56 | } 57 | Write-Verbose "Loaded XSL transformer" 58 | } 59 | 60 | $script:list = [System.Xml.Xsl.XsltArgumentList]::new() 61 | $script:list.AddExtensionObject("urn:trxfn", [TrxFn]::new()) 62 | $script:wrtr = [System.IO.StreamWriter]::new($mdFile) 63 | try { 64 | Write-Verbose "Transforming TRX to MD" 65 | $script:xslt.Transform( 66 | [string]$trxFile, 67 | [System.Xml.Xsl.XsltArgumentList]$script:list, 68 | [System.IO.TextWriter]$script:wrtr) 69 | } 70 | finally { 71 | $script:wrtr.Dispose() 72 | } 73 | -------------------------------------------------------------------------------- /src/ACMESharp/README.md: -------------------------------------------------------------------------------- 1 | # README - ACMESharp Core 2 | 3 | ## Changes 4 | 5 | * Based on .NET Standard / .NET Core 6 | * Moved to HttpClient from WebRequest 7 | * Using async code throughout 8 | * Uses PO files for message localization? 9 | * Expanded support for RSA keys to include more SHA (348, 512) and RSA (2048-4096) sizes 10 | * Added support for EC keys for both account and cert keys, supporting standard curves 11 | * P-256 12 | * P-348 13 | * P-521 14 | * Improved Integration Testing setup 15 | * Better separation of concerns in the core API 16 | 17 | * Hoped to use some other, more complete and well maintained JOSE library for 18 | doing things like JWS signing and JWK-related operations, but unfortunately 19 | the landscape for this hasn't changed too much in that still needed to use 20 | an internal library for this. 21 | 22 | ## What's Implemented and Working 23 | 24 | * ACME Resource Directory Lookup 25 | * First Nonce Lookup 26 | * Create Account 27 | * Create/Check Duplicate Account 28 | * Update Account 29 | * Change Account Key 30 | * Deactivate Account 31 | * Create Order 32 | * Decode Challenge details for types: 33 | * `dns-01` 34 | * `http-01` 35 | * `tls-alpn-01` - initial support for [this extension](https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05) (thanks [Wouter](https://github.com/WouterTinus)) 36 | * Answer Challenge 37 | * Refresh Challenge 38 | * Deactivate Authorization 39 | * Finalize Authorization (Submit CSR) 40 | * Revoke Certificate (thank you [Wouter](https://github.com/WouterTinus)!) 41 | * Cross-platform support 42 | * Tested on [Windows](https://ci.appveyor.com/project/ebekker/acmesharpcore/build/job/6vive79j4xprmh93/tests) 43 | * Tested on [Linux](https://ci.appveyor.com/project/ebekker/acmesharpcore/build/job/1a528ap82uol4bsg/tests) (Ubuntu 16.04) 44 | 45 | ## What's Not Implemented/Not Working 46 | 47 | * Automatic detection/handling of Change of TOS from [ACME 7.3.4](https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3.4) is not implemented 48 | * External Account Binding from [ACME 7.3.5](https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3.5) has not been implemented 49 | * Order Pre-Authorizations from [ACME 7.4.1](https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.1) is not implemented 50 | 51 | ## Contributors 52 | 53 | * Translate PO files 54 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/XunitDebugging/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ACMESharp.IntegrationTests.Debugging 5 | { 6 | static class DictionaryExtensions 7 | { 8 | public static bool GetAndRemoveParameterWithoutValue(this Dictionary> dictionary, string key) 9 | { 10 | var result = TryGetParameterWithoutValue(dictionary, key); 11 | if (result) 12 | dictionary.Remove(key); 13 | 14 | return result; 15 | } 16 | 17 | public static string GetAndRemoveParameterWithValue(this Dictionary> dictionary, string key) 18 | { 19 | if (dictionary.TryGetSingleValue(key, out var result)) 20 | { 21 | if (result == null) 22 | throw new ArgumentException($"Missing value for option '{key}'"); 23 | 24 | dictionary.Remove(key); 25 | } 26 | 27 | return result; 28 | } 29 | 30 | public static bool TryGetAndRemoveParameterWithoutValue(this Dictionary> dictionary, string key) 31 | { 32 | var result = TryGetParameterWithoutValue(dictionary, key); 33 | if (result) 34 | dictionary.Remove(key); 35 | 36 | return result; 37 | } 38 | 39 | public static bool TryGetParameterWithoutValue(this Dictionary> dictionary, string key) 40 | { 41 | if (dictionary.TryGetSingleValue(key, out var result)) 42 | { 43 | if (result != null) 44 | throw new ArgumentException($"Option '{key}' should not have a value"); 45 | 46 | return true; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | public static bool TryGetSingleValue(this Dictionary> dictionary, string key, out string value) 53 | { 54 | value = null; 55 | 56 | if (!dictionary.TryGetValue(key, out var values)) 57 | return false; 58 | 59 | if (values.Count > 1) 60 | throw new ArgumentException($"Option '{key}' cannot be set more than once"); 61 | 62 | value = values[0]; 63 | return true; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Threading.Tasks; 8 | using ACMEKestrel.Crypto; 9 | using ACMESharp.Crypto; 10 | using Microsoft.AspNetCore; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.AspNetCore.Server.Kestrel.Https.Internal; 13 | using Microsoft.Extensions.Configuration; 14 | using Microsoft.Extensions.Logging; 15 | 16 | namespace ACMEKestrel 17 | { 18 | public class Program 19 | { 20 | static readonly IEnumerable DefaultDnsNames = new[] { 21 | "test1.example.com", 22 | "test-alt1.example.com", 23 | "test-alt21.example.com" 24 | }; 25 | 26 | static IEnumerable DnsNames { get; set; } 27 | 28 | public static void Main(string[] args) 29 | { 30 | var dnsNamesFile = ".\\_IGNORE\\dnsnames.json"; 31 | if (File.Exists(dnsNamesFile)) 32 | DnsNames = Newtonsoft.Json.JsonConvert.DeserializeObject>( 33 | File.ReadAllText(dnsNamesFile)); 34 | 35 | CreateWebHostBuilder(args).Build().Run(); 36 | } 37 | 38 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) 39 | { 40 | var builder = WebHost.CreateDefaultBuilder(args); 41 | 42 | // Simple Form: 43 | /* 44 | builder.AddAcmeServices( 45 | Program.DnsNames ?? DefaultDnsNames, 46 | contactEmails: new[] { "acmetest@mailinator.com" }, 47 | acceptTos: true) 48 | */ 49 | 50 | // Full Form with access to All Options: 51 | builder.AddAcmeServices(new AcmeOptions 52 | { 53 | AcmeRootDir = "_IGNORE/_acmesharp", 54 | DnsNames = Program.DnsNames ?? DefaultDnsNames, 55 | AccountContactEmails = new[] { "acmetest@mailinator.com" }, 56 | AcceptTermsOfService = true, 57 | CertificateKeyAlgor = "rsa", 58 | }); 59 | 60 | builder.UseStartup(); 61 | 62 | return builder; 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/Base64Tool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ACMESharp.Crypto 5 | { 6 | /// 7 | /// Collection of convenient crypto operations working 8 | /// with URL-safe Base64 encoding. 9 | /// 10 | public class Base64Tool 11 | { 12 | 13 | /// 14 | /// URL-safe Base64 encoding as prescribed in RFC 7515 Appendix C. 15 | /// 16 | public string UrlEncode(string raw, Encoding encoding = null) 17 | { 18 | if (encoding == null) 19 | encoding = Encoding.UTF8; 20 | return UrlEncode(encoding.GetBytes(raw)); 21 | } 22 | 23 | /// 24 | /// URL-safe Base64 encoding as prescribed in RFC 7515 Appendix C. 25 | /// 26 | public string UrlEncode(byte[] raw) 27 | { 28 | string enc = Convert.ToBase64String(raw); // Regular base64 encoder 29 | enc = enc.Split('=')[0]; // Remove any trailing '='s 30 | enc = enc.Replace('+', '-'); // 62nd char of encoding 31 | enc = enc.Replace('/', '_'); // 63rd char of encoding 32 | return enc; 33 | } 34 | 35 | /// 36 | /// URL-safe Base64 decoding as prescribed in RFC 7515 Appendix C. 37 | /// 38 | public byte[] UrlDecode(string enc) 39 | { 40 | string raw = enc; 41 | raw = raw.Replace('-', '+'); // 62nd char of encoding 42 | raw = raw.Replace('_', '/'); // 63rd char of encoding 43 | switch (raw.Length % 4) // Pad with trailing '='s 44 | { 45 | case 0: break; // No pad chars in this case 46 | case 2: raw += "=="; break; // Two pad chars 47 | case 3: raw += "="; break; // One pad char 48 | default: 49 | throw new System.Exception("Illegal base64url string!"); 50 | } 51 | return Convert.FromBase64String(raw); // Standard base64 decoder 52 | } 53 | 54 | public string UrlDecodeToString(string enc, Encoding encoding = null) 55 | { 56 | if (encoding == null) 57 | encoding = Encoding.UTF8; 58 | return encoding.GetString(UrlDecode(enc)); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/ACMESharp/Crypto/RsaTool.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Crypto 5 | { 6 | /// 7 | /// Collection of convenient crypto operations working 8 | /// with RSA keys and algorithms. 9 | /// 10 | public class RsaTool 11 | { 12 | public RSA GenerateAlgorithm(string rsaKeys) 13 | { 14 | var rsa = RSA.Create(); 15 | var keys = JsonConvert.DeserializeObject(rsaKeys); 16 | var rsaParams = new RSAParameters 17 | { 18 | D = keys.D, 19 | DP = keys.DP, 20 | DQ = keys.DQ, 21 | Exponent = keys.Exponent, 22 | InverseQ = keys.InverseQ, 23 | Modulus = keys.Modulus, 24 | P = keys.P, 25 | Q = keys.Q, 26 | }; 27 | rsa.ImportParameters(rsaParams); 28 | return rsa; 29 | } 30 | 31 | public string GenerateKeys(RSA rsa) 32 | { 33 | var rsaParams = rsa.ExportParameters(true); 34 | var keys = new RsaKeys 35 | { 36 | D = rsaParams.D, 37 | DP = rsaParams.DP, 38 | DQ = rsaParams.DQ, 39 | Exponent = rsaParams.Exponent, 40 | InverseQ = rsaParams.InverseQ, 41 | Modulus = rsaParams.Modulus, 42 | P = rsaParams.P, 43 | Q = rsaParams.Q, 44 | }; 45 | var json = JsonConvert.SerializeObject(keys); 46 | return json; 47 | } 48 | 49 | // We have to create our own structure to represent an export of RSA Keys because 50 | // there is no standard format or method supported for .NET Standard/Core yet. 51 | // https://github.com/dotnet/corefx/issues/23686 52 | // https://gist.github.com/Jargon64/5b172c452827e15b21882f1d76a94be4/ 53 | private class RsaKeys 54 | { 55 | public byte[] Modulus { get; set; } 56 | public byte[] Exponent { get; set; } 57 | public byte[] P { get; set; } 58 | public byte[] Q { get; set; } 59 | public byte[] DP { get; set; } 60 | public byte[] DQ { get; set; } 61 | public byte[] InverseQ { get; set; } 62 | public byte[] D { get; set; } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | end_of_line = crlf 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.{json,csproj,props,targets,sln}] 11 | indent_size = 2 12 | 13 | [*.cs] 14 | # Prefer "var" everywhere 15 | csharp_style_var_for_built_in_types = true : suggestion 16 | csharp_style_var_when_type_is_apparent = true : suggestion 17 | csharp_style_var_elsewhere = true : suggestion 18 | 19 | # Newline settings 20 | csharp_new_line_before_open_brace = all 21 | csharp_new_line_before_else = true 22 | csharp_new_line_before_catch = true 23 | csharp_new_line_before_finally = true 24 | csharp_new_line_before_members_in_object_initializers = true 25 | csharp_new_line_before_members_in_anonymous_types = true 26 | 27 | # Sort using and Import directives with System.* appearing first 28 | dotnet_sort_system_directives_first = true 29 | 30 | # Avoid "this." if not necessary 31 | dotnet_style_qualification_for_field = false : suggestion 32 | dotnet_style_qualification_for_property = false : suggestion 33 | dotnet_style_qualification_for_method = false : suggestion 34 | dotnet_style_qualification_for_event = false : suggestion 35 | 36 | # Use language keywords instead of framework type names for type references 37 | dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion 38 | dotnet_style_predefined_type_for_member_access = true : suggestion 39 | 40 | # Suggest more modern language features when available 41 | csharp_style_pattern_matching_over_is_with_cast_check = true : none 42 | csharp_style_pattern_matching_over_as_with_null_check = true : none 43 | csharp_style_inlined_variable_declaration = true : none 44 | csharp_style_throw_expression = true : none 45 | csharp_style_conditional_delegate_call = true : none 46 | 47 | dotnet_style_object_initializer = true : suggestion 48 | dotnet_style_collection_initializer = true : suggestion 49 | dotnet_style_coalesce_expression = true : suggestion 50 | dotnet_style_null_propagation = true : suggestion 51 | dotnet_style_explicit_tuple_names = true : suggestion -------------------------------------------------------------------------------- /src/examples/Examples.Common/DnsUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using DnsClient; 8 | using DnsClient.Protocol; 9 | 10 | namespace Examples.Common 11 | { 12 | public static class DnsUtil 13 | { 14 | private static LookupClient _Client; 15 | 16 | public static string[] DnsServers { get; set; } 17 | 18 | public static LookupClient Client 19 | { 20 | get 21 | { 22 | if (_Client == null) 23 | { 24 | lock (typeof(DnsUtil)) 25 | { 26 | if (_Client == null) 27 | { 28 | if (DnsServers?.Length > 0) 29 | { 30 | var nameServers = DnsServers.SelectMany(x => Dns.GetHostAddresses(x)).ToArray(); 31 | _Client = new DnsClient.LookupClient(nameServers); 32 | } 33 | else 34 | { 35 | _Client = new DnsClient.LookupClient(); 36 | } 37 | } 38 | } 39 | } 40 | return _Client; 41 | } 42 | } 43 | 44 | public static async Task> LookupRecordAsync(string type, string name) 45 | { 46 | var dnsType = (DnsClient.QueryType)Enum.Parse(typeof(DnsClient.QueryType), type); 47 | var dnsResp = await Client.QueryAsync(name, dnsType); 48 | 49 | if (dnsResp.HasError) 50 | { 51 | if ("Non-Existent Domain".Equals(dnsResp.ErrorMessage, 52 | StringComparison.OrdinalIgnoreCase)) 53 | return null; 54 | throw new Exception("DNS lookup error: " + dnsResp.ErrorMessage); 55 | } 56 | 57 | return dnsResp.AllRecords.SelectMany(x => x.ValueAsStrings()); 58 | } 59 | 60 | public static IEnumerable ValueAsStrings(this DnsResourceRecord drr) 61 | { 62 | switch (drr) 63 | { 64 | case TxtRecord txt: 65 | return txt.Text; 66 | default: 67 | return new[] { drr.ToString() }; 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Program.cs: -------------------------------------------------------------------------------- 1 | using ACMEBlazor.Services; 2 | using ACMEBlazor.Storage; 3 | using ACMESharp.Protocol; 4 | using Blazor.Extensions; 5 | using Blazor.Extensions.Storage; 6 | using BlazorDB; 7 | using Microsoft.AspNetCore.Blazor.Browser.Rendering; 8 | using Microsoft.AspNetCore.Blazor.Browser.Services; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using System; 11 | using System.Collections.Generic; 12 | 13 | namespace ACMEBlazor 14 | { 15 | public class Program 16 | { 17 | public const string LetsEncryptV2StagingEndpoint = "https://acme-staging-v02.api.letsencrypt.org/"; 18 | 19 | public const string LetsEncryptV2Endpoint = "https://acme-v02.api.letsencrypt.org/"; 20 | 21 | static void Main(string[] args) 22 | { 23 | var localStorage = new LocalStorage(); 24 | Console.WriteLine("Loading App State"); 25 | var app = new AppState(); 26 | Console.WriteLine(" * loading Account"); 27 | app.Account = localStorage.GetItem(AppState.AccountKey); 28 | if (app.Account != null) 29 | Console.WriteLine(" * got Account: " + app.Account.Details.Kid); 30 | 31 | Console.WriteLine(" * loading Orders"); 32 | var orderId = 1; 33 | var orders = new List(); 34 | var order = localStorage.GetItem(AppState.OrderKey + orderId); 35 | while (order != null) 36 | { 37 | orders.Add(order); 38 | ++orderId; 39 | order = localStorage.GetItem(AppState.OrderKey + orderId); 40 | } 41 | app.Orders = orders.ToArray(); 42 | Console.WriteLine(" * got Orders: " + app.Orders.Length); 43 | 44 | var serviceProvider = new BrowserServiceProvider(services => 45 | { 46 | //services.AddSingleton(new AcmeProtocolClient(new Uri(LetsEncryptV2StagingEndpoint))); 47 | 48 | services.AddSingleton(new Repository()); 49 | 50 | services.AddBlazorDB(options => 51 | { 52 | options.LogDebug = true; 53 | options.Assembly = typeof(Program).Assembly; 54 | }); 55 | 56 | services.AddStorage(); 57 | services.AddSingleton(app); 58 | }); 59 | 60 | new BrowserRenderer(serviceProvider).AddComponent("app"); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Resources/ServiceDirectory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace ACMESharp.Protocol.Resources 5 | { 6 | public class ServiceDirectory 7 | { 8 | [JsonExtensionData] 9 | private IDictionary _extra; 10 | 11 | public string Directory { get; set; } = "directory"; 12 | 13 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 14 | public string NewNonce { get; set; } //! = "acme/new-nonce"; 15 | 16 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 17 | public string NewAccount { get; set; } //! = "acme/new-acct"; 18 | 19 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 20 | public string NewOrder { get; set; } //! = "acme/new-order"; 21 | 22 | 23 | /// 24 | /// This is an optional resource that an ACME CA may support 25 | /// if it supports Pre-Authorizations. 26 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.1 27 | /// 28 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 29 | public string NewAuthz { get; set; } //! = "acme/new-authz"; 30 | 31 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 32 | public string RevokeCert { get; set; } //! = "acme/revoke-cert"; 33 | 34 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 35 | public string KeyChange { get; set; } //! = "acme/key-change"; 36 | 37 | public DirectoryMeta Meta { get; set; } 38 | 39 | public IEnumerable GetExtraNames() => _extra?.Keys; 40 | 41 | public object GetExtra(string name) => _extra?[name]; 42 | 43 | public void SetExtra(string name, object value) 44 | { 45 | if (_extra == null) 46 | _extra = new Dictionary(); 47 | _extra[name] = value; 48 | } 49 | } 50 | 51 | public class DirectoryMeta 52 | { 53 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 54 | public string TermsOfService { get; set; } 55 | 56 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 57 | public string Website { get; set; } 58 | 59 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 60 | public string[] CaaIdentities { get; set; } 61 | 62 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 63 | public string ExternalAccountRequired { get; set; } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/PKISharp.SimplePKI/Util/Base64Tool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace PKISharp.SimplePKI.Util 5 | { 6 | // TODO:!!!!!! 7 | // This needs to be reconciled with the version in ACMESharp!!!! 8 | 9 | /// 10 | /// Collection of convenient crypto operations working 11 | /// with URL-safe Base64 encoding. 12 | /// 13 | internal class Base64Tool 14 | { 15 | public static Base64Tool Instance = new Base64Tool(); 16 | 17 | 18 | /// 19 | /// URL-safe Base64 encoding as prescribed in RFC 7515 Appendix C. 20 | /// 21 | public string UrlEncode(string raw, Encoding encoding = null) 22 | { 23 | if (encoding == null) 24 | encoding = Encoding.UTF8; 25 | return UrlEncode(encoding.GetBytes(raw)); 26 | } 27 | 28 | /// 29 | /// URL-safe Base64 encoding as prescribed in RFC 7515 Appendix C. 30 | /// 31 | public string UrlEncode(byte[] raw) 32 | { 33 | string enc = Convert.ToBase64String(raw); // Regular base64 encoder 34 | enc = enc.Split('=')[0]; // Remove any trailing '='s 35 | enc = enc.Replace('+', '-'); // 62nd char of encoding 36 | enc = enc.Replace('/', '_'); // 63rd char of encoding 37 | return enc; 38 | } 39 | 40 | /// 41 | /// URL-safe Base64 decoding as prescribed in RFC 7515 Appendix C. 42 | /// 43 | public byte[] UrlDecode(string enc) 44 | { 45 | string raw = enc; 46 | raw = raw.Replace('-', '+'); // 62nd char of encoding 47 | raw = raw.Replace('_', '/'); // 63rd char of encoding 48 | switch (raw.Length % 4) // Pad with trailing '='s 49 | { 50 | case 0: break; // No pad chars in this case 51 | case 2: raw += "=="; break; // Two pad chars 52 | case 3: raw += "="; break; // One pad char 53 | default: 54 | throw new System.Exception("Illegal base64url string!"); 55 | } 56 | return Convert.FromBase64String(raw); // Standard base64 decoder 57 | } 58 | 59 | public string UrlDecodeToString(string enc, Encoding encoding = null) 60 | { 61 | if (encoding == null) 62 | encoding = Encoding.UTF8; 63 | return encoding.GetString(UrlDecode(enc)); 64 | } 65 | }} 66 | -------------------------------------------------------------------------------- /src/ACMESharp/AcmeAccount.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ACMESharp.Protocol.Resources; 3 | 4 | namespace ACMESharp 5 | { 6 | /// 7 | /// Represents the details of a registered ACME Account with a specific ACME CA. 8 | /// 9 | public class AcmeAccount : AcmeAccount.IAccountDetails 10 | { 11 | private Account _account; 12 | 13 | public AcmeAccount(Account account, string kid, string tosLink) 14 | { 15 | _account = account; 16 | 17 | Kid = kid; 18 | TosLink = tosLink; 19 | } 20 | 21 | Account IAccountDetails.AccountDetails => _account; 22 | 23 | public IEnumerable Contacts => _account?.Contact; 24 | 25 | public object PublicKey => _account?.Key; 26 | 27 | /// 28 | /// This is the Key Identifier used in most messages sent to the ACME CA after 29 | /// the initial Account registration. It references the public key that was 30 | /// registered with the Account in the JWS-signed Account create message. 31 | /// 32 | /// 33 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-6.2 34 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.3 35 | /// 36 | /// The KID is returned as a Location header in the Account 37 | /// creation response. 38 | /// 39 | /// 40 | /// 41 | public string Kid { get; } 42 | 43 | public string TosLink { get; } 44 | 45 | /// 46 | /// CA-assigned unique identifier for the Account. 47 | /// 48 | /// 49 | public string Id => _account?.Id; 50 | 51 | public AccountStatus Status { get; } 52 | 53 | public AccountStatus GeStatus() 54 | { 55 | switch (_account?.Status) 56 | { 57 | case "valid": return AccountStatus.Valid; 58 | case "deactivated": return AccountStatus.Deactivated; 59 | case "revoked": return AccountStatus.Revoked; 60 | default: return AccountStatus.Unknown; 61 | } 62 | } 63 | 64 | public enum AccountStatus 65 | { 66 | Unknown = 0, 67 | Valid = 1, 68 | Deactivated = 2, 69 | Revoked = 3, 70 | } 71 | 72 | public interface IAccountDetails 73 | { 74 | Account AccountDetails { get; } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Controllers/AcmeDirectoryController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ACMESharp.Protocol.Resources; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace ACMESharp.MockServer.Controllers 6 | { 7 | [Route(AcmeDirectoryController.ControllerRoute)] 8 | [ApiController] 9 | public class AcmeDirectoryController : ControllerBase 10 | { 11 | public const string ControllerRoute = "directory"; 12 | 13 | [HttpGet("")] 14 | public ActionResult GetDirectory() 15 | { 16 | var dir = new ServiceDirectory 17 | { 18 | Directory = Url.Action(nameof(GetDirectory), 19 | controller: null, 20 | values: null, protocol: Request.Scheme), 21 | NewNonce = Url.Action(nameof(AcmeController.NewNonce), 22 | controller: AcmeController.ControllerRoute, 23 | values: null, protocol: Request.Scheme), 24 | NewAccount = Url.Action(nameof(AcmeController.NewAccount), 25 | controller: AcmeController.ControllerRoute, 26 | values: null, protocol: Request.Scheme), 27 | NewOrder = Url.Action(nameof(AcmeController.NewOrder), 28 | controller: AcmeController.ControllerRoute, 29 | values: null, protocol: Request.Scheme), 30 | NewAuthz = null, 31 | KeyChange = null, 32 | RevokeCert = Url.Action(nameof(AcmeController.Revoke), 33 | controller: AcmeController.ControllerRoute, 34 | values: null, protocol: Request.Scheme), 35 | 36 | Meta = new DirectoryMeta 37 | { 38 | TermsOfService = Url.Action(nameof(GetTermsOfService), 39 | controller: null, values: null, protocol: Request.Scheme), 40 | Website = null, 41 | CaaIdentities = null, 42 | ExternalAccountRequired = null, 43 | } 44 | }; 45 | 46 | var random = Guid.NewGuid().ToString().Replace("-", ""); 47 | //dir.SetExtra($"prop_{random}", random); 48 | dir.SetExtra($"prop_{random}", 49 | "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/"); 50 | 51 | return dir; 52 | } 53 | 54 | [HttpGet("tos")] 55 | public ActionResult GetTermsOfService() 56 | { 57 | return "No Terms!"; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/ACMESharp.MockServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.HttpsPolicy; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Logging; 12 | using Microsoft.Extensions.Options; 13 | using PKISharp.SimplePKI; 14 | 15 | namespace ACMESharp.MockServer 16 | { 17 | public class Startup 18 | { 19 | public const string RepositoryFilePathEnvVar = "ACME_REPO_PATH"; 20 | public const string RepositoryFilePath = "acme-mockserver.db"; 21 | 22 | public Startup(IConfiguration configuration) 23 | { 24 | Configuration = configuration; 25 | } 26 | 27 | public IConfiguration Configuration { get; } 28 | 29 | // This method gets called by the runtime. Use this method to add services to the container. 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | var repoPath = Environment.GetEnvironmentVariable(RepositoryFilePathEnvVar) 33 | ?? RepositoryFilePath; 34 | var repo = Storage.Impl.LiteDbRepo.GetInstance(repoPath); 35 | 36 | services.AddSingleton(repo); 37 | services.AddSingleton(); 38 | services.AddSingleton(new CertificateAuthority(new CertificateAuthority.Options 39 | { 40 | CaKeyPairSavePath = "ca-kypr.save", 41 | KeyPairAlgorithm = PkiAsymmetricAlgorithm.Rsa, 42 | BitLength = 2048, 43 | CaCertificateSavePath = "ca-cert.save", 44 | CaSubjectName = "cn=mock-acme-ca", 45 | SignatureHashAlgorithm = PkiHashAlgorithm.Sha512, 46 | })); 47 | 48 | 49 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 50 | } 51 | 52 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 53 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 54 | { 55 | if (env.IsDevelopment()) 56 | { 57 | app.UseDeveloperExceptionPage(); 58 | } 59 | else 60 | { 61 | app.UseHsts(); 62 | } 63 | 64 | app.UseHttpsRedirection(); 65 | app.UseMvc(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/AcmeHttp01ChallengeHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using ACMESharp.Authorizations; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace ACMEKestrel 10 | { 11 | public class AcmeHttp01ChallengeHandler 12 | { 13 | public static readonly string AcmeHttp01PathPrefix = 14 | ACMESharp.Authorizations.Http01ChallengeValidationDetails.HttpPathPrefix.Trim('/'); 15 | 16 | public static async Task HandleHttp01ChallengeRequest(HttpContext context) 17 | { 18 | var logger = context.RequestServices.GetService>(); 19 | var state = context.RequestServices.GetService(); 20 | var fullPath = $"{context.Request.PathBase}{context.Request.Path}".Trim('/'); 21 | 22 | logger.LogInformation("Running ACME Challenge Request Handler"); 23 | if (state.Http01Responses.TryGetValue(fullPath, out var httpDetails)) 24 | { 25 | logger.LogInformation("Found match on [{0}] with response [{1}]", 26 | fullPath, httpDetails.HttpResourceValue); 27 | context.Response.ContentType = httpDetails.HttpResourceContentType; 28 | await context.Response.WriteAsync(httpDetails.HttpResourceValue); 29 | } 30 | else 31 | { 32 | logger.LogInformation("NO MATCH FOUND ON [{0}]", fullPath); 33 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 34 | await context.Response.WriteAsync("No matching ACME response path"); 35 | } 36 | } 37 | 38 | public static bool AddChallengeHandling(IServiceProvider services, IChallengeValidationDetails chlngDetails) 39 | { 40 | var logger = services.GetService>(); 41 | var state = services.GetService(); 42 | var httpDetails = chlngDetails as Http01ChallengeValidationDetails; 43 | if (httpDetails == null) 44 | { 45 | logger.LogInformation("Unable to handle non-Http01 Challenge details"); 46 | return false; 47 | } 48 | 49 | var fullPath = httpDetails.HttpResourcePath.Trim('/'); 50 | logger.LogInformation("Handling Challenges with HTTP full path of [{0}]", fullPath); 51 | state.Http01Responses[fullPath] = httpDetails; 52 | return true; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Shared/NavMenu.cshtml: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | 68 |
69 | 70 | @functions { 71 | bool collapseNavMenu = true; 72 | 73 | void ToggleNavMenu() 74 | { 75 | collapseNavMenu = !collapseNavMenu; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/StateFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Runtime.CompilerServices; 6 | using ACMESharp.Testing.Xunit; 7 | using Microsoft.Extensions.Logging; 8 | using Newtonsoft.Json; 9 | 10 | namespace ACMESharp.IntegrationTests 11 | { 12 | public class StateFixture 13 | { 14 | public StateFixture() 15 | { 16 | // Need a place to stash stuff 17 | if (!Directory.Exists("_TMP")) 18 | Directory.CreateDirectory("_TMP"); 19 | 20 | Factory = new LoggerFactory().AddFile("integration-tests.log"); 21 | } 22 | 23 | public ILoggerFactory Factory { get; } 24 | 25 | public Random Rng { get; } = new Random(); 26 | 27 | public void WriteTo(string saveName, byte[] value) 28 | { 29 | File.WriteAllBytes($"_TMP/{saveName}", value); 30 | } 31 | 32 | public void WriteTo(string saveName, string value) 33 | { 34 | File.WriteAllText($"_TMP/{saveName}", value); 35 | } 36 | 37 | public void AppendTo(string saveName, string value) 38 | { 39 | File.AppendAllText($"_TMP/{saveName}", value); 40 | } 41 | 42 | public string ReadFrom(string saveName) 43 | { 44 | var fromName = $"_TMP/{saveName}"; 45 | if (File.Exists(fromName)) 46 | return File.ReadAllText(fromName); 47 | 48 | return null; 49 | } 50 | 51 | public void ReadFrom(string saveName, out byte[] value) 52 | { 53 | var fromName = $"_TMP/{saveName}"; 54 | if (File.Exists(fromName)) 55 | value = File.ReadAllBytes(fromName); 56 | else 57 | value = null; 58 | } 59 | 60 | public void SaveObject(string saveName, object o) 61 | { 62 | var json = JsonConvert.SerializeObject(o, Formatting.Indented); 63 | WriteTo(saveName, json); 64 | } 65 | 66 | public T LoadObject(string saveName) 67 | { 68 | var json = ReadFrom(saveName); 69 | 70 | return json == null ? default(T) : JsonConvert.DeserializeObject(json); 71 | } 72 | 73 | public byte[] RandomBytes(int byteLen) 74 | { 75 | var bytes = new byte[byteLen]; 76 | Rng.NextBytes(bytes); 77 | return bytes; 78 | } 79 | 80 | public string RandomBytesString(int byteLen) => 81 | BitConverter.ToString(RandomBytes(byteLen)); 82 | } 83 | } -------------------------------------------------------------------------------- /src/examples/ACMEKestrel/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.HttpsPolicy; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | 13 | namespace ACMEKestrel 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.Configure(options => 28 | { 29 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 30 | options.CheckConsentNeeded = context => true; 31 | options.MinimumSameSitePolicy = SameSiteMode.None; 32 | }); 33 | 34 | 35 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 36 | } 37 | 38 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 39 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 40 | { 41 | if (env.IsDevelopment()) 42 | { 43 | app.UseDeveloperExceptionPage(); 44 | } 45 | else 46 | { 47 | app.UseExceptionHandler("/Error"); 48 | app.UseHsts(); 49 | } 50 | 51 | // Disable this so we can response to ACME challenges on port 80 52 | // A future improvement might be to copy and augment this Middleware 53 | // to detect ACME Challenge requests, and skip those requests 54 | // See: 55 | // https://github.com/aspnet/BasicMiddleware/blob/dev/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionBuilderExtensions.cs 56 | // https://github.com/aspnet/BasicMiddleware/blob/dev/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionMiddleware.cs 57 | //app.UseHttpsRedirection(); 58 | 59 | app.UseAcmeChallengeHandler(); 60 | app.UseStaticFiles(); 61 | app.UseCookiePolicy(); 62 | 63 | app.UseMvc(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/Pages/Orders.cshtml: -------------------------------------------------------------------------------- 1 | @page "/orders" 2 | @using BlazorContextMenu 3 | @inject HttpClient Http 4 | @addTagHelper *, BlazorContextMenu 5 | 6 | 12 | 13 |

ACME Orders

14 | 15 | 16 | Item 1 17 | Item 2 18 | Item 3 (disabled) 19 | 20 | 21 | Submenu 22 | 23 | Submenu Item 1 24 | Submenu Item 2 25 | 26 | 27 | 28 | 29 | 30 |

This component demonstrates fetching data from the server. (BEK: Right-click date for more options.)

31 | 32 | @if (forecasts == null) 33 | { 34 |

Loading...

35 | } 36 | else 37 | { 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | @foreach (var forecast in forecasts) 49 | { 50 | 51 | 57 | 58 | 59 | 60 | 61 | } 62 | 63 |
DateTemp. (C)Temp. (F)Summary
52 | 53 | @forecast.Date.ToShortDateString() 55 | 56 | @forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
64 | } 65 | 66 | @functions { 67 | WeatherForecast[] forecasts; 68 | 69 | protected override async Task OnInitAsync() 70 | { 71 | forecasts = await Http.GetJsonAsync("sample-data/weather.json"); 72 | } 73 | 74 | void OnClick(MenuItemClickEventArgs e) 75 | { 76 | Console.WriteLine($"Item Clicked => Menu: {e.ContextMenuId}, MenuTarget: {e.ContextMenuTargetId}, IsCanceled: {e.IsCanceled}, Item: {e.MenuItemElement}, MouseEvent: {e.MouseEvent}"); 77 | } 78 | 79 | class WeatherForecast 80 | { 81 | public DateTime Date { get; set; } 82 | public int TemperatureC { get; set; } 83 | public int TemperatureF { get; set; } 84 | public string Summary { get; set; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/ACMESharp.IntegrationTests/S3Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Amazon; 6 | using Amazon.Runtime; 7 | using Amazon.S3; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ACMESharp.IntegrationTests 11 | { 12 | public class S3Helper 13 | { 14 | public string AwsProfileName { get; set; } 15 | 16 | public string AwsRegion { get; set; } 17 | 18 | public string BucketName { get; set; } 19 | 20 | public string CannedAcl 21 | { 22 | get { return S3CannedAcl?.Value; } 23 | set 24 | { 25 | S3CannedAcl = S3CannedACL.FindValue(value); 26 | } 27 | } 28 | 29 | public S3CannedACL S3CannedAcl 30 | { get; set; } 31 | 32 | public async Task EditFile(string filePath, string contentType, string content) 33 | { 34 | #pragma warning disable 618 // "'StoredProfileCredentials' is obsolete..." 35 | var creds = new StoredProfileAWSCredentials("acmesharp-tests"); 36 | #pragma warning restore 618 37 | var reg = RegionEndpoint.GetBySystemName(AwsRegion); 38 | var delete = content == null; 39 | 40 | // We need to strip off any leading '/' in the path or 41 | // else it creates a path with an empty leading segment 42 | // if (filePath.StartsWith("/")) 43 | // filePath = filePath.Substring(1); 44 | filePath = filePath.Trim('/'); 45 | 46 | using (var s3 = new Amazon.S3.AmazonS3Client(creds, reg)) 47 | { 48 | if (delete) 49 | { 50 | var s3Requ = new Amazon.S3.Model.DeleteObjectRequest 51 | { 52 | BucketName = BucketName, 53 | Key = filePath, 54 | }; 55 | var s3Resp = await s3.DeleteObjectAsync(s3Requ); 56 | } 57 | else 58 | { 59 | using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(content))) 60 | { 61 | var s3Requ = new Amazon.S3.Model.PutObjectRequest 62 | { 63 | BucketName = BucketName, 64 | Key = filePath, 65 | ContentType = contentType, 66 | InputStream = ms, 67 | CannedACL = S3CannedAcl, 68 | }; 69 | 70 | var s3Resp = await s3.PutObjectAsync(s3Requ); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/ACMESharp/Protocol/Constants.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Headers; 2 | 3 | namespace ACMESharp.Protocol 4 | { 5 | public static class Constants 6 | { 7 | static Constants() 8 | { 9 | var asmVer = typeof(Constants).Assembly.GetName().Version; 10 | UserAgentHeaderValue = $"ACMESharp/{asmVer} (ACME 2.0)"; 11 | } 12 | 13 | /// 14 | /// Date Time format used by ACME notBefore and notAfter 15 | /// fields, as defined by 16 | /// RFC3339 and 17 | /// ISO 8601. 18 | /// 19 | public const string Rfc3339DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fffK"; 20 | 21 | /// 22 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-6.1 23 | /// 24 | /// 25 | /// Computed dynamically at assembly load to incorporate the full. 26 | /// 27 | public static readonly string UserAgentHeaderValue; 28 | 29 | /// 30 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-6.1 31 | /// 32 | public const string AcceptLanguageHeaderValue = "en-us"; 33 | 34 | /// 35 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-6.2 36 | /// 37 | public const string ContentTypeHeaderName = "Content-Type"; 38 | /// 39 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-6.2 40 | /// 41 | // public const string ContentTypeHeaderValue = "application/jose+json"; 42 | public static readonly MediaTypeHeaderValue JsonContentTypeHeaderValue = 43 | MediaTypeHeaderValue.Parse("application/jose+json"); 44 | public static readonly MediaTypeHeaderValue ProblemContentTypeHeaderValue = 45 | MediaTypeHeaderValue.Parse("application/problem+json"); 46 | 47 | /// 48 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-6.6 49 | /// 50 | public const string ErrorTypeNamespacePrefix = "urn:ietf:params:acme:error:"; 51 | 52 | /// 53 | /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.2 54 | /// 55 | public const string ReplayNonceHeaderName = "Replay-Nonce"; 56 | 57 | /// 58 | /// The Link Header Relation key used to identify a URL to the Terms-Of-Service. 59 | /// 60 | public const string TosLinkHeaderRelationKey = "terms-of-service"; 61 | } 62 | } -------------------------------------------------------------------------------- /test/ACMESharp.UnitTests/JwsHelperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using ACMESharp.Crypto.JOSE; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace ACMESharp.UnitTests 7 | { 8 | [TestClass] 9 | public class JwsHelperTests 10 | { 11 | [TestMethod] 12 | [TestCategory("skipCI")] 13 | public void TestSignFlagJson() 14 | { 15 | Func sigFunc = (x) => 16 | { 17 | using (var rsa = new System.Security.Cryptography.RSACryptoServiceProvider()) 18 | { 19 | rsa.ImportParameters(JwsTests.GetRsaParamsForRfc7515Example_A_2_1()); 20 | using (var sha256 = new System.Security.Cryptography.SHA256CryptoServiceProvider()) 21 | { 22 | return rsa.SignData(x, sha256); 23 | } 24 | } 25 | }; 26 | 27 | object protectedSample = new // From the RFC example 28 | { 29 | alg = "RS256" 30 | }; 31 | object headerSample = new // From the RFC example 32 | { 33 | kid = "2010-12-29" 34 | }; 35 | string payloadSample = // From the RFC example 36 | "{\"iss\":\"joe\",\r\n" + 37 | " \"exp\":1300819380,\r\n" + 38 | " \"http://example.com/is_root\":true}"; 39 | 40 | var wsRegex = new Regex("\\s+"); 41 | var sigExpected = // Derived from the RFC example in A.6.4 42 | wsRegex.Replace(@"{ 43 | ""header"":{""kid"":""2010-12-29""}, 44 | ""protected"":""eyJhbGciOiJSUzI1NiJ9"", 45 | ""payload"":""eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"", 46 | ""signature"": 47 | ""cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ 48 | mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb 49 | KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl 50 | b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES 51 | c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX 52 | LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"" 53 | }", ""); 54 | var sigActual = wsRegex.Replace(JwsHelper.SignFlatJson( 55 | sigFunc, payloadSample, protectedSample, headerSample), ""); 56 | Assert.AreEqual(sigExpected, sigActual); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/examples/ACMEBlazor/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | app { 8 | position: relative; 9 | display: flex; 10 | flex-direction: column; 11 | } 12 | 13 | .top-row { 14 | height: 3.5rem; 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .main { 20 | flex: 1; 21 | } 22 | 23 | .main .top-row { 24 | background-color: #e6e6e6; 25 | border-bottom: 1px solid #d6d5d5; 26 | } 27 | 28 | .sidebar { 29 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 30 | } 31 | 32 | .sidebar .top-row { 33 | background-color: rgba(0,0,0,0.4); 34 | } 35 | 36 | .sidebar .navbar-brand { 37 | font-size: 1.1rem; 38 | } 39 | 40 | .sidebar .oi { 41 | width: 2rem; 42 | font-size: 1.1rem; 43 | vertical-align: text-top; 44 | top: -2px; 45 | } 46 | 47 | .nav-item { 48 | font-size: 0.9rem; 49 | padding-bottom: 0.5rem; 50 | } 51 | 52 | .nav-item:first-of-type { 53 | padding-top: 1rem; 54 | } 55 | 56 | .nav-item:last-of-type { 57 | padding-bottom: 1rem; 58 | } 59 | 60 | .nav-item a { 61 | color: #d7d7d7; 62 | border-radius: 4px; 63 | height: 3rem; 64 | display: flex; 65 | align-items: center; 66 | line-height: 3rem; 67 | } 68 | 69 | .nav-item a.active { 70 | background-color: rgba(255,255,255,0.25); 71 | color: white; 72 | } 73 | 74 | .nav-item a:hover { 75 | background-color: rgba(255,255,255,0.1); 76 | color: white; 77 | } 78 | 79 | .content { 80 | padding-top: 1.1rem; 81 | } 82 | 83 | .navbar-toggler { 84 | background-color: rgba(255, 255, 255, 0.1); 85 | } 86 | 87 | @media (max-width: 767.98px) { 88 | .main .top-row { 89 | display: none; 90 | } 91 | } 92 | 93 | @media (min-width: 768px) { 94 | app { 95 | flex-direction: row; 96 | } 97 | 98 | .sidebar { 99 | width: 250px; 100 | height: 100vh; 101 | position: sticky; 102 | top: 0; 103 | } 104 | 105 | .main .top-row { 106 | position: sticky; 107 | top: 0; 108 | } 109 | 110 | .main > div { 111 | padding-left: 2rem !important; 112 | padding-right: 1.5rem !important; 113 | } 114 | 115 | .navbar-toggler { 116 | display: none; 117 | } 118 | 119 | .sidebar .collapse { 120 | /* Never collapse the sidebar for wide screens */ 121 | display: block; 122 | } 123 | } 124 | --------------------------------------------------------------------------------