const float renewalPeriod = 60; // can't easily make this a command line option since it would have to be saved static void ScheduleRenewal(TargetBinding binding) { EnsureTaskScheduler(); var renewals = settings.LoadRenewals(); foreach (var existing in from r in renewals.ToArray() where r.Binding.Host == binding.Host select r) { Console.WriteLine($" Removing existing scheduled renewal {existing}"); renewals.Remove(existing); } var result = new ScheduledRenewal() { Binding = binding, Date = DateTime.UtcNow.AddDays(renewalPeriod) }; renewals.Add(result); settings.SaveRenewals(renewals); Console.WriteLine($" Renewal Scheduled {result}"); }
static string GetCertificate(TargetBinding binding) { var dnsIdentifier = binding.Host; var rsaKeys = CsrHelper.GenerateRsaPrivateKey(); var csrDetails = new CsrHelper.CsrDetails { CommonName = dnsIdentifier }; var csr = CsrHelper.GenerateCsr(csrDetails, rsaKeys); byte[] derRaw; using (var bs = new MemoryStream()) { csr.ExportAsDer(bs); derRaw = bs.ToArray(); } var derB64u = JwsHelper.Base64UrlEncode(derRaw); Console.WriteLine($"\nRequesting Certificate"); var certRequ = client.RequestCertificate(derB64u); Console.WriteLine($" Request Status: {certRequ.StatusCode}"); //Console.WriteLine($"Refreshing Cert Request"); //client.RefreshCertificateRequest(certRequ); if (certRequ.StatusCode == System.Net.HttpStatusCode.Created) { var keyGenFile = Path.Combine(configPath, $"{dnsIdentifier}-gen-key.json"); var keyPemFile = Path.Combine(configPath, $"{dnsIdentifier}-key.pem"); var csrGenFile = Path.Combine(configPath, $"{dnsIdentifier}-gen-csr.json"); var csrPemFile = Path.Combine(configPath, $"{dnsIdentifier}-csr.pem"); var crtDerFile = Path.Combine(configPath, $"{dnsIdentifier}-crt.der"); var crtPemFile = Path.Combine(configPath, $"{dnsIdentifier}-crt.pem"); var crtPfxFile = Path.Combine(configPath, $"{dnsIdentifier}-all.pfx"); using (var fs = new FileStream(keyGenFile, FileMode.Create)) { rsaKeys.Save(fs); File.WriteAllText(keyPemFile, rsaKeys.Pem); } using (var fs = new FileStream(csrGenFile, FileMode.Create)) { csr.Save(fs); File.WriteAllText(csrPemFile, csr.Pem); } Console.WriteLine($" Saving Certificate to {crtDerFile}"); using (var file = File.Create(crtDerFile)) certRequ.SaveCertificate(file); using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { CsrHelper.Crt.ConvertDerToPem(source, target); } // can't create a pfx until we get an irsPemFile, which seems to be some issuer cert thing. var isrPemFile = GetIssuerCertificate(certRequ); Console.WriteLine($" Saving Certificate to {crtPfxFile} (with no password set)"); CsrHelper.Crt.ConvertToPfx(keyPemFile, crtPemFile, isrPemFile, crtPfxFile, FileMode.Create); return crtPfxFile; } throw new Exception($"Request status = {certRequ.StatusCode}"); }
private static void ConfigureBinding(TargetBinding binding, X509Store store, X509Certificate2 certificate) { using (var iisManager = new ServerManager()) { var site = binding.GetSite(iisManager); var existingBinding = (from b in site.Bindings where b.Host == binding.Host && b.Protocol == "https" select b).FirstOrDefault(); if (existingBinding != null) { Console.WriteLine($" Updating Existing https Binding"); existingBinding.CertificateHash = certificate.GetCertHash(); existingBinding.CertificateStoreName = store.Name; } else { Console.WriteLine($" Adding https Binding"); var iisBinding = site.Bindings.Add(":443:" + binding.Host, certificate.GetCertHash(), store.Name); iisBinding.Protocol = "https"; // only do this for IIS 8+ and only if users want it //iisBinding.SetAttributeValue("sslFlags", 1); } Console.WriteLine($" Committing binding changes to IIS"); iisManager.CommitChanges(); } }
//public Version GetIisVersion() //{ // using (RegistryKey componentsKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\InetStp", false)) // { // if (componentsKey != null) // { // int majorVersion = (int)componentsKey.GetValue("MajorVersion", -1); // int minorVersion = (int)componentsKey.GetValue("MinorVersion", -1); // if (majorVersion != -1 && minorVersion != -1) // { // return new Version(majorVersion, minorVersion); // } // } // return new Version(0, 0); // } //} private static void InstallCertificate(TargetBinding binding, string pfxFilename, out X509Store store, out X509Certificate2 certificate) { Console.WriteLine($" Opening Certificate Store"); store = new X509Store(StoreName.My, StoreLocation.LocalMachine); store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite); Console.WriteLine($" Loading .pfx"); // See http://paulstovell.com/blog/x509certificate2 certificate = new X509Certificate2(pfxFilename, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); certificate.FriendlyName = $"{binding.Host} {DateTime.Now}"; Console.WriteLine($" Adding Certificate to Store"); store.Add(certificate); Console.WriteLine($" Closing Certificate Store"); store.Close(); }
static void Auto(TargetBinding binding) { var dnsIdentifier = binding.Host; var auth = Authorize(dnsIdentifier, binding.PhysicalPath); if (auth.Status == "valid") { var pfxFilename = GetCertificate(binding); //if (options.Test && !options.Renew) //{ // Console.WriteLine($"\nDo you want to install the .pfx into the Certificate Store? (Y/N) "); // if (!PromptYesNo()) // return; //} X509Store store; X509Certificate2 certificate; InstallCertificate(binding, pfxFilename, out store, out certificate); if (!options.Renew) { Console.WriteLine($"\nDo you want to add/update an https IIS binding? (Y/N) "); if (!PromptYesNo()) return; } ConfigureBinding(binding, store, certificate); if (!options.Renew) { Console.WriteLine($"\nDo you want to automatically renew this certificate in 60 days? This will add a task scheduler task. (Y/N) "); if (!PromptYesNo()) return; ScheduleRenewal(binding); } } }