diff --git a/.vs/ACME.net/v14/.suo b/.vs/ACME.net/v14/.suo
index d41d029..7bc6b64 100644
Binary files a/.vs/ACME.net/v14/.suo and b/.vs/ACME.net/v14/.suo differ
diff --git a/README.md b/README.md
index 42aa741..26805ca 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,26 @@ take a look at these projects:
[.net ACME protocol library](https://github.com/ebekker/letsencrypt-win/).
[A simple ACME Client for Windows](https://github.com/Lone-Coder/letsencrypt-win-simple)
+# Using acme.exe
+
+You can use acme.exe with or without IIS integration. With IIS integration, acme.exe autoamtically
+configures your IIS to respond to the ACME domain validation challenge, and it updates your IIS
+web site with the new SSL certificate. To use IIS integration, you must run acme.exe on your IIS web
+server.
+
+Examples:
+
+Request a certificate for www.yourdomain.com and accept the terms of service of the ACME server (-a):
+'''
+acme.exe -a www.yourdomain.com
+'''
+
+If you don't want to use IIS integration or can't use it / you are not using IIS, you can also
+run acme.exe without IIS support. In that case, you need to manually copy the challenge file
+that is required to validate domain ownership to your server.
+
+Request a certificate for www.yourdomain.com without IIS integration
+
# Projects in this repository
## Oocx.ACME
diff --git a/src/Oocx.ACME.CLRConsole/Oocx.ACME.CLRConsole.csproj b/src/Oocx.ACME.CLRConsole/Oocx.ACME.CLRConsole.csproj
index cf9c195..f0e4d81 100644
--- a/src/Oocx.ACME.CLRConsole/Oocx.ACME.CLRConsole.csproj
+++ b/src/Oocx.ACME.CLRConsole/Oocx.ACME.CLRConsole.csproj
@@ -61,7 +61,8 @@
..\..\artifacts\bin\Oocx.ACME.Console\Debug\net46\Oocx.ACME.Console.dll
True
-
+
+ False
..\..\artifacts\bin\Oocx.ACME.IIS\Debug\net46\Oocx.ACME.IIS.dll
diff --git a/src/Oocx.ACME.Console/ContainerConfiguration.cs b/src/Oocx.ACME.Console/ContainerConfiguration.cs
index 8f179d5..ff196a3 100644
--- a/src/Oocx.ACME.Console/ContainerConfiguration.cs
+++ b/src/Oocx.ACME.Console/ContainerConfiguration.cs
@@ -30,7 +30,7 @@ public IContainer Configure(Options options)
builder.RegisterType().As().WithParameter("basePath", options.AccountKeyContainerLocation ?? Environment.CurrentDirectory);
}
- if ("manual".Equals(options.ChallengeProvider, StringComparison.OrdinalIgnoreCase))
+ if ("manual-http-01".Equals(options.ChallengeProvider, StringComparison.OrdinalIgnoreCase))
{
builder.RegisterType().As();
}
@@ -50,7 +50,7 @@ public IContainer Configure(Options options)
}
else
{
- builder.RegisterType().As();
+ builder.RegisterType().As();
}
builder.RegisterType().As();
diff --git a/src/Oocx.ACME.Console/Options.cs b/src/Oocx.ACME.Console/Options.cs
index 485bf17..87c3955 100644
--- a/src/Oocx.ACME.Console/Options.cs
+++ b/src/Oocx.ACME.Console/Options.cs
@@ -20,7 +20,7 @@ public class Options
[Option('t', "termsOfServiceUri", HelpText = "The uri of the terms of service that you accept.", Default = "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf")]
public string TermsOfServiceUri { get; set; }
- [Option('i', "ignoreSSLErrors", HelpText = "Ignore SSL certificate errors for the HTTPS connection to the ACME server (useful for debugging messages with fiddler)")]
+ [Option("ignoreSSLErrors", HelpText = "Ignore SSL certificate errors for the HTTPS connection to the ACME server (useful for debugging messages with fiddler)")]
public bool IgnoreSSLCertificateErrors { get; set; }
[Option('v', "verbosity", HelpText = "Configures the log level (Verbose, Info, Warning, Error, Disabled - casing is important).", Default = LogLevel.Info)]
@@ -35,10 +35,10 @@ public class Options
[Option('n', "accountKeyName", HelpText = "The name of the key file or key container used to store the acme registration key.", Default = "acme-key")]
public string AccountKeyName { get; set; }
- [Option('c', "challengeProvider", HelpText = "The type of web server integration to use for ACME challenges. Supported types are: 'manual' (no integration), 'iis-http-01' (IIS with http-01 challenge)", Default = "iis-http-01")]
+ [Option('c', "challengeProvider", HelpText = "The type of web server integration to use for ACME challenges. Supported types are: 'manual-http-01' (no integration with http-01 challenge), 'iis-http-01' (IIS with http-01 challenge)", Default = "iis-http-01")]
public string ChallengeProvider { get; set; }
- [Option('s', "serverConfigurationProvider", HelpText = "The type of web server configuration to use to install and configure certificates. Supported types are: 'manual' (no integration), 'iis' (installs certificates to localmachine\\my and configures IIS bindings)", Default = "iis")]
+ [Option('i', "serverConfigurationProvider", HelpText = "The type of web server configuration to use to install and configure certificates. Supported types are: 'manual' (no integration), 'iis' (installs certificates to localmachine\\my and configures IIS bindings)", Default = "iis")]
public string ServerConfigurationProvider { get; set; }
[Option('w', "iisWebSite", HelpText = "The IIS web site that should be configured to use the new certificate (used with --serverConfigurationProvider iis). If you do not specifiy a web site, the provider will try to find a matching site with a binding for your domain.")]
diff --git a/src/Oocx.ACME.Console/Program.cs b/src/Oocx.ACME.Console/Program.cs
index 71294d9..a73db0b 100644
--- a/src/Oocx.ACME.Console/Program.cs
+++ b/src/Oocx.ACME.Console/Program.cs
@@ -35,7 +35,7 @@ private void Execute(Options options)
return;
}
- var process = container.Resolve(new Parameter[] { new NamedParameter("options", options) });
+ var process = container.Resolve(new NamedParameter("options", options));
process.StartAsync().GetAwaiter().GetResult();
}
catch (AggregateException ex)
diff --git a/src/Oocx.ACME.IIS/Oocx.ACME.IIS.xproj b/src/Oocx.ACME.IIS/Oocx.ACME.IIS.xproj
index b5270ed..381fae3 100644
--- a/src/Oocx.ACME.IIS/Oocx.ACME.IIS.xproj
+++ b/src/Oocx.ACME.IIS/Oocx.ACME.IIS.xproj
@@ -4,7 +4,6 @@
14.0
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
-
cc1c7776-20ec-41a1-ad04-e7e19319b1a9
@@ -12,9 +11,11 @@
..\..\artifacts\obj\$(MSBuildProjectName)
..\..\artifacts\bin\$(MSBuildProjectName)\
-
2.0
+
+ True
+
-
+
\ No newline at end of file
diff --git a/src/Oocx.ACME/Client/AcmeClient.cs b/src/Oocx.ACME/Client/AcmeClient.cs
index 0233e03..76fb7c9 100644
--- a/src/Oocx.ACME/Client/AcmeClient.cs
+++ b/src/Oocx.ACME/Client/AcmeClient.cs
@@ -76,7 +76,12 @@ public async Task RegisterAsync(string termsOfServiceUri,
{
var location = ex.Response.Headers.Location.ToString();
Info($"using existing registration: {location}");
- return await PostAsync(new Uri(location), new UpdateRegistrationRequest());
+ var response = await PostAsync(new Uri(location), new UpdateRegistrationRequest());
+ if (string.IsNullOrEmpty(response.Location))
+ {
+ response.Location = location;
+ }
+ return response;
}
}
@@ -151,8 +156,7 @@ public async Task NewCertificateRequestAsync(byte[] csr)
var request = new CertificateRequest {Csr = csr.Base64UrlEncoded()};
var response = await PostAsync(directory.NewCertificate, request);
- Verbose($"location: {response.Location}");
- Verbose($"link: {response.Link}");
+ Verbose($"location: {response.Location}");
return response;
}
@@ -219,14 +223,30 @@ private async Task SendAsync(HttpMethod method, Uri uri, objec
private static void GetHeaderValues(HttpResponseMessage response, TResult responseContent)
{
var properties =
- typeof (TResult).GetProperties(BindingFlags.Public | BindingFlags.SetProperty)
+ typeof (TResult).GetProperties(BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof (string))
.ToDictionary(p => p.Name, p => p);
foreach (var header in response.Headers)
{
- if (properties.ContainsKey(header.Key))
+ if (properties.ContainsKey(header.Key) && header.Value.Count() == 1)
+ {
+ properties[header.Key].SetValue(responseContent, header.Value.First());
+ }
+
+ if (header.Key == "Link")
{
- properties[header.Key].SetValue(responseContent, header.Value);
+ foreach (var link in header.Value)
+ {
+ var parts = link.Split(';');
+ if (parts.Length != 2)
+ {
+ continue;
+ }
+ if (parts[1] == "rel=\"terms-of-service\"" && properties.ContainsKey("Agreement"))
+ {
+ properties["Agreement"].SetValue(responseContent, parts[0].Substring(1, parts[0].Length - 2));
+ }
+ }
}
}
}
diff --git a/src/Oocx.ACME/Client/ManualChallengeProvider.cs b/src/Oocx.ACME/Client/ManualChallengeProvider.cs
index 29d7089..35a1782 100644
--- a/src/Oocx.ACME/Client/ManualChallengeProvider.cs
+++ b/src/Oocx.ACME/Client/ManualChallengeProvider.cs
@@ -40,9 +40,9 @@ public async Task AcceptChallengeAsync(string domain, string s
return new PendingChallenge()
{
- Instructions = $"Copy {challengeFile} to https://{domain ?? siteName}/.well-known/acme/{challenge.Token}",
+ Instructions = $"Copy {challengeFile} to https://{domain ?? siteName}/.well-known/acme-challenge/{challenge.Token}",
Complete = () => client.CompleteChallengeAsync(challenge)
};
}
- }
+ }
}
\ No newline at end of file
diff --git a/src/Oocx.ACME/Client/ManualServerConfigurationPRovider.cs b/src/Oocx.ACME/Client/ManualServerConfigurationPRovider.cs
new file mode 100644
index 0000000..43b3c59
--- /dev/null
+++ b/src/Oocx.ACME/Client/ManualServerConfigurationPRovider.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Security.Cryptography;
+using Oocx.ACME.Common;
+using Oocx.ACME.Services;
+
+namespace Oocx.ACME.Client
+{
+ public class ManualServerConfigurationProvider : IServerConfigurationProvider
+ {
+ public byte[] InstallCertificateWithPrivateKey(string certificatePath, string certificateStoreName, RSAParameters privateKey)
+ {
+ return null;
+ }
+
+ public void ConfigureServer(string domain, byte[] certificateHash, string certificateStoreName, string siteName, string binding)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Oocx.ACME/Protocol/CertificateResponse.cs b/src/Oocx.ACME/Protocol/CertificateResponse.cs
index f1f3470..c237f74 100644
--- a/src/Oocx.ACME/Protocol/CertificateResponse.cs
+++ b/src/Oocx.ACME/Protocol/CertificateResponse.cs
@@ -2,9 +2,7 @@ namespace Oocx.ACME.Protocol
{
public class CertificateResponse
{
- public string Location { get; set; }
-
- public string Link { get; set; }
+ public string Location { get; set; }
public byte[] Certificate { get; set; }
}