mirror of
https://github.com/MAKS-IT-COM/maksit-certs-ui.git
synced 2025-12-31 04:00:03 +01:00
(refactor): code cleanup and bugfixing
This commit is contained in:
parent
767b4f2fc6
commit
f7411a4e3d
@ -8,8 +8,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DomainResult.Common" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -106,7 +106,4 @@ public class JwsService : IJwsService {
|
|||||||
.Split('=').First() // Remove any trailing '='s
|
.Split('=').First() // Remove any trailing '='s
|
||||||
.Replace('+', '-') // 62nd char of encoding
|
.Replace('+', '-') // 62nd char of encoding
|
||||||
.Replace('/', '_'); // 63rd char of encoding
|
.Replace('/', '_'); // 63rd char of encoding
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,7 +52,7 @@ public class LetsEncryptService : ILetsEncryptService {
|
|||||||
|
|
||||||
private HttpClient _httpClient;
|
private HttpClient _httpClient;
|
||||||
|
|
||||||
private IJwsService _jwsService;
|
private IJwsService? _jwsService;
|
||||||
private AcmeDirectory? _directory;
|
private AcmeDirectory? _directory;
|
||||||
private RegistrationCache? _cache;
|
private RegistrationCache? _cache;
|
||||||
|
|
||||||
|
|||||||
@ -23,20 +23,17 @@ public class App : IApp {
|
|||||||
private readonly ILogger<App> _logger;
|
private readonly ILogger<App> _logger;
|
||||||
private readonly Configuration _appSettings;
|
private readonly Configuration _appSettings;
|
||||||
private readonly ILetsEncryptService _letsEncryptService;
|
private readonly ILetsEncryptService _letsEncryptService;
|
||||||
private readonly IKeyService _keyService;
|
|
||||||
private readonly ITerminalService _terminalService;
|
private readonly ITerminalService _terminalService;
|
||||||
|
|
||||||
public App(
|
public App(
|
||||||
ILogger<App> logger,
|
ILogger<App> logger,
|
||||||
IOptions<Configuration> appSettings,
|
IOptions<Configuration> appSettings,
|
||||||
ILetsEncryptService letsEncryptService,
|
ILetsEncryptService letsEncryptService,
|
||||||
IKeyService keyService,
|
|
||||||
ITerminalService terminalService
|
ITerminalService terminalService
|
||||||
) {
|
) {
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_appSettings = appSettings.Value;
|
_appSettings = appSettings.Value;
|
||||||
_letsEncryptService = letsEncryptService;
|
_letsEncryptService = letsEncryptService;
|
||||||
_keyService = keyService;
|
|
||||||
_terminalService = terminalService;
|
_terminalService = terminalService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,16 +99,12 @@ public class App : IApp {
|
|||||||
// if valid check if cert and key exists otherwise recreate
|
// if valid check if cert and key exists otherwise recreate
|
||||||
// else continue with new certificate request
|
// else continue with new certificate request
|
||||||
var certRes = new CachedCertificateResult();
|
var certRes = new CachedCertificateResult();
|
||||||
if (registrationCache.TryGetCachedCertificate(site.Name, out certRes)) {
|
if (registrationCache != null && registrationCache.TryGetCachedCertificate(site.Name, out certRes)) {
|
||||||
string cert = Path.Combine(sslPath, $"{site.Name}.crt");
|
|
||||||
//if(!File.Exists(cert))
|
|
||||||
File.WriteAllText(cert, certRes.Certificate);
|
|
||||||
|
|
||||||
string key = Path.Combine(sslPath, $"{site.Name}.key");
|
File.WriteAllText(Path.Combine(sslPath, $"{site.Name}.crt"), certRes.Certificate);
|
||||||
//if(!File.Exists(key)) {
|
|
||||||
using (StreamWriter writer = File.CreateText(key))
|
if (certRes.PrivateKey != null)
|
||||||
_keyService.ExportPrivateKey(certRes.PrivateKey, writer);
|
File.WriteAllText(Path.Combine(sslPath, $"{site.Name}.key"), certRes.PrivateKey.ExportRSAPrivateKeyPem());
|
||||||
//}
|
|
||||||
|
|
||||||
_logger.LogInformation("Certificate and Key exists and valid. Restored from cache.");
|
_logger.LogInformation("Certificate and Key exists and valid. Restored from cache.");
|
||||||
}
|
}
|
||||||
@ -188,11 +181,10 @@ public class App : IApp {
|
|||||||
certRes = new CachedCertificateResult();
|
certRes = new CachedCertificateResult();
|
||||||
if (registrationCache.TryGetCachedCertificate(site.Name, out certRes)) {
|
if (registrationCache.TryGetCachedCertificate(site.Name, out certRes)) {
|
||||||
|
|
||||||
File.WriteAllText(Path.Combine(sslPath, site.Name + ".crt"), certRes.Certificate);
|
File.WriteAllText(Path.Combine(sslPath, $"{site.Name}.crt"), certRes.Certificate);
|
||||||
|
|
||||||
using (var writer = File.CreateText(Path.Combine(sslPath, site.Name + ".key"))) {
|
if(certRes.PrivateKey != null)
|
||||||
_keyService.ExportPrivateKey(certRes.PrivateKey, writer);
|
File.WriteAllText(Path.Combine(sslPath, $"{site.Name}.key"), certRes.PrivateKey.ExportRSAPrivateKeyPem());
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("Certificate saved.");
|
_logger.LogInformation("Certificate saved.");
|
||||||
|
|
||||||
@ -256,7 +248,6 @@ public class App : IApp {
|
|||||||
string owner,
|
string owner,
|
||||||
string changeMode
|
string changeMode
|
||||||
) {
|
) {
|
||||||
|
|
||||||
using var sshService = new SSHService(logger, sshSettings.Host, sshSettings.Port, sshSettings.Username, sshSettings.Password);
|
using var sshService = new SSHService(logger, sshSettings.Host, sshSettings.Port, sshSettings.Username, sshSettings.Password);
|
||||||
sshService.Connect();
|
sshService.Connect();
|
||||||
|
|
||||||
|
|||||||
@ -42,8 +42,6 @@ class Program {
|
|||||||
|
|
||||||
#region Services
|
#region Services
|
||||||
services.RegisterLetsEncrypt();
|
services.RegisterLetsEncrypt();
|
||||||
|
|
||||||
services.AddSingleton<IKeyService, KeyService>();
|
|
||||||
services.AddSingleton<ITerminalService, TerminalService>();
|
services.AddSingleton<ITerminalService, TerminalService>();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -1,150 +0,0 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace MaksIT.LetsEncryptConsole.Services;
|
|
||||||
|
|
||||||
public interface IKeyService {
|
|
||||||
void ExportPublicKey(RSACryptoServiceProvider csp, TextWriter outputStream);
|
|
||||||
void ExportPrivateKey(RSACryptoServiceProvider csp, TextWriter outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KeyService : IKeyService {
|
|
||||||
/// <summary>
|
|
||||||
/// Export a certificate to a PEM format string
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cert">The certificate to export</param>
|
|
||||||
/// <returns>A PEM encoded string</returns>
|
|
||||||
//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 void ExportPublicKey(RSACryptoServiceProvider csp, TextWriter outputStream) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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-----");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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 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 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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DomainResult.Common" Version="3.1.0" />
|
<PackageReference Include="DomainResult.Common" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||||
<PackageReference Include="SSH.NET" Version="2020.0.2" />
|
<PackageReference Include="SSH.NET" Version="2020.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
using DomainResults.Common;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
using DomainResults.Common;
|
||||||
|
|
||||||
using Renci.SshNet;
|
using Renci.SshNet;
|
||||||
using Renci.SshNet.Common;
|
using Renci.SshNet.Common;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace MaksIT.SSHProvider {
|
namespace MaksIT.SSHProvider {
|
||||||
|
|
||||||
@ -22,8 +25,6 @@ namespace MaksIT.SSHProvider {
|
|||||||
public readonly SshClient _sshClient;
|
public readonly SshClient _sshClient;
|
||||||
public readonly SftpClient _sftpClient;
|
public readonly SftpClient _sftpClient;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SSHService(
|
public SSHService(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
string host,
|
string host,
|
||||||
@ -31,11 +32,40 @@ namespace MaksIT.SSHProvider {
|
|||||||
string username,
|
string username,
|
||||||
string password
|
string password
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||||
|
throw new ArgumentNullException($"{nameof(username)} or {nameof(password)} is null, empty or white space");
|
||||||
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_sshClient = new SshClient(host, port, username, password);
|
_sshClient = new SshClient(host, port, username, password);
|
||||||
_sftpClient = new SftpClient(host, port, username, password);
|
_sftpClient = new SftpClient(host, port, username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SSHService(
|
||||||
|
ILogger logger,
|
||||||
|
string host,
|
||||||
|
int port,
|
||||||
|
string username,
|
||||||
|
string [] privateKeys
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(username) || privateKeys.Any(x => string.IsNullOrWhiteSpace(x)))
|
||||||
|
throw new ArgumentNullException($"{nameof(username)} or {nameof(privateKeys)} contains key which is null, empty or white space");
|
||||||
|
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
var privateKeyFiles = new List<PrivateKeyFile>();
|
||||||
|
foreach (var privateKey in privateKeys) {
|
||||||
|
using (var ms = new MemoryStream(Encoding.ASCII.GetBytes(privateKey))) {
|
||||||
|
privateKeyFiles.Add(new PrivateKeyFile(ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_sshClient = new SshClient(host, port, username, privateKeyFiles.ToArray());
|
||||||
|
_sftpClient = new SftpClient(host, port, username, privateKeyFiles.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
public IDomainResult Connect() {
|
public IDomainResult Connect() {
|
||||||
try {
|
try {
|
||||||
_sshClient.Connect();
|
_sshClient.Connect();
|
||||||
|
|||||||
@ -10,13 +10,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.5.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="coverlet.collector" Version="3.2.0">
|
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user