summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.lib/Clients/IIS/IISHelper.cs2
-rw-r--r--src/main.lib/Services/CertificateService.cs106
-rw-r--r--src/main.test/Tests/TargetPluginTests/IISSitesTests.cs4
3 files changed, 75 insertions, 37 deletions
diff --git a/src/main.lib/Clients/IIS/IISHelper.cs b/src/main.lib/Clients/IIS/IISHelper.cs
index 9e8771a..93d08ce 100644
--- a/src/main.lib/Clients/IIS/IISHelper.cs
+++ b/src/main.lib/Clients/IIS/IISHelper.cs
@@ -102,7 +102,6 @@ namespace PKISharp.WACS.Clients.IIS
Https = sbi.https
}).
DistinctBy(t => t.HostUnicode + "@" + t.SiteId).
- OrderBy(t => t.HostUnicode).
ToList();
return targets;
@@ -213,6 +212,7 @@ namespace PKISharp.WACS.Clients.IIS
{
return site.Bindings.Select(x => x.Host.ToLower()).
Where(x => !string.IsNullOrWhiteSpace(x)).
+ OrderBy(x => x).
Distinct().
ToList();
}
diff --git a/src/main.lib/Services/CertificateService.cs b/src/main.lib/Services/CertificateService.cs
index 5156ed9..70d5b50 100644
--- a/src/main.lib/Services/CertificateService.cs
+++ b/src/main.lib/Services/CertificateService.cs
@@ -21,6 +21,9 @@ namespace PKISharp.WACS.Services
{
internal class CertificateService : ICertificateService
{
+ private const string CsrPostFix = "-csr.pem";
+ private const string PfxPostFix = "-temp.pfx";
+
private readonly IInputService _inputService;
private readonly ILogService _log;
private readonly ISettingsService _settings;
@@ -82,11 +85,11 @@ namespace PKISharp.WACS.Services
/// Delete cached files related to a specific renewal
/// </summary>
/// <param name="renewal"></param>
- private void ClearCache(Renewal renewal)
+ private void ClearCache(Renewal renewal, string prefix = "*", string postfix = "*")
{
- foreach (var f in _cache.GetFiles($"{renewal.Id}*"))
+ foreach (var f in _cache.GetFiles($"{prefix}{renewal.Id}{postfix}"))
{
- _log.Verbose("Deleting {file} from cache", f.Name);
+ _log.Verbose("Deleting {file} from certificate cache @ {folder}", f.Name, _cache.FullName);
f.Delete();
}
}
@@ -121,36 +124,49 @@ namespace PKISharp.WACS.Services
/// <returns></returns>
public CertificateInfo? CachedInfo(Renewal renewal, Target? target = null)
{
- var fullPattern = PfxFilePattern(renewal, "*");
- var directory = new DirectoryInfo(Path.GetDirectoryName(fullPattern));
- var filePattern = Path.GetFileName(fullPattern);
- var allFiles = directory.GetFiles(filePattern);
- var pfxFileInfo = allFiles.
- OrderByDescending(x => x.LastWriteTime).
- FirstOrDefault();
+ var nameAll = GetPath(renewal, $"*{PfxPostFix}");
+ var directory = new DirectoryInfo(Path.GetDirectoryName(nameAll));
+ var allPattern = Path.GetFileName(nameAll);
+ var allFiles = directory.GetFiles(allPattern);
+ if (!allFiles.Any())
+ {
+ return null;
+ }
+ FileInfo? fileCache = null;
if (target != null)
{
- var cacheKey = CacheKey(renewal, target);
- var fileName = Path.GetFileName(PfxFilePattern(renewal, $"-{cacheKey}-"));
- var specificFile = allFiles.Where(x => x.Name == fileName).FirstOrDefault();
- if (specificFile != null)
+ var key = CacheKey(renewal, target);
+ var keyName = Path.GetFileName(GetPath(renewal, $"-{key}{PfxPostFix}"));
+ var keyFile = allFiles.Where(x => x.Name == keyName).FirstOrDefault();
+ if (keyFile != null)
{
- pfxFileInfo = specificFile;
+ fileCache = keyFile;
+ }
+ else
+ {
+ var legacyName = Path.GetFileName(GetPath(renewal, PfxPostFix));
+ var legacyFile = allFiles.Where(x => x.Name == legacyName).FirstOrDefault();
+ if (legacyFile != null)
+ {
+ var legacyInfo = FromCache(legacyFile, renewal.PfxPassword?.Value);
+ if (Match(legacyInfo, target))
+ {
+ fileCache = legacyFile;
+ }
+ }
}
}
-
- // Delete other (older) cache files
- foreach (var other in allFiles.Except(new[] { pfxFileInfo }))
+ else
{
- other.Delete();
+ fileCache = allFiles.OrderByDescending(x => x.LastWriteTime).FirstOrDefault();
}
-
- if (pfxFileInfo != null)
+
+ if (fileCache != null)
{
try
{
- return FromCache(pfxFileInfo, renewal.PfxPassword?.Value);
+ return FromCache(fileCache, renewal.PfxPassword?.Value);
}
catch
{
@@ -162,6 +178,22 @@ namespace PKISharp.WACS.Services
}
/// <summary>
+ /// See if the information in the certificate matches
+ /// that of the specified target. Used to figure out whether
+ /// or not the cache is out of date.
+ /// </summary>
+ /// <param name="target"></param>
+ /// <returns></returns>
+ private bool Match(CertificateInfo info, Target target)
+ {
+ var identifiers = target.GetHosts(false);
+ var idn = new IdnMapping();
+ return info.SubjectName == idn.GetAscii(target.CommonName) &&
+ info.HostNames.Count == identifiers.Count() &&
+ info.HostNames.All(h => identifiers.Contains(idn.GetAscii(h)));
+ }
+
+ /// <summary>
/// To check if it's possible to reuse a previously retrieved
/// certificate we create a hash of its key properties and included
/// that hash in the file name. If we get the same hash on a
@@ -197,7 +229,7 @@ namespace PKISharp.WACS.Services
{
// What are we going to get?
var cacheKey = CacheKey(renewal, target);
- var pfxFileInfo = new FileInfo(PfxFilePattern(renewal, cacheKey));
+ var pfxFileInfo = new FileInfo(GetPath(renewal, $"-{cacheKey}{PfxPostFix}"));
// Determine/check the common name
var identifiers = target.GetHosts(false);
@@ -255,10 +287,11 @@ namespace PKISharp.WACS.Services
return cache;
}
}
- // Cache is present but not used anymore
- cache.CacheFile.Delete();
}
+ // Clear cache and write new cert
+ ClearCache(renewal, postfix: CsrPostFix);
+
if (target.CsrBytes == null)
{
if (csrPlugin == null)
@@ -267,9 +300,13 @@ namespace PKISharp.WACS.Services
}
var keyFile = GetPath(renewal, ".keys");
var csr = await csrPlugin.GenerateCsr(keyFile, commonNameAscii, identifiers);
+ var keySet = await csrPlugin.GetKeys();
target.CsrBytes = csr.GetDerEncoded();
- target.PrivateKey = (await csrPlugin.GetKeys()).Private;
- File.WriteAllText(GetPath(renewal, "-csr.pem"), _pemService.GetPem("CERTIFICATE REQUEST", target.CsrBytes));
+ target.PrivateKey = keySet.Private;
+ var csrPath = GetPath(renewal, CsrPostFix);
+ File.WriteAllText(csrPath, _pemService.GetPem("CERTIFICATE REQUEST", target.CsrBytes));
+ _log.Debug("CSR stored at {path} in certificate cache folder {folder}", Path.GetFileName(csrPath), Path.GetDirectoryName(csrPath));
+
}
_log.Verbose("Submitting CSR");
@@ -346,7 +383,15 @@ namespace PKISharp.WACS.Services
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
+
+ ClearCache(renewal, postfix: $"*{PfxPostFix}");
File.WriteAllBytes(pfxFileInfo.FullName, tempPfx.Export(X509ContentType.Pfx, renewal.PfxPassword?.Value));
+ _log.Debug("Certificate written to cache file {path} in certificate cache folder {folder}. It will be " +
+ "reused when renewing within {x} day(s) as long as the Target and Csr parameters remain the same and " +
+ "the --force switch is not used.",
+ pfxFileInfo.Name,
+ pfxFileInfo.Directory.FullName,
+ _settings.Cache.ReuseDays);
if (csrPlugin != null)
{
@@ -452,13 +497,6 @@ namespace PKISharp.WACS.Services
}
/// <summary>
- /// Path to the cached PFX file
- /// </summary>
- /// <param name="renewal"></param>
- /// <returns></returns>
- private string PfxFilePattern(Renewal renewal, string cacheKey) => GetPath(renewal, $"{cacheKey}temp.pfx");
-
- /// <summary>
/// Common filter for different store plugins
/// </summary>
/// <param name="friendlyName"></param>
diff --git a/src/main.test/Tests/TargetPluginTests/IISSitesTests.cs b/src/main.test/Tests/TargetPluginTests/IISSitesTests.cs
index d20eb61..028a3e9 100644
--- a/src/main.test/Tests/TargetPluginTests/IISSitesTests.cs
+++ b/src/main.test/Tests/TargetPluginTests/IISSitesTests.cs
@@ -134,8 +134,8 @@ namespace PKISharp.WACS.UnitTests.Tests.TargetPluginTests
var site = iis.GetWebSite(siteId);
var options = new IISSitesOptions() { SiteIds = new List<long>() { 1, 2 }, CommonName = "missing.example.com" };
var target = Target(options);
- Assert.AreEqual(target.IsValid(log), true);
- Assert.AreEqual(target.CommonName, site.Bindings.First().Host);
+ Assert.AreEqual(true, target.IsValid(log));
+ Assert.AreEqual(site.Bindings.First().Host, target.CommonName);
}
[TestMethod]