From 9047a482bae68b8cf06bbe9339923f5ad31b8950 Mon Sep 17 00:00:00 2001 From: maks-it Date: Sun, 30 Jun 2019 15:21:10 +0200 Subject: [PATCH] intermediate corrections for backup purposes. --- LetsEncrypt/ACMEv2/CachedCertificateResult.cs | 2 +- LetsEncrypt/ACMEv2/LetsEncryptClient.cs | 4 +- LetsEncrypt/Library.cs | 170 +++++++++++++++++- LetsEncrypt/Program.cs | 79 +++++--- 4 files changed, 220 insertions(+), 35 deletions(-) diff --git a/LetsEncrypt/ACMEv2/CachedCertificateResult.cs b/LetsEncrypt/ACMEv2/CachedCertificateResult.cs index 0bb886c..a71a079 100644 --- a/LetsEncrypt/ACMEv2/CachedCertificateResult.cs +++ b/LetsEncrypt/ACMEv2/CachedCertificateResult.cs @@ -4,7 +4,7 @@ namespace ACMEv2 { public class CachedCertificateResult { - public RSA PrivateKey; + public RSACryptoServiceProvider PrivateKey; public string Certificate; } diff --git a/LetsEncrypt/ACMEv2/LetsEncryptClient.cs b/LetsEncrypt/ACMEv2/LetsEncryptClient.cs index 21b978e..93f898f 100644 --- a/LetsEncrypt/ACMEv2/LetsEncryptClient.cs +++ b/LetsEncrypt/ACMEv2/LetsEncryptClient.cs @@ -510,7 +510,7 @@ namespace ACMEv2 return false; } - var cert = new X509Certificate2(cache.Cert); + var cert = new X509Certificate2(Encoding.ASCII.GetBytes(cache.Cert)); // if it is about to expire, we need to refresh if ((cert.NotAfter - DateTime.UtcNow).TotalDays < 14) @@ -519,6 +519,7 @@ namespace ACMEv2 var rsa = new RSACryptoServiceProvider(4096); rsa.ImportCspBlob(cache.Private); + value = new CachedCertificateResult { Certificate = cache.Cert, @@ -527,6 +528,7 @@ namespace ACMEv2 return true; } + /// /// /// diff --git a/LetsEncrypt/Library.cs b/LetsEncrypt/Library.cs index 76cc4bd..4c394c7 100644 --- a/LetsEncrypt/Library.cs +++ b/LetsEncrypt/Library.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -12,15 +14,171 @@ namespace LetsEncrypt /// /// The certificate to export /// A PEM encoded string - public static string ExportToPEM(X509Certificate cert) + //public static string ExportToPEM(X509Certificate2 cert) + //{ + // StringBuilder builder = new StringBuilder(); + + // builder.AppendLine("-----BEGIN CERTIFICATE-----"); + // builder.AppendLine(Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)); + // builder.AppendLine("-----END CERTIFICATE-----"); + + // return builder.ToString(); + //} + + + public static void ExportPublicKey(RSACryptoServiceProvider csp, TextWriter outputStream) { - StringBuilder builder = new StringBuilder(); + var parameters = csp.ExportParameters(false); + using (var stream = new MemoryStream()) + { + var writer = new BinaryWriter(stream); + writer.Write((byte)0x30); // SEQUENCE + using (var innerStream = new MemoryStream()) + { + var innerWriter = new BinaryWriter(innerStream); + innerWriter.Write((byte)0x30); // SEQUENCE + EncodeLength(innerWriter, 13); + innerWriter.Write((byte)0x06); // OBJECT IDENTIFIER + var rsaEncryptionOid = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 }; + EncodeLength(innerWriter, rsaEncryptionOid.Length); + innerWriter.Write(rsaEncryptionOid); + innerWriter.Write((byte)0x05); // NULL + EncodeLength(innerWriter, 0); + innerWriter.Write((byte)0x03); // BIT STRING + using (var bitStringStream = new MemoryStream()) + { + var bitStringWriter = new BinaryWriter(bitStringStream); + bitStringWriter.Write((byte)0x00); // # of unused bits + bitStringWriter.Write((byte)0x30); // SEQUENCE + using (var paramsStream = new MemoryStream()) + { + var paramsWriter = new BinaryWriter(paramsStream); + EncodeIntegerBigEndian(paramsWriter, parameters.Modulus); // Modulus + EncodeIntegerBigEndian(paramsWriter, parameters.Exponent); // Exponent + var paramsLength = (int)paramsStream.Length; + EncodeLength(bitStringWriter, paramsLength); + bitStringWriter.Write(paramsStream.GetBuffer(), 0, paramsLength); + } + var bitStringLength = (int)bitStringStream.Length; + EncodeLength(innerWriter, bitStringLength); + innerWriter.Write(bitStringStream.GetBuffer(), 0, bitStringLength); + } + var length = (int)innerStream.Length; + EncodeLength(writer, length); + writer.Write(innerStream.GetBuffer(), 0, length); + } - builder.AppendLine("-----BEGIN CERTIFICATE-----"); - builder.AppendLine(Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)); - builder.AppendLine("-----END CERTIFICATE-----"); + var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray(); + outputStream.WriteLine("-----BEGIN PUBLIC KEY-----"); + for (var i = 0; i < base64.Length; i += 64) + { + outputStream.WriteLine(base64, i, Math.Min(64, base64.Length - i)); + } + outputStream.WriteLine("-----END PUBLIC KEY-----"); + } + } - return builder.ToString(); + public static void ExportPrivateKey(RSACryptoServiceProvider csp, TextWriter outputStream) + { + if (csp.PublicOnly) throw new ArgumentException("CSP does not contain a private key", "csp"); + var parameters = csp.ExportParameters(true); + using (var stream = new MemoryStream()) + { + var writer = new BinaryWriter(stream); + writer.Write((byte)0x30); // SEQUENCE + using (var innerStream = new MemoryStream()) + { + var innerWriter = new BinaryWriter(innerStream); + EncodeIntegerBigEndian(innerWriter, new byte[] { 0x00 }); // Version + EncodeIntegerBigEndian(innerWriter, parameters.Modulus); + EncodeIntegerBigEndian(innerWriter, parameters.Exponent); + EncodeIntegerBigEndian(innerWriter, parameters.D); + EncodeIntegerBigEndian(innerWriter, parameters.P); + EncodeIntegerBigEndian(innerWriter, parameters.Q); + EncodeIntegerBigEndian(innerWriter, parameters.DP); + EncodeIntegerBigEndian(innerWriter, parameters.DQ); + EncodeIntegerBigEndian(innerWriter, parameters.InverseQ); + var length = (int)innerStream.Length; + EncodeLength(writer, length); + writer.Write(innerStream.GetBuffer(), 0, length); + } + + var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray(); + outputStream.WriteLine("-----BEGIN RSA PRIVATE KEY-----"); + // Output as Base64 with lines chopped at 64 characters + for (var i = 0; i < base64.Length; i += 64) + { + outputStream.WriteLine(base64, i, Math.Min(64, base64.Length - i)); + } + outputStream.WriteLine("-----END RSA PRIVATE KEY-----"); + } + } + + private static void EncodeLength(BinaryWriter stream, int length) + { + if (length < 0) throw new ArgumentOutOfRangeException("length", "Length must be non-negative"); + if (length < 0x80) + { + // Short form + stream.Write((byte)length); + } + else + { + // Long form + var temp = length; + var bytesRequired = 0; + while (temp > 0) + { + temp >>= 8; + bytesRequired++; + } + stream.Write((byte)(bytesRequired | 0x80)); + for (var i = bytesRequired - 1; i >= 0; i--) + { + stream.Write((byte)(length >> (8 * i) & 0xff)); + } + } + } + + private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true) + { + stream.Write((byte)0x02); // INTEGER + var prefixZeros = 0; + for (var i = 0; i < value.Length; i++) + { + if (value[i] != 0) break; + prefixZeros++; + } + if (value.Length - prefixZeros == 0) + { + EncodeLength(stream, 1); + stream.Write((byte)0); + } + else + { + if (forceUnsigned && value[prefixZeros] > 0x7f) + { + // Add a prefix zero to force unsigned if the MSB is 1 + EncodeLength(stream, value.Length - prefixZeros + 1); + stream.Write((byte)0); + } + else + { + EncodeLength(stream, value.Length - prefixZeros); + } + for (var i = prefixZeros; i < value.Length; i++) + { + stream.Write(value[i]); + } + } } } + + + + + + + + } diff --git a/LetsEncrypt/Program.cs b/LetsEncrypt/Program.cs index 1592aea..063382d 100644 --- a/LetsEncrypt/Program.cs +++ b/LetsEncrypt/Program.cs @@ -47,47 +47,72 @@ namespace LetsEncrypt try { - LetsEncryptClient client = new LetsEncryptClient(LetsEncryptClient.StagingV2, AppDomain.CurrentDomain.BaseDirectory); + LetsEncryptClient client = new LetsEncryptClient(LetsEncryptClient.ProductionV2, AppDomain.CurrentDomain.BaseDirectory); Console.WriteLine("1. Client Initialization..."); // 1 client.Init(contacts.ToArray()).Wait(); Console.WriteLine(string.Format("Terms of service: {0}",client.GetTermsOfServiceUri())); - client.NewNonce().Wait(); - // 2 - try + // get cached certificate and chech if it's valid + CachedCertificateResult certRes = new CachedCertificateResult(); + if (client.TryGetCachedCertificate(hosts, out certRes)) { - Console.WriteLine("2. Client New Order..."); - Task> orders = client.NewOrder(hosts.ToArray(), "http-01"); - orders.Wait(); + File.WriteAllText(Path.Combine(certsPath, "maks-it.com.crt"), certRes.Certificate); - foreach (var result in orders.Result) + using (StreamWriter writer = File.CreateText(Path.Combine(certsPath, "maks-it.com.key"))) + Library.ExportPrivateKey(certRes.PrivateKey, writer); + } + else { + + client.NewNonce().Wait(); + + // 2 + try { - Console.WriteLine("Key: " + result.Key + Environment.NewLine + "Value: " + result.Value); - string[] splitToken = result.Value.Split('~'); - File.WriteAllText(FS.Path.Combine(tokensPath, splitToken[0]), splitToken[1]); + Console.WriteLine("2. Client New Order..."); + Task> orders = client.NewOrder(hosts.ToArray(), "http-01"); + orders.Wait(); + + foreach (var result in orders.Result) + { + Console.WriteLine("Key: " + result.Key + Environment.NewLine + "Value: " + result.Value); + string[] splitToken = result.Value.Split('~'); + File.WriteAllText(FS.Path.Combine(tokensPath, splitToken[0]), splitToken[1]); + } + + // 3 + Console.WriteLine("3. Client Complete Challange..."); + client.CompleteChallenges().Wait(); + Console.WriteLine("Challanges comleted."); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message.ToString()); + client.GetOrder(hosts.ToArray()).Wait(); } - // 3 - Console.WriteLine("3. Client Complete Challange..."); - client.CompleteChallenges().Wait(); - Console.WriteLine("Challanges comleted."); - } - catch (Exception ex) { - Console.WriteLine(ex.Message.ToString()); - client.GetOrder(hosts.ToArray()).Wait(); - } - - // 4 Download certificate - Console.WriteLine("4. Download certificate..."); - Task<(X509Certificate2 Cert, RSA PrivateKey)> certificate = client.GetCertificate(); - certificate.Wait(); + // 4 Download certificate + Console.WriteLine("4. Download certificate..."); + client.GetCertificate().Wait(); - File.WriteAllText(Path.Combine(certsPath, "maks-it.com.crt"), Library.ExportToPEM(certificate.Result.Cert)); - Console.WriteLine("Certificate saved."); + + // 5 Write to filesystem + //CachedCertificateResult certRes = new CachedCertificateResult(); + //if (client.TryGetCachedCertificate(hosts, out certRes)) { + // File.WriteAllText(Path.Combine(certsPath, "maks-it.com.crt"), certRes.Certificate); + + // using (StreamWriter writer = File.CreateText(Path.Combine(certsPath, "maks-it.com.key"))) + // Library.ExportPrivateKey(certRes.PrivateKey, writer); + //} + + Console.WriteLine("Certificate saved."); + } + + + } catch (Exception ex) { Console.WriteLine(ex.Message.ToString());