Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions Certify/Commands/CertRequest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using CERTENROLLLib;
using CERTENROLLLib;
using Certify.Lib;
using CommandLine;
using System;
using System.Collections.Generic;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -60,6 +61,12 @@ public class Options : DefaultOptions

[Option("install", HelpText = "Install certificate in the current store")]
public bool Install { get; set; }

[Option('u', "username", HelpText = "Username for authentication (format: user@domain.fqdn). Omit to use the current user.")]
public string Username { get; set; }

[Option('p', "password", HelpText = "Password for authentication. If omitted while --username is set, you'll be prompted (input hidden).")]
public string Password { get; set; }
}

public static int Execute(Options opts)
Expand Down Expand Up @@ -87,6 +94,12 @@ public static int Execute(Options opts)
return 1;
}

// Prompt for password if a username was given but no password was provided
if (!string.IsNullOrEmpty(opts.Username) && string.IsNullOrEmpty(opts.Password))
{
opts.Password = ReadPasswordMasked($"Password for {opts.Username}: ");
}

var sans = new List<Tuple<SubjectAltNameType, string>>();

void AddSubjectAltNames(IEnumerable<string> names, SubjectAltNameType type)
Expand Down Expand Up @@ -118,6 +131,9 @@ private static void RequestCert(Options opts, IEnumerable<Tuple<SubjectAltNameTy
Console.WriteLine();
Console.WriteLine($"[*] Current user context : {WindowsIdentity.GetCurrent().Name}");

if (!string.IsNullOrEmpty(opts.Username))
Console.WriteLine($"[*] Authenticating as : {opts.Username}");

var subject_name = opts.SubjectName;

if (string.IsNullOrEmpty(subject_name))
Expand All @@ -136,7 +152,7 @@ private static void RequestCert(Options opts, IEnumerable<Tuple<SubjectAltNameTy
Console.WriteLine($"[*] No subject name specified, using current context as subject.");
}
}

if (string.IsNullOrEmpty(subject_name))
{
subject_name = "CN=User";
Expand All @@ -156,7 +172,7 @@ private static void RequestCert(Options opts, IEnumerable<Tuple<SubjectAltNameTy
if (opts.ApplicationPolicies != null && opts.ApplicationPolicies.Any())
Console.WriteLine($"[*] Application Policies : {string.Join(", ", opts.ApplicationPolicies)}");

var csr = CertEnrollment.CreateCertRequestMessage(opts.TemplateName, subject_name, sans,
var csr = CertEnrollment.CreateCertRequestMessage(opts.TemplateName, subject_name, sans,
opts.SidExtension, opts.ApplicationPolicies, opts.KeySize, opts.MachineContext);

Console.WriteLine();
Expand All @@ -173,10 +189,10 @@ private static void RequestCert(Options opts, IEnumerable<Tuple<SubjectAltNameTy
Console.WriteLine(csr.Item2);
}
else
{
{
try
{
int request_id = CertEnrollment.SendCertificateRequest(opts.CertificateAuthority, csr.Item1);
int request_id = CertEnrollment.SendCertificateRequest(opts.CertificateAuthority, csr.Item1, opts.Username, opts.Password);

Console.WriteLine($"[*] Request ID : {request_id}");
Console.WriteLine();
Expand All @@ -186,9 +202,9 @@ private static void RequestCert(Options opts, IEnumerable<Tuple<SubjectAltNameTy
var certificate_pem = string.Empty;

if (!opts.Install)
certificate_pem = CertEnrollment.DownloadCert(opts.CertificateAuthority, request_id);
certificate_pem = CertEnrollment.DownloadCert(opts.CertificateAuthority, request_id, opts.Username, opts.Password);
else
certificate_pem = CertEnrollment.DownloadAndInstallCert(opts.CertificateAuthority, request_id, X509CertificateEnrollmentContext.ContextUser);
certificate_pem = CertEnrollment.DownloadAndInstallCert(opts.CertificateAuthority, request_id, X509CertificateEnrollmentContext.ContextUser, opts.Username, opts.Password);

if (opts.OutputPem)
{
Expand Down Expand Up @@ -229,6 +245,47 @@ private static string GetCurrentComputerDN()
{
return $"CN={System.Net.Dns.GetHostEntry("").HostName}";
}

// Reads a password from the console without echoing it to the screen.
private static string ReadPasswordMasked(string prompt)
{
Console.Write(prompt);

var secure = new SecureString();

ConsoleKeyInfo key;
while ((key = Console.ReadKey(intercept: true)).Key != ConsoleKey.Enter)
{
if (key.Key == ConsoleKey.Backspace)
{
if (secure.Length > 0)
{
secure.RemoveAt(secure.Length - 1);
Console.Write("\b \b");
}
continue;
}

if (key.KeyChar != '\0')
{
secure.AppendChar(key.KeyChar);
Console.Write('*');
}
}

Console.WriteLine();
secure.MakeReadOnly();

var ptr = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(secure);
try
{
return System.Runtime.InteropServices.Marshal.PtrToStringUni(ptr);
}
finally
{
System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}
}
}

Expand Down
57 changes: 55 additions & 2 deletions Certify/Commands/EnumCas.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using Certify.Domain;
using Certify.Domain;
using Certify.Lib;
using CommandLine;
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Security;
using System.Security.Principal;

namespace Certify.Commands
Expand Down Expand Up @@ -40,6 +41,13 @@ public class Options : DefaultOptions

[Option("skip-web-checks", HelpText = "Skip web service checks")]
public bool SkipWebServiceChecks { get; set; }

[Option('u', "username", HelpText = "Username for LDAP bind (format: user@domain.fqdn). Omit to bind as the current user.")]
public string Username { get; set; }

[Option('p', "password", HelpText = "Password for LDAP bind. If omitted while --username is set, you'll be prompted (input hidden).")]
public string Password { get; set; }

}

public static int Execute(Options opts)
Expand All @@ -58,7 +66,12 @@ public static int Execute(Options opts)
return 1;
}

var ldap = new LdapOperations(opts.Domain, opts.LdapServer);
if (!string.IsNullOrEmpty(opts.Username) && string.IsNullOrEmpty(opts.Password))
{
opts.Password = ReadPasswordMasked($"Password for {opts.Username}: ");
}

var ldap = new LdapOperations(opts.Domain, opts.LdapServer, opts.Username, opts.Password);

Console.WriteLine($"[*] Using the search base '{ldap.ConfigurationPath}'");

Expand Down Expand Up @@ -192,6 +205,46 @@ private static void PrintEnterpriseCAs(Options opts, LdapOperations ldap, List<s
}
}

private static string ReadPasswordMasked(string prompt)
{
Console.Write(prompt);

var secure = new SecureString();

ConsoleKeyInfo key;
while ((key = Console.ReadKey(intercept: true)).Key != ConsoleKey.Enter)
{
if (key.Key == ConsoleKey.Backspace)
{
if (secure.Length > 0)
{
secure.RemoveAt(secure.Length - 1);
Console.Write("\b \b");
}
continue;
}

if (key.KeyChar != '\0')
{
secure.AppendChar(key.KeyChar);
Console.Write('*');
}
}

Console.WriteLine();
secure.MakeReadOnly();

var ptr = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(secure);
try
{
return System.Runtime.InteropServices.Marshal.PtrToStringUni(ptr);
}
finally
{
System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}

private static void PrintCAWebServices(CertificateAuthorityWebServices web_services)
{
var join_sep = $"\n{new string(' ', 36)}";
Expand Down
56 changes: 54 additions & 2 deletions Certify/Commands/EnumPkiObjects.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using Certify.Domain;
using Certify.Domain;
using Certify.Lib;
using CommandLine;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Security;
using System.Security.Principal;

namespace Certify.Commands
Expand All @@ -25,6 +26,12 @@ public class Options : DefaultOptions

[Option("show-admins", HelpText = "Include admin permissions")]
public bool ShowAdmins { get; set; }

[Option('u', "username", HelpText = "Username for LDAP bind (format: user@domain.fqdn). Omit to bind as the current user.")]
public string Username { get; set; }

[Option('p', "password", HelpText = "Password for LDAP bind. If omitted while --username is set, you'll be prompted (input hidden).")]
public string Password { get; set; }
}

public static int Execute(Options opts)
Expand All @@ -37,7 +44,12 @@ public static int Execute(Options opts)
return 1;
}

var ldap = new LdapOperations(opts.Domain, opts.LdapServer);
if (!string.IsNullOrEmpty(opts.Username) && string.IsNullOrEmpty(opts.Password))
{
opts.Password = ReadPasswordMasked($"Password for {opts.Username}: ");
}

var ldap = new LdapOperations(opts.Domain, opts.LdapServer, opts.Username, opts.Password);

Console.WriteLine($"[*] Using the search base '{ldap.ConfigurationPath}'");

Expand Down Expand Up @@ -72,6 +84,46 @@ public static int Execute(Options opts)
return 0;
}

private static string ReadPasswordMasked(string prompt)
{
Console.Write(prompt);

var secure = new SecureString();

ConsoleKeyInfo key;
while ((key = Console.ReadKey(intercept: true)).Key != ConsoleKey.Enter)
{
if (key.Key == ConsoleKey.Backspace)
{
if (secure.Length > 0)
{
secure.RemoveAt(secure.Length - 1);
Console.Write("\b \b");
}
continue;
}

if (key.KeyChar != '\0')
{
secure.AppendChar(key.KeyChar);
Console.Write('*');
}
}

Console.WriteLine();
secure.MakeReadOnly();

var ptr = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(secure);
try
{
return System.Runtime.InteropServices.Marshal.PtrToStringUni(ptr);
}
finally
{
System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}

private static Dictionary<string, List<Tuple<string, string>>> GetPkiObjectControllers(IEnumerable<PKIObject> pki_objects)
{
var object_controllers = new Dictionary<string, List<Tuple<string, string>>>();
Expand Down
Loading