public void Load() { var path1 = Path.GetTempFileName(); var path2 = Path.GetTempFileName(); try { // Separate files with CRLF line endings. File.WriteAllText(path1, TestCertPart); File.WriteAllText(path2, TestKeyPart); var cert = TlsCertificate.Load(path1, path2); Assert.Equal(TlsCertificate.NormalizePem(TestCertPart), cert.CertPem); Assert.Equal(TlsCertificate.NormalizePem(TestKeyPart), cert.KeyPem); // Separate files with LF line endings. File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart)); File.WriteAllText(path2, TlsCertificate.NormalizePem(TestKeyPart)); cert = TlsCertificate.Load(path1, path2); Assert.Equal(TlsCertificate.NormalizePem(TestCertPart), cert.CertPem); Assert.Equal(TlsCertificate.NormalizePem(TestKeyPart), cert.KeyPem); // Combined file with CRLF line endings. File.WriteAllText(path1, TestCertPart + TestKeyPart); cert = TlsCertificate.Load(path1); File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart)); File.WriteAllText(path2, TlsCertificate.NormalizePem(TestKeyPart)); // Combined file with LF line endings. File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart) + TlsCertificate.NormalizePem(TestKeyPart)); cert = TlsCertificate.Load(path1); File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart)); File.WriteAllText(path2, TlsCertificate.NormalizePem(TestKeyPart)); } finally { File.Delete(path1); File.Delete(path2); } }
/// <summary> /// Loads the certificate files (if any). /// </summary> /// <returns> /// Returns a <see cref="TlsCertificate"/> instance with the loaded public certificate /// and private key or <c>null</c> if no certificate is defined. /// </returns> public TlsCertificate Load() { if (IsSecured) { var certificate = TlsCertificate.Load(Path); certificate.Parse(); return(certificate); } else { return(null); } }
public void Load() { using (var tempFolder = new TempFolder()) { var path1 = Path.Combine(tempFolder.Path, "test1"); var path2 = Path.Combine(tempFolder.Path, "test2"); // Separate files with CRLF line endings. File.WriteAllText(path1, TestCertPart); File.WriteAllText(path2, TestKeyPart); var cert = TlsCertificate.Load(path1, path2); Assert.Equal(TlsCertificate.NormalizePem(TestCertPart), cert.CertPem); Assert.Equal(TlsCertificate.NormalizePem(TestKeyPart), cert.KeyPem); // Separate files with LF line endings. File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart)); File.WriteAllText(path2, TlsCertificate.NormalizePem(TestKeyPart)); cert = TlsCertificate.Load(path1, path2); Assert.Equal(TlsCertificate.NormalizePem(TestCertPart), cert.CertPem); Assert.Equal(TlsCertificate.NormalizePem(TestKeyPart), cert.KeyPem); // Combined file with CRLF line endings. File.WriteAllText(path1, TestCertPart + TestKeyPart); cert = TlsCertificate.Load(path1); File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart)); File.WriteAllText(path2, TlsCertificate.NormalizePem(TestKeyPart)); // Combined file with LF line endings. File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart) + TlsCertificate.NormalizePem(TestKeyPart)); cert = TlsCertificate.Load(path1); File.WriteAllText(path1, TlsCertificate.NormalizePem(TestCertPart)); File.WriteAllText(path2, TlsCertificate.NormalizePem(TestKeyPart)); } }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.Arguments.Length == 0 || commandLine.HasHelpOption) { Help(); Program.Exit(0); } Program.ConnectHive(); var hive = HiveHelper.Hive; // Parse the arguments. var command = commandLine.Arguments.ElementAtOrDefault(0); if (string.IsNullOrEmpty(command)) { Console.Error.WriteLine($"*** ERROR: COMMAND expected."); Program.Exit(1); } var registry = string.Empty; var username = string.Empty; var password = string.Empty; var certPath = string.Empty; var secret = string.Empty; switch (command) { case "login": // Read the arguments. registry = commandLine.Arguments.ElementAtOrDefault(1); if (!string.IsNullOrEmpty(registry)) { if (!HiveDefinition.NameRegex.IsMatch(registry)) { Console.Error.WriteLine($"*** ERROR: [{registry}] is not a valid hostname."); Program.Exit(1); } username = commandLine.Arguments.ElementAtOrDefault(1); if (!string.IsNullOrEmpty(username)) { password = commandLine.Arguments.ElementAtOrDefault(2); if (password == "-") { password = NeonHelper.ReadStandardInputText().Trim(); if (string.IsNullOrEmpty(password)) { Console.Error.WriteLine("*** ERROR: No password was read from STDIN."); Program.Exit(1); } } } } // Execute the command. hive.Registry.Login(registry, username, password); break; case "logout": // Read the arguments. registry = commandLine.Arguments.ElementAtOrDefault(1); if (!string.IsNullOrEmpty(registry) && !HiveDefinition.NameRegex.IsMatch(registry)) { Console.Error.WriteLine($"*** ERROR: [{registry}] is not a valid hostname."); Program.Exit(1); } // Execute the command. hive.Registry.Logout(registry); break; case "service": var serviceCommand = commandLine.Arguments.ElementAtOrDefault(1); if (string.IsNullOrEmpty(serviceCommand)) { Console.Error.WriteLine($"*** ERROR: service COMMAND expected."); Program.Exit(1); } switch (serviceCommand) { case "create": registry = commandLine.Arguments.ElementAtOrDefault(2); if (string.IsNullOrEmpty(registry)) { Console.Error.WriteLine($"*** ERROR: Expected HOSTNAME."); Program.Exit(1); } if (!HiveDefinition.NameRegex.IsMatch(registry)) { Console.Error.WriteLine($"*** ERROR: [{registry}] is not a valid hostname."); Program.Exit(1); } certPath = commandLine.Arguments.ElementAtOrDefault(3); if (string.IsNullOrEmpty(certPath)) { Console.Error.WriteLine($"*** ERROR: Expected CERT-PATH."); Program.Exit(1); } if (!File.Exists(certPath)) { Console.Error.WriteLine($"*** ERROR: Cannot load certificate from [{certPath}]."); Program.Exit(1); } var certificate = TlsCertificate.Load(certPath); certificate.Parse(); if (!certificate.IsValidDate()) { Console.Error.WriteLine($"*** ERROR: The certificate expired on [{certificate.ValidUntil}]."); Program.Exit(1); } if (!certificate.IsValidHost(registry)) { Console.Error.WriteLine($"*** ERROR: The certificate does not cover [{registry}]."); Program.Exit(1); } secret = commandLine.Arguments.ElementAtOrDefault(4); if (string.IsNullOrEmpty(secret)) { Console.Error.WriteLine($"*** ERROR: Expected SECRET."); Program.Exit(1); } username = commandLine.Arguments.ElementAtOrDefault(5); if (!string.IsNullOrEmpty(username)) { password = commandLine.Arguments.ElementAtOrDefault(6); if (password == "-") { password = NeonHelper.ReadStandardInputText().Trim(); if (string.IsNullOrEmpty(password)) { Console.Error.WriteLine("*** ERROR: No password was read from STDIN."); Program.Exit(1); } } } // Execute the command. Console.WriteLine(); hive.Registry.CreateLocalRegistry(registry, username, password, secret, certificate, progress: message => Console.WriteLine(message)); break; case "prune": if (!hive.Registry.HasLocalRegistry) { Console.Error.WriteLine($"*** ERROR: The [{hive.Name}] hive does not have a local registry deployed."); Program.Exit(1); } Console.WriteLine(); hive.Registry.PruneLocalRegistry(progress: message => Console.WriteLine(message)); break; case "remove": case "rm": if (!hive.Registry.HasLocalRegistry) { Console.Error.WriteLine($"*** ERROR: The [{hive.Name}] hive does not have a local registry deployed."); Program.Exit(1); } Console.WriteLine(); hive.Registry.RemoveLocalRegistry(progress: message => Console.WriteLine(message)); break; case "status": Console.WriteLine(); if (hive.Registry.HasLocalRegistry) { Console.WriteLine($"Local Docker registry is deployed on [{hive.Name}]."); Program.Exit(0); } else { Console.WriteLine($"No local Docker registry is deployed on [{hive.Name}]."); Program.Exit(1); } break; default: Console.Error.WriteLine($"*** ERROR: Unexpected [{serviceCommand}] command."); Program.Exit(1); break; } break; default: Console.Error.WriteLine($"*** ERROR: Unexpected [{command}] command."); Program.Exit(1); break; } }
/// <summary> /// Verifies a local certificate file. /// </summary> /// <param name="commandLine">The command line.</param> private void VerifyLocalCertificate(CommandLine commandLine) { // $todo(jeff.lill): // // Consider using the new [TlsCertificate.Validate()] method. The only // disadvantage of this method is that it doesn't output the error // messages from the certificate tool. // // On the other hand, this code might become moot when we upgrade // to use the built-in .NET X.509 classes. if (commandLine.Arguments.Length != 1) { Console.Error.WriteLine("*** ERROR: Expected argument: PATH"); Program.Exit(1); } var certificate = TlsCertificate.Load(commandLine.Arguments[0]); // We're going to split the certificate into two files, the issued // certificate and the certificate authority's certificate chain // (AKA the CA bundle). var tempCertPath = Path.GetTempFileName(); var tempCaPath = Path.GetTempFileName(); var tool = "openssl"; if (NeonHelper.IsLinux && HiveHelper.InToolContainer) { // Choose nicer looking temporary file names when we're running the // tool container and don't need to worry about conflicting files. tempCertPath = Path.GetFileNameWithoutExtension(commandLine.Arguments[0]); tempCaPath = tempCertPath + ".ca"; } try { var pos = certificate.CertPem.IndexOf("-----END CERTIFICATE-----"); if (pos == -1) { throw new ArgumentNullException("The certificate is not formatted properly."); } pos = certificate.CertPem.IndexOf("-----BEGIN CERTIFICATE-----", pos); var issuedCert = certificate.CertPem.Substring(0, pos); var caBundle = certificate.CertPem.Substring(pos); File.WriteAllText(tempCertPath, issuedCert); File.WriteAllText(tempCaPath, caBundle); var sbArgs = new StringBuilder(); // We're going to use [certutil] for Windows and [OpenSSL] // for everything else. if (NeonHelper.IsWindows) { tool = "certutil"; sbArgs.Append("-verify "); sbArgs.Append($"\"{tempCertPath}\" "); sbArgs.Append($"\"{tempCaPath}\""); var result = NeonHelper.ExecuteCapture("certutil", sbArgs.ToString()); Console.WriteLine(result.OutputText); Console.Error.WriteLine(result.ErrorText); Program.Exit(result.ExitCode); } else { sbArgs.Append("verify "); sbArgs.Append("-purpose sslserver "); sbArgs.Append($"-CAfile \"{tempCaPath}\" "); sbArgs.Append($"\"{tempCertPath}\""); var result = NeonHelper.ExecuteCapture("openssl", sbArgs.ToString()); Console.WriteLine(result.OutputText); Console.Error.WriteLine(result.ErrorText); Program.Exit(result.ExitCode); } } catch (Win32Exception) { Console.Error.WriteLine($"*** ERROR: Cannot find the [{tool}] SSL certificate utility on the PATH."); Program.Exit(1); } finally { File.Delete(tempCertPath); File.Delete(tempCaPath); } }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } // Process the command arguments. TlsCertificate certificate; string certName; var command = commandLine.Arguments.FirstOrDefault(); if (command == null) { Console.WriteLine(usage); Program.Exit(1); } commandLine = commandLine.Shift(1); switch (command) { case "get": Program.ConnectHive(); certName = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(certName)) { Console.Error.WriteLine("*** ERROR: Expected arguments: NAME"); Program.Exit(1); } if (!HiveDefinition.IsValidName(certName)) { Console.Error.WriteLine($"*** ERROR: [{certName}] is not a valid certificate name."); Program.Exit(1); } certificate = HiveHelper.Hive.Certificate.Get(certName); if (certificate == null) { Console.Error.WriteLine($"*** ERROR: Certificate [{certName}] does not exist."); Program.Exit(1); } Console.WriteLine(certificate.CombinedPem); break; case "join": if (commandLine.Arguments.Length != 3) { Console.Error.WriteLine("*** ERROR: Expected arguments: PATH-CERT PATH-KEY PATH-OUTPUT"); Program.Exit(1); } certificate = TlsCertificate.Load(commandLine.Arguments[0], commandLine.Arguments[1]); File.WriteAllText(commandLine.Arguments[2], certificate.CombinedPem); break; case "list": case "ls": Program.ConnectHive(); var certList = new List <CertInfo>(); DateTime?checkDate = null; bool expired = false; if (commandLine.GetOption("--expired") != null) { checkDate = DateTime.UtcNow; expired = true; } else if (commandLine.GetOption("--expiring") != null) { checkDate = DateTime.UtcNow + TimeSpan.FromDays(30); } // List the certificate key/names and then fetch each one // to capture details like the expiration date and covered // hostnames. foreach (var name in HiveHelper.Hive.Certificate.List()) { certificate = HiveHelper.Hive.Certificate.Get(name); if (checkDate.HasValue && certificate.IsValidDate(checkDate)) { continue; } certList.Add(new CertInfo(name, certificate)); } if (checkDate.HasValue && certList.Count == 0) { Console.WriteLine(expired ? "* No certificates have expired." : "* No certificates are expiring within 30 days."); Program.Exit(0); } if (certList.Count > 0) { var nameHeader = "Name"; var validUntilHeader = "Valid Until"; var hostsHeader = "Hosts"; var nameColumnWidth = Math.Max(nameHeader.Length, certList.Max(ci => ci.Name.Length)); var dateColumnWidth = Math.Max(validUntilHeader.Length, certList.Max(ci => ci.ValidUntil.Length)); var hostColumnWidth = Math.Max(hostsHeader.Length, certList.Max(ci => ci.Hosts.Length)); Console.WriteLine($"{nameHeader}{new string(' ', nameColumnWidth - "Name".Length)} {validUntilHeader}{new string(' ', dateColumnWidth - validUntilHeader.Length)} {hostsHeader}"); Console.WriteLine($"{new string('-', nameColumnWidth)} {new string('-', dateColumnWidth)} {new string('-', hostColumnWidth)}"); foreach (var certInfo in certList.OrderBy(ci => ci.Name.ToLowerInvariant())) { Console.WriteLine($"{certInfo.Name}{new string(' ', nameColumnWidth - certInfo.Name.Length)} {certInfo.ValidUntil}{new string(' ', dateColumnWidth - certInfo.ValidUntil.Length)} {certInfo.Hosts}"); } if (checkDate.HasValue) { Program.Exit(1); } } else { Console.WriteLine("* No certificates"); } break; case "remove": case "rm": Program.ConnectHive(); certName = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(certName)) { Console.Error.WriteLine("*** ERROR: Expected arguments: NAME"); Program.Exit(1); } if (!HiveDefinition.IsValidName(certName)) { Console.Error.WriteLine($"*** ERROR: [{certName}] is not a valid certificate name."); Program.Exit(1); } if (HiveHelper.Hive.Certificate.Get(certName) != null) { HiveHelper.Hive.Certificate.Remove(certName); Console.WriteLine($"Certificate [{certName}] was removed."); } else { Console.Error.WriteLine($"*** ERROR: [{certName}] does not exist."); Program.Exit(1); } break; case "set": Program.ConnectHive(); using (var vault = HiveHelper.OpenVault(Program.HiveLogin.VaultCredentials.RootToken)) { if (commandLine.Arguments.Length != 2) { Console.Error.WriteLine("*** ERROR: Expected arguments: NAME PATH"); Program.Exit(1); } certName = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(certName)) { Console.Error.WriteLine("*** ERROR: Expected arguments: NAME"); Program.Exit(1); } if (!HiveDefinition.IsValidName(certName)) { Console.Error.WriteLine($"*** ERROR: [{certName}] is not a valid certificate name."); Program.Exit(1); } certificate = TlsCertificate.Load(commandLine.Arguments.ElementAtOrDefault(1)); certificate.Parse(); if (HiveHelper.Hive.Certificate.Get(certName) == null) { HiveHelper.Hive.Certificate.Set(certName, certificate); Console.WriteLine($"Certificate [{certName}] was added."); } else { HiveHelper.Hive.Certificate.Set(certName, certificate); Console.WriteLine($"Certificate [{certName}] was updated."); } } break; case "split": if (commandLine.Arguments.Length != 3) { Console.Error.WriteLine("*** ERROR: Expected arguments: PATH PATH-CERT PATH-KEY"); Program.Exit(1); } certificate = TlsCertificate.Load(commandLine.Arguments[0]); File.WriteAllText(commandLine.Arguments[1], certificate.CertPem); File.WriteAllText(commandLine.Arguments[2], certificate.KeyPem); break; case "verify": VerifyLocalCertificate(commandLine); break; default: Console.Error.WriteLine($"*** ERROR: Unknown command: [{command}]"); Program.Exit(1); break; } }