mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
Certificate information can now be stored in database. Second attempt at key generation under Android still no luck.
This commit is contained in:
parent
6da55bc4a5
commit
e07b45d43f
@ -6,6 +6,14 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -18,13 +18,13 @@ namespace QRBee.Core.Security
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="subjectName"></param>
|
/// <param name="subjectName"></param>
|
||||||
/// <returns>Certificate request to be sent to CA in PEM format</returns>
|
/// <returns>Certificate request to be sent to CA in PEM format</returns>
|
||||||
ReadableCertificateRequest GeneratePrivateKey(string? subjectName = null);
|
ReadableCertificateRequest GeneratePrivateKey(string subjectName = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Re-create certificate request if CA response was not received in time.
|
/// Re-create certificate request if CA response was not received in time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Certificate request to be sent to CA in PEM format</returns>
|
/// <returns>Certificate request to be sent to CA in PEM format</returns>
|
||||||
ReadableCertificateRequest CreateCertificateRequest();
|
ReadableCertificateRequest CreateCertificateRequest(string subjectName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attach CA-generated public key certificate to the private key
|
/// Attach CA-generated public key certificate to the private key
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||||
<BundleAssemblies>false</BundleAssemblies>
|
<BundleAssemblies>false</BundleAssemblies>
|
||||||
<MandroidI18n />
|
<MandroidI18n />
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|||||||
@ -13,49 +13,55 @@ namespace QRBee.Droid.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AndroidPrivateKeyHandler : IPrivateKeyHandler
|
public class AndroidPrivateKeyHandler : IPrivateKeyHandler
|
||||||
{
|
{
|
||||||
private X509Certificate2? _certificate;
|
private X509Certificate2 _certificate;
|
||||||
private readonly object _syncObject = new object();
|
private readonly object _syncObject = new object();
|
||||||
|
|
||||||
private const string FileName = "private_key.p12";
|
private const string RawRsaKeyFileName = "rsa.key";
|
||||||
protected string CommonName { get; set; }
|
private const string SignedCertificateFileName = "private_key.p12";
|
||||||
|
private const string VeryBadNeverUsePrivateKeyPassword = "’³¶¾]Ô<N◄¾♪¢ :6TyŽ÷ç♦Mô¶–²ùPÎJj";
|
||||||
|
private const int EncryptionIterationCount = 534;
|
||||||
|
|
||||||
private const int RSABits = 2048;
|
private const int RSABits = 2048;
|
||||||
private const int CertificateValidityDays = 3650;
|
private const int CertificateValidityDays = 3650;
|
||||||
|
|
||||||
protected string CertificatePassword { get; set; }
|
private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{SignedCertificateFileName}";
|
||||||
|
private string PrivateRsaKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{RawRsaKeyFileName}";
|
||||||
private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{FileName}";
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Exists()
|
public bool Exists()
|
||||||
=> File.Exists(PrivateKeyFileName);
|
=> File.Exists(PrivateKeyFileName);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadableCertificateRequest GeneratePrivateKey(string? subjectName)
|
public ReadableCertificateRequest GeneratePrivateKey(string subjectName)
|
||||||
{
|
{
|
||||||
// locking used to make sure that only one thread generating a private key
|
// locking used to make sure that only one thread generating a private key
|
||||||
lock (_syncObject)
|
lock (_syncObject)
|
||||||
{
|
{
|
||||||
var pk = CreateSelfSignedClientCertificate(subjectName ?? CommonName);
|
if ( File.Exists(PrivateRsaKeyFileName) )
|
||||||
var pkcs12data = pk.Export(X509ContentType.Pfx, CertificatePassword);
|
File.Delete(PrivateRsaKeyFileName);
|
||||||
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
|
||||||
|
|
||||||
_certificate?.Dispose();
|
using var rsa = RSA.Create(RSABits);
|
||||||
_certificate = new X509Certificate2(pkcs12data, CertificatePassword);
|
var bytes = rsa.ExportEncryptedPkcs8PrivateKey(VeryBadNeverUsePrivateKeyPassword, new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, EncryptionIterationCount));
|
||||||
|
File.WriteAllBytes(PrivateRsaKeyFileName, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateCertificateRequest();
|
return CreateCertificateRequest(subjectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadableCertificateRequest CreateCertificateRequest()
|
public ReadableCertificateRequest CreateCertificateRequest(string subjectName)
|
||||||
{
|
{
|
||||||
var pk = LoadPrivateKey();
|
if (File.Exists(PrivateRsaKeyFileName))
|
||||||
var rsa = pk.GetRSAPublicKey();
|
throw new ApplicationException("Private key does not exist");
|
||||||
|
|
||||||
|
var bytes = File.ReadAllBytes(PrivateRsaKeyFileName);
|
||||||
|
using var rsa = RSA.Create(RSABits);
|
||||||
|
rsa.ImportEncryptedPkcs8PrivateKey(VeryBadNeverUsePrivateKeyPassword, bytes, out _);
|
||||||
|
|
||||||
var request = new ReadableCertificateRequest
|
var request = new ReadableCertificateRequest
|
||||||
{
|
{
|
||||||
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
|
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
|
||||||
SubjectName = pk.SubjectName.Name
|
SubjectName = subjectName
|
||||||
};
|
};
|
||||||
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
||||||
|
|
||||||
@ -128,7 +134,7 @@ namespace QRBee.Droid.Services
|
|||||||
if (!Exists())
|
if (!Exists())
|
||||||
throw new CryptographicException("PrivateKey does not exist");
|
throw new CryptographicException("PrivateKey does not exist");
|
||||||
|
|
||||||
_certificate = new X509Certificate2(PrivateKeyFileName, CertificatePassword);
|
_certificate = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUsePrivateKeyPassword);
|
||||||
return _certificate;
|
return _certificate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,14 +146,14 @@ namespace QRBee.Droid.Services
|
|||||||
// https://stackoverflow.com/questions/18462064/associate-a-private-key-with-the-x509certificate2-class-in-net
|
// https://stackoverflow.com/questions/18462064/associate-a-private-key-with-the-x509certificate2-class-in-net
|
||||||
|
|
||||||
// we can't use LoadPrivateKey here as it creating non-exportable key
|
// we can't use LoadPrivateKey here as it creating non-exportable key
|
||||||
var pk = new X509Certificate2(PrivateKeyFileName, CertificatePassword, X509KeyStorageFlags.Exportable);
|
var pk = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUsePrivateKeyPassword, X509KeyStorageFlags.Exportable);
|
||||||
using var rsa = pk.GetRSAPrivateKey();
|
using var rsa = pk.GetRSAPrivateKey();
|
||||||
if (rsa == null)
|
if (rsa == null)
|
||||||
throw new CryptographicException("Can't get PrivateKey");
|
throw new CryptographicException("Can't get PrivateKey");
|
||||||
|
|
||||||
var newPk = cert.CopyWithPrivateKey(rsa);
|
var newPk = cert.CopyWithPrivateKey(rsa);
|
||||||
|
|
||||||
var pkcs12data = newPk.Export(X509ContentType.Pfx, CertificatePassword);
|
var pkcs12data = newPk.Export(X509ContentType.Pfx, VeryBadNeverUsePrivateKeyPassword);
|
||||||
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
||||||
|
|
||||||
lock ( _syncObject )
|
lock ( _syncObject )
|
||||||
|
|||||||
@ -51,6 +51,7 @@
|
|||||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||||
<MtouchLink>None</MtouchLink>
|
<MtouchLink>None</MtouchLink>
|
||||||
<MtouchInterpreter>-all</MtouchInterpreter>
|
<MtouchInterpreter>-all</MtouchInterpreter>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@ -133,10 +134,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Plugin.Fingerprint">
|
<PackageReference Include="Plugin.Fingerprint">
|
||||||
<Version>2.1.4</Version>
|
<Version>2.1.5</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2244" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile">
|
<PackageReference Include="ZXing.Net.Mobile">
|
||||||
<Version>2.4.1</Version>
|
<Version>2.4.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@ -7,17 +7,19 @@
|
|||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2244" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -133,7 +133,7 @@ namespace QRBee.ViewModels
|
|||||||
DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"),
|
DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"),
|
||||||
Email = Email,
|
Email = Email,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
CertificateRequest = _privateKeyHandler.CreateCertificateRequest(),
|
CertificateRequest = _privateKeyHandler.CreateCertificateRequest(Email),
|
||||||
RegisterAsMerchant = false
|
RegisterAsMerchant = false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,14 @@
|
|||||||
<UserSecretsId>3b7dc7f1-0b82-4746-b99b-73c43c8826e0</UserSecretsId>
|
<UserSecretsId>3b7dc7f1-0b82-4746-b99b-73c43c8826e0</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="6.1.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="6.1.0" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
|
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
|
||||||
|
|||||||
14
QRBeeApi/Services/Database/CertificateInfo.cs
Normal file
14
QRBeeApi/Services/Database/CertificateInfo.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace QRBee.Api.Services.Database
|
||||||
|
{
|
||||||
|
public class CertificateInfo
|
||||||
|
{
|
||||||
|
|
||||||
|
[BsonId] public string? Id { get; set; }
|
||||||
|
public string? ClientId { get; set; }
|
||||||
|
public string? Certificate { get; set; }
|
||||||
|
public DateTime ServerTimeStamp { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,5 +30,19 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">Information to be inserted</param>
|
/// <param name="info">Information to be inserted</param>
|
||||||
Task PutTransactionInfo(TransactionInfo info);
|
Task PutTransactionInfo(TransactionInfo info);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts CertificateInfo into database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">Information to be inserted</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task InsertCertificate(CertificateInfo info);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve certificate information from database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Identifier by which certificate information will be retrieved</param>
|
||||||
|
/// <returns>Certificate information</returns>
|
||||||
|
Task<CertificateInfo> GetCertificateInfo(string id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
using QRBee.Api.Controllers;
|
using QRBee.Api.Controllers;
|
||||||
|
|
||||||
@ -100,5 +101,50 @@ namespace QRBee.Api.Services.Database
|
|||||||
|
|
||||||
return cursor.Current.FirstOrDefault();
|
return cursor.Current.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task InsertCertificate(CertificateInfo info)
|
||||||
|
{
|
||||||
|
var collection = _database.GetCollection<CertificateInfo>("Certificates");
|
||||||
|
|
||||||
|
if (info.Id == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Info Id is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var certificate = await TryGetCertificateInfo(info.Id);
|
||||||
|
|
||||||
|
if (certificate == null)
|
||||||
|
{
|
||||||
|
await collection.InsertOneAsync(info);
|
||||||
|
_logger.LogInformation($"Inserted new certificate with ID: {info.Id}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation($"Found certificate with ID: {info.Id}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to find if the Certificate already exists in the database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">parameter by which to find CertificateInfo</param>
|
||||||
|
/// <returns>null if certificate doesn't exist or CertificateInfo</returns>
|
||||||
|
private async Task<CertificateInfo?> TryGetCertificateInfo(string id)
|
||||||
|
{
|
||||||
|
var collection = _database.GetCollection<CertificateInfo>("Transactions");
|
||||||
|
using var cursor = await collection.FindAsync($"{{ Id: \"{id}\" }}");
|
||||||
|
if (!await cursor.MoveNextAsync())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor.Current.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CertificateInfo> GetCertificateInfo(string id)
|
||||||
|
{
|
||||||
|
var certificate = await TryGetCertificateInfo(id);
|
||||||
|
return certificate ?? throw new ApplicationException($"Certificate with Id: {id} not found.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using QRBee.Core;
|
using QRBee.Core;
|
||||||
using QRBee.Core.Data;
|
using QRBee.Core.Data;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using QRBee.Api.Services.Database;
|
using QRBee.Api.Services.Database;
|
||||||
@ -49,7 +50,9 @@ namespace QRBee.Api.Services
|
|||||||
|
|
||||||
var clientId = await _storage.PutUserInfo(info);
|
var clientId = await _storage.PutUserInfo(info);
|
||||||
var clientCertificate = _securityService.CreateCertificate(clientId,System.Convert.FromBase64String(request.CertificateRequest.RsaPublicKey));
|
var clientCertificate = _securityService.CreateCertificate(clientId,System.Convert.FromBase64String(request.CertificateRequest.RsaPublicKey));
|
||||||
//TODO save certificate to certificate mongoDB collection
|
|
||||||
|
var convertedClientCertificate = Convert(clientCertificate, clientId);
|
||||||
|
await _storage.InsertCertificate(convertedClientCertificate);
|
||||||
|
|
||||||
return new RegistrationResponse
|
return new RegistrationResponse
|
||||||
{
|
{
|
||||||
@ -124,5 +127,17 @@ namespace QRBee.Api.Services
|
|||||||
return new TransactionInfo(request, DateTime.UtcNow);
|
return new TransactionInfo(request, DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CertificateInfo Convert(X509Certificate2 certificate, string clientId)
|
||||||
|
{
|
||||||
|
var convertedCertificate = _securityService.Serialize(certificate);
|
||||||
|
return new CertificateInfo
|
||||||
|
{
|
||||||
|
Id = certificate.SerialNumber,
|
||||||
|
ClientId = clientId,
|
||||||
|
Certificate = convertedCertificate,
|
||||||
|
ServerTimeStamp = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,10 @@ namespace QRBee.Api.Services
|
|||||||
private readonly object _syncObject = new object();
|
private readonly object _syncObject = new object();
|
||||||
|
|
||||||
private const string FileName = "private_key.p12";
|
private const string FileName = "private_key.p12";
|
||||||
protected string CommonName { get; set; }
|
|
||||||
private const int RSABits = 2048;
|
private const int RSABits = 2048;
|
||||||
private const int CertificateValidityDays = 3650;
|
private const int CertificateValidityDays = 3650;
|
||||||
|
|
||||||
protected string CertificatePassword { get; set; }
|
private const string VeryBadNeverUseCertificatePassword = "+ñèbòFëc׎ßRúß¿ãçPN";
|
||||||
|
|
||||||
private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{FileName}";
|
private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{FileName}";
|
||||||
|
|
||||||
@ -27,33 +26,38 @@ namespace QRBee.Api.Services
|
|||||||
=> File.Exists(PrivateKeyFileName);
|
=> File.Exists(PrivateKeyFileName);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadableCertificateRequest GeneratePrivateKey(string? subjectName)
|
public ReadableCertificateRequest GeneratePrivateKey(string subjectName)
|
||||||
{
|
{
|
||||||
// locking used to make sure that only one thread generating a private key
|
// locking used to make sure that only one thread generating a private key
|
||||||
lock (_syncObject)
|
lock (_syncObject)
|
||||||
{
|
{
|
||||||
var pk = CreateSelfSignedServerCertificate(subjectName ?? CommonName);
|
var pk = CreateSelfSignedServerCertificate(subjectName);
|
||||||
var pkcs12data = pk.Export(X509ContentType.Pfx, CertificatePassword);
|
var pkcs12data = pk.Export(X509ContentType.Pfx, VeryBadNeverUseCertificatePassword);
|
||||||
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
||||||
|
|
||||||
_certificate?.Dispose();
|
_certificate?.Dispose();
|
||||||
_certificate = new X509Certificate2(pkcs12data, CertificatePassword);
|
_certificate = new X509Certificate2(pkcs12data, VeryBadNeverUseCertificatePassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateCertificateRequest();
|
return CreateCertificateRequest(subjectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadableCertificateRequest CreateCertificateRequest()
|
public ReadableCertificateRequest CreateCertificateRequest(string subjectName)
|
||||||
{
|
{
|
||||||
//TODO in fact server should create certificate request in standard format if we ever want to get externally sighed certificate.
|
//TODO in fact server should create certificate request in standard format if we ever want to get externally sighed certificate.
|
||||||
var pk = LoadPrivateKey();
|
var pk = LoadPrivateKey();
|
||||||
var rsa = pk.GetRSAPublicKey();
|
var rsa = pk.GetRSAPublicKey();
|
||||||
|
|
||||||
|
if (rsa == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Object missing public key.");
|
||||||
|
}
|
||||||
|
|
||||||
var request = new ReadableCertificateRequest
|
var request = new ReadableCertificateRequest
|
||||||
{
|
{
|
||||||
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
|
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
|
||||||
SubjectName = pk.SubjectName.Name
|
SubjectName = subjectName
|
||||||
};
|
};
|
||||||
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
||||||
|
|
||||||
@ -147,7 +151,7 @@ namespace QRBee.Api.Services
|
|||||||
if (!Exists())
|
if (!Exists())
|
||||||
throw new CryptographicException("PrivateKey does not exist");
|
throw new CryptographicException("PrivateKey does not exist");
|
||||||
|
|
||||||
_certificate = new X509Certificate2(PrivateKeyFileName, CertificatePassword);
|
_certificate = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUseCertificatePassword);
|
||||||
return _certificate;
|
return _certificate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,14 +163,14 @@ namespace QRBee.Api.Services
|
|||||||
// https://stackoverflow.com/questions/18462064/associate-a-private-key-with-the-x509certificate2-class-in-net
|
// https://stackoverflow.com/questions/18462064/associate-a-private-key-with-the-x509certificate2-class-in-net
|
||||||
|
|
||||||
// we can't use LoadPrivateKey here as it creating non-exportable key
|
// we can't use LoadPrivateKey here as it creating non-exportable key
|
||||||
var pk = new X509Certificate2(PrivateKeyFileName, CertificatePassword, X509KeyStorageFlags.Exportable);
|
var pk = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUseCertificatePassword, X509KeyStorageFlags.Exportable);
|
||||||
using var rsa = pk.GetRSAPrivateKey();
|
using var rsa = pk.GetRSAPrivateKey();
|
||||||
if (rsa == null)
|
if (rsa == null)
|
||||||
throw new CryptographicException("Can't get PrivateKey");
|
throw new CryptographicException("Can't get PrivateKey");
|
||||||
|
|
||||||
var newPk = cert.CopyWithPrivateKey(rsa);
|
var newPk = cert.CopyWithPrivateKey(rsa);
|
||||||
|
|
||||||
var pkcs12data = newPk.Export(X509ContentType.Pfx, CertificatePassword);
|
var pkcs12data = newPk.Export(X509ContentType.Pfx, VeryBadNeverUseCertificatePassword);
|
||||||
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
|
||||||
|
|
||||||
lock ( _syncObject )
|
lock ( _syncObject )
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user