mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
Certificate request implemented.
This commit is contained in:
parent
e07b45d43f
commit
8d5702b621
@ -6,16 +6,11 @@ namespace QRBee.Core.Security
|
|||||||
{
|
{
|
||||||
public class ReadableCertificateRequest
|
public class ReadableCertificateRequest
|
||||||
{
|
{
|
||||||
private byte[] _rsaPublicKey;
|
|
||||||
private byte[] _signature;
|
private byte[] _signature;
|
||||||
|
|
||||||
public string SubjectName { get; set; }
|
public string SubjectName { get; set; }
|
||||||
|
|
||||||
public string RsaPublicKey
|
public StringRSAParameters RsaPublicKey { get; set; }
|
||||||
{
|
|
||||||
get => _rsaPublicKey != null ? Convert.ToBase64String(_rsaPublicKey): null;
|
|
||||||
set => _rsaPublicKey = value != null ? Convert.FromBase64String(value) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Signature
|
public string Signature
|
||||||
{
|
{
|
||||||
@ -23,7 +18,7 @@ namespace QRBee.Core.Security
|
|||||||
set => _signature = value != null ? Convert.FromBase64String(value) : null;
|
set => _signature = value != null ? Convert.FromBase64String(value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AsDataForSignature() => $"{SubjectName}|{RsaPublicKey}";
|
public string AsDataForSignature() => $"{SubjectName}|{RsaPublicKey.ConvertToJson()}";
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
QRBee.Core/Security/StringRSAParameters.cs
Normal file
27
QRBee.Core/Security/StringRSAParameters.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace QRBee.Core.Security
|
||||||
|
{
|
||||||
|
public class StringRSAParameters
|
||||||
|
{
|
||||||
|
public string StringExponent { get; set; }
|
||||||
|
|
||||||
|
public string StringModulus { get; set; }
|
||||||
|
|
||||||
|
public string StringP { get; set; }
|
||||||
|
|
||||||
|
public string StringQ { get; set; }
|
||||||
|
|
||||||
|
public string StringDP { get; set; }
|
||||||
|
|
||||||
|
public string StringDQ { get; set; }
|
||||||
|
|
||||||
|
public string StringInverseQ { get; set; }
|
||||||
|
|
||||||
|
public string StringD { get; set; }
|
||||||
|
|
||||||
|
public string ConvertToJson() => JsonConvert.SerializeObject(this);
|
||||||
|
|
||||||
|
public static StringRSAParameters ConvertFromJson(string json) => JsonConvert.DeserializeObject<StringRSAParameters>(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -82,6 +82,7 @@
|
|||||||
<Compile Include="MainActivity.cs" />
|
<Compile Include="MainActivity.cs" />
|
||||||
<Compile Include="Resources\Resource.designer.cs" />
|
<Compile Include="Resources\Resource.designer.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Services\CryptoHelper.cs" />
|
||||||
<Compile Include="Services\LocalSettings.cs" />
|
<Compile Include="Services\LocalSettings.cs" />
|
||||||
<Compile Include="Services\AndroidPrivateKeyHandler.cs" />
|
<Compile Include="Services\AndroidPrivateKeyHandler.cs" />
|
||||||
<Compile Include="Services\QRScannerService.cs" />
|
<Compile Include="Services\QRScannerService.cs" />
|
||||||
|
|||||||
@ -24,8 +24,8 @@ namespace QRBee.Droid.Services
|
|||||||
private const int RSABits = 2048;
|
private const int RSABits = 2048;
|
||||||
private const int CertificateValidityDays = 3650;
|
private const int CertificateValidityDays = 3650;
|
||||||
|
|
||||||
private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{SignedCertificateFileName}";
|
private string PrivateKeyFileName => $"{Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)}/{SignedCertificateFileName}";
|
||||||
private string PrivateRsaKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{RawRsaKeyFileName}";
|
private string PrivateRsaKeyFileName => $"{Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)}/{RawRsaKeyFileName}";
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Exists()
|
public bool Exists()
|
||||||
@ -41,26 +41,51 @@ namespace QRBee.Droid.Services
|
|||||||
File.Delete(PrivateRsaKeyFileName);
|
File.Delete(PrivateRsaKeyFileName);
|
||||||
|
|
||||||
using var rsa = RSA.Create(RSABits);
|
using var rsa = RSA.Create(RSABits);
|
||||||
var bytes = rsa.ExportEncryptedPkcs8PrivateKey(VeryBadNeverUsePrivateKeyPassword, new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, EncryptionIterationCount));
|
|
||||||
|
var s = ExportKeyToJson(rsa,true);
|
||||||
|
|
||||||
|
var bytes = CryptoHelper.EncryptStringAES(s, VeryBadNeverUsePrivateKeyPassword);
|
||||||
File.WriteAllBytes(PrivateRsaKeyFileName, bytes);
|
File.WriteAllBytes(PrivateRsaKeyFileName, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateCertificateRequest(subjectName);
|
return CreateCertificateRequest(subjectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ExportKeyToJson(RSA rsa,bool includePrivateKey)
|
||||||
|
{
|
||||||
|
//Workaround for absence of half of cryptography subsystem in Mono
|
||||||
|
var stringParameters = ExportKey(rsa, includePrivateKey);
|
||||||
|
var s = stringParameters.ConvertToJson();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringRSAParameters ExportKey(RSA rsa, bool includePrivateKey)
|
||||||
|
{
|
||||||
|
var rsaParameters = rsa.ExportParameters(includePrivateKey);
|
||||||
|
var stringParameters = new StringRSAParameters
|
||||||
|
{
|
||||||
|
StringExponent = SafeConvertToBase64(rsaParameters.Exponent),
|
||||||
|
StringModulus = SafeConvertToBase64(rsaParameters.Modulus),
|
||||||
|
StringP = SafeConvertToBase64(rsaParameters.P),
|
||||||
|
StringQ = SafeConvertToBase64(rsaParameters.Q),
|
||||||
|
StringDP = SafeConvertToBase64(rsaParameters.DP),
|
||||||
|
StringDQ = SafeConvertToBase64(rsaParameters.DQ),
|
||||||
|
StringInverseQ = SafeConvertToBase64(rsaParameters.InverseQ),
|
||||||
|
StringD = SafeConvertToBase64(rsaParameters.D)
|
||||||
|
};
|
||||||
|
return stringParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SafeConvertToBase64(byte[] bytes) => bytes == null ? "" : Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadableCertificateRequest CreateCertificateRequest(string subjectName)
|
public ReadableCertificateRequest CreateCertificateRequest(string subjectName)
|
||||||
{
|
{
|
||||||
if (File.Exists(PrivateRsaKeyFileName))
|
using var rsa = LoadRsaPrivateKey();
|
||||||
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 = ExportKey(rsa,false),
|
||||||
SubjectName = subjectName
|
SubjectName = subjectName
|
||||||
};
|
};
|
||||||
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
||||||
@ -144,12 +169,7 @@ namespace QRBee.Droid.Services
|
|||||||
{
|
{
|
||||||
// heavily modified version of:
|
// heavily modified version of:
|
||||||
// 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
|
||||||
|
using var rsa = LoadRsaPrivateKey();
|
||||||
// we can't use LoadPrivateKey here as it creating non-exportable key
|
|
||||||
var pk = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUsePrivateKeyPassword, X509KeyStorageFlags.Exportable);
|
|
||||||
using var rsa = pk.GetRSAPrivateKey();
|
|
||||||
if (rsa == null)
|
|
||||||
throw new CryptographicException("Can't get PrivateKey");
|
|
||||||
|
|
||||||
var newPk = cert.CopyWithPrivateKey(rsa);
|
var newPk = cert.CopyWithPrivateKey(rsa);
|
||||||
|
|
||||||
@ -164,5 +184,30 @@ namespace QRBee.Droid.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RSA LoadRsaPrivateKey()
|
||||||
|
{
|
||||||
|
var bytes = File.ReadAllBytes(PrivateRsaKeyFileName);
|
||||||
|
var s = CryptoHelper.DecryptStringAES(bytes, VeryBadNeverUsePrivateKeyPassword);
|
||||||
|
|
||||||
|
var stringParameters = StringRSAParameters.ConvertFromJson(s);
|
||||||
|
var rsaParameters = new RSAParameters
|
||||||
|
{
|
||||||
|
Exponent = SafeConvertFromBase64(stringParameters.StringExponent),
|
||||||
|
Modulus = SafeConvertFromBase64(stringParameters.StringModulus),
|
||||||
|
P = SafeConvertFromBase64(stringParameters.StringP),
|
||||||
|
Q = SafeConvertFromBase64(stringParameters.StringQ),
|
||||||
|
DP = SafeConvertFromBase64(stringParameters.StringDP),
|
||||||
|
DQ = SafeConvertFromBase64(stringParameters.StringDQ),
|
||||||
|
InverseQ = SafeConvertFromBase64(stringParameters.StringInverseQ),
|
||||||
|
D = SafeConvertFromBase64(stringParameters.StringD)
|
||||||
|
};
|
||||||
|
|
||||||
|
var rsa = RSA.Create(rsaParameters);
|
||||||
|
if (rsa == null)
|
||||||
|
throw new CryptographicException("Can't get PrivateKey");
|
||||||
|
return rsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] SafeConvertFromBase64(string s) => string.IsNullOrWhiteSpace(s) ? null : Convert.FromBase64String(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
136
QRBee/QRBee.Android/Services/CryptoHelper.cs
Normal file
136
QRBee/QRBee.Android/Services/CryptoHelper.cs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Stream = System.IO.Stream;
|
||||||
|
|
||||||
|
namespace QRBee.Droid.Services
|
||||||
|
{
|
||||||
|
internal class CryptoHelper
|
||||||
|
{
|
||||||
|
//https://stackoverflow.com/questions/202011/encrypt-and-decrypt-a-string-in-c
|
||||||
|
|
||||||
|
//While an app specific salt is not the best practice for
|
||||||
|
//password based encryption, it's probably safe enough as long as
|
||||||
|
//it is truly uncommon. Also too much work to alter this answer otherwise.
|
||||||
|
// Never use salt like this
|
||||||
|
private static byte[] _salt = System.Text.Encoding.UTF8.GetBytes("‘wÑÏU4)MÓvcvsª");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encrypt the given string using AES. The string can be decrypted using
|
||||||
|
/// DecryptStringAES(). The sharedSecret parameters must match.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plainText">The text to encrypt.</param>
|
||||||
|
/// <param name="sharedSecret">A password used to generate a key for encryption.</param>
|
||||||
|
public static byte[] EncryptStringAES(string plainText, string sharedSecret)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(plainText))
|
||||||
|
throw new ArgumentNullException(nameof(plainText));
|
||||||
|
if (string.IsNullOrEmpty(sharedSecret))
|
||||||
|
throw new ArgumentNullException(nameof(sharedSecret));
|
||||||
|
|
||||||
|
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// generate the key from the shared secret and the salt
|
||||||
|
var key = new Rfc2898DeriveBytes(sharedSecret, _salt);
|
||||||
|
|
||||||
|
// Create a RijndaelManaged object
|
||||||
|
aesAlg = new RijndaelManaged();
|
||||||
|
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
|
||||||
|
|
||||||
|
// Create a decryptor to perform the stream transform.
|
||||||
|
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
|
||||||
|
|
||||||
|
// Create the streams used for encryption.
|
||||||
|
using MemoryStream msEncrypt = new MemoryStream();
|
||||||
|
// prepend the IV
|
||||||
|
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
|
||||||
|
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
|
||||||
|
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
|
||||||
|
{
|
||||||
|
using (var swEncrypt = new StreamWriter(csEncrypt))
|
||||||
|
{
|
||||||
|
//Write all data to the stream.
|
||||||
|
swEncrypt.Write(plainText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msEncrypt.ToArray();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Clear the RijndaelManaged object.
|
||||||
|
aesAlg?.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decrypt the given string. Assumes the string was encrypted using
|
||||||
|
/// EncryptStringAES(), using an identical sharedSecret.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cipherText">The text to decrypt.</param>
|
||||||
|
/// <param name="sharedSecret">A password used to generate a key for decryption.</param>
|
||||||
|
public static string DecryptStringAES(byte[] bytes, string sharedSecret)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sharedSecret))
|
||||||
|
throw new ArgumentNullException(nameof(sharedSecret));
|
||||||
|
|
||||||
|
// Declare the RijndaelManaged object
|
||||||
|
// used to decrypt the data.
|
||||||
|
RijndaelManaged aesAlg = null;
|
||||||
|
|
||||||
|
// Declare the string used to hold
|
||||||
|
// the decrypted text.
|
||||||
|
string plaintext = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// generate the key from the shared secret and the salt
|
||||||
|
var key = new Rfc2898DeriveBytes(sharedSecret, _salt);
|
||||||
|
|
||||||
|
// Create the streams used for decryption.
|
||||||
|
using var msDecrypt = new MemoryStream(bytes);
|
||||||
|
|
||||||
|
// Create a RijndaelManaged object
|
||||||
|
// with the specified key and IV.
|
||||||
|
aesAlg = new RijndaelManaged();
|
||||||
|
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
|
||||||
|
|
||||||
|
// Get the initialization vector from the encrypted stream
|
||||||
|
aesAlg.IV = ReadByteArray(msDecrypt);
|
||||||
|
|
||||||
|
// Create a decrytor to perform the stream transform.
|
||||||
|
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
|
||||||
|
using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
|
||||||
|
using var srDecrypt = new StreamReader(csDecrypt);
|
||||||
|
|
||||||
|
plaintext = srDecrypt.ReadToEnd();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Clear the RijndaelManaged object.
|
||||||
|
aesAlg?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] ReadByteArray(Stream s)
|
||||||
|
{
|
||||||
|
byte[] rawLength = new byte[sizeof(int)];
|
||||||
|
if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
|
||||||
|
{
|
||||||
|
throw new SystemException("Stream did not contain properly formatted byte array");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
|
||||||
|
if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
|
||||||
|
{
|
||||||
|
throw new SystemException("Did not read byte array properly");
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -103,11 +103,13 @@ namespace QRBee.ViewModels
|
|||||||
var client = new HttpClient(GetInsecureHandler());
|
var client = new HttpClient(GetInsecureHandler());
|
||||||
|
|
||||||
var service = new Core.Client.Client(_settings.QRBeeApiUrl,client);
|
var service = new Core.Client.Client(_settings.QRBeeApiUrl,client);
|
||||||
|
Settings settings;
|
||||||
|
RegistrationRequest request;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//TODO Check if ClientId already in LocalSettings. If Yes update data in database
|
//TODO Check if ClientId already in LocalSettings. If Yes update data in database
|
||||||
var settings = _settings.LoadSettings();
|
|
||||||
|
settings = _settings.LoadSettings();
|
||||||
|
|
||||||
//save local settings
|
//save local settings
|
||||||
settings.CardHolderName = CardHolderName;
|
settings.CardHolderName = CardHolderName;
|
||||||
@ -128,15 +130,25 @@ namespace QRBee.ViewModels
|
|||||||
_privateKeyHandler.GeneratePrivateKey(settings.Name);
|
_privateKeyHandler.GeneratePrivateKey(settings.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = new RegistrationRequest
|
request = new RegistrationRequest
|
||||||
{
|
{
|
||||||
DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"),
|
DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"),
|
||||||
Email = Email,
|
Email = Email,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
CertificateRequest = _privateKeyHandler.CreateCertificateRequest(Email),
|
CertificateRequest = _privateKeyHandler.CreateCertificateRequest(Email),
|
||||||
RegisterAsMerchant = false
|
RegisterAsMerchant = false
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//TODO: delete exception message in error message
|
||||||
|
var page = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
|
||||||
|
await page.DisplayAlert("Error", $"The ClientSide isn't working: {e.Message}", "Ok");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
if (!settings.IsRegistered)
|
if (!settings.IsRegistered)
|
||||||
{
|
{
|
||||||
var response = await service.RegisterAsync(request);
|
var response = await service.RegisterAsync(request);
|
||||||
@ -159,8 +171,6 @@ namespace QRBee.ViewModels
|
|||||||
await page.DisplayAlert("Success", "Your data has been updated successfully", "Ok");
|
await page.DisplayAlert("Success", "Your data has been updated successfully", "Ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await Shell.Current.GoToAsync($"//{nameof(MainPage)}");
|
await Shell.Current.GoToAsync($"//{nameof(MainPage)}");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@ -49,7 +50,7 @@ namespace QRBee.Api.Services
|
|||||||
var info = Convert(request);
|
var info = Convert(request);
|
||||||
|
|
||||||
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,Encoding.UTF8.GetBytes(request.CertificateRequest.RsaPublicKey.ConvertToJson()));
|
||||||
|
|
||||||
var convertedClientCertificate = Convert(clientCertificate, clientId);
|
var convertedClientCertificate = Convert(clientCertificate, clientId);
|
||||||
await _storage.InsertCertificate(convertedClientCertificate);
|
await _storage.InsertCertificate(convertedClientCertificate);
|
||||||
@ -107,16 +108,45 @@ namespace QRBee.Api.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Check digital signature
|
//Check digital signature
|
||||||
var verified = _securityService.Verify(
|
using var rsa = LoadRsaPublicKey(certificateRequest.RsaPublicKey);
|
||||||
Encoding.UTF8.GetBytes(certificateRequest.AsDataForSignature()),
|
|
||||||
Encoding.UTF8.GetBytes(certificateRequest.Signature),
|
var data = Encoding.UTF8.GetBytes(certificateRequest.AsDataForSignature());
|
||||||
_privateKeyHandler.LoadPrivateKey());
|
var signature = System.Convert.FromBase64String(certificateRequest.Signature);
|
||||||
|
var verified = rsa.VerifyData(
|
||||||
|
data,
|
||||||
|
signature,
|
||||||
|
HashAlgorithmName.SHA256,
|
||||||
|
RSASignaturePadding.Pkcs1
|
||||||
|
);
|
||||||
|
|
||||||
if (!verified)
|
if (!verified)
|
||||||
{
|
{
|
||||||
throw new ApplicationException($"Digital signature is not valid.");
|
throw new ApplicationException($"Digital signature is not valid.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static RSA LoadRsaPublicKey(StringRSAParameters stringParameters)
|
||||||
|
{
|
||||||
|
var rsaParameters = new RSAParameters
|
||||||
|
{
|
||||||
|
Exponent = SafeConvertFromBase64(stringParameters.StringExponent),
|
||||||
|
Modulus = SafeConvertFromBase64(stringParameters.StringModulus),
|
||||||
|
P = SafeConvertFromBase64(stringParameters.StringP),
|
||||||
|
Q = SafeConvertFromBase64(stringParameters.StringQ),
|
||||||
|
DP = SafeConvertFromBase64(stringParameters.StringDP),
|
||||||
|
DQ = SafeConvertFromBase64(stringParameters.StringDQ),
|
||||||
|
InverseQ = SafeConvertFromBase64(stringParameters.StringInverseQ),
|
||||||
|
D = SafeConvertFromBase64(stringParameters.StringD)
|
||||||
|
};
|
||||||
|
|
||||||
|
var rsa = RSA.Create(rsaParameters);
|
||||||
|
if (rsa == null)
|
||||||
|
throw new CryptographicException("Can't create public key");
|
||||||
|
return rsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[]? SafeConvertFromBase64(string? s) => string.IsNullOrWhiteSpace(s) ? null : System.Convert.FromBase64String(s);
|
||||||
|
|
||||||
private static UserInfo Convert(RegistrationRequest request)
|
private static UserInfo Convert(RegistrationRequest request)
|
||||||
{
|
{
|
||||||
return new UserInfo(request.Name, request.Email, request.DateOfBirth);
|
return new UserInfo(request.Name, request.Email, request.DateOfBirth);
|
||||||
|
|||||||
@ -19,7 +19,7 @@ namespace QRBee.Api.Services
|
|||||||
|
|
||||||
private const string VeryBadNeverUseCertificatePassword = "+ñèbòFëc׎ßRúß¿ãçPN";
|
private const string VeryBadNeverUseCertificatePassword = "+ñèbòFëc׎ßRúß¿ãçPN";
|
||||||
|
|
||||||
private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{FileName}";
|
private string PrivateKeyFileName => $"{Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)}/{FileName}";
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Exists()
|
public bool Exists()
|
||||||
@ -56,7 +56,7 @@ namespace QRBee.Api.Services
|
|||||||
|
|
||||||
var request = new ReadableCertificateRequest
|
var request = new ReadableCertificateRequest
|
||||||
{
|
{
|
||||||
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
|
RsaPublicKey = ExportKey(rsa,false),
|
||||||
SubjectName = subjectName
|
SubjectName = subjectName
|
||||||
};
|
};
|
||||||
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
|
||||||
@ -67,6 +67,26 @@ namespace QRBee.Api.Services
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static StringRSAParameters ExportKey(RSA rsa, bool includePrivateKey)
|
||||||
|
{
|
||||||
|
var rsaParameters = rsa.ExportParameters(includePrivateKey);
|
||||||
|
var stringParameters = new StringRSAParameters
|
||||||
|
{
|
||||||
|
StringExponent = SafeConvertToBase64(rsaParameters.Exponent),
|
||||||
|
StringModulus = SafeConvertToBase64(rsaParameters.Modulus),
|
||||||
|
StringP = SafeConvertToBase64(rsaParameters.P),
|
||||||
|
StringQ = SafeConvertToBase64(rsaParameters.Q),
|
||||||
|
StringDP = SafeConvertToBase64(rsaParameters.DP),
|
||||||
|
StringDQ = SafeConvertToBase64(rsaParameters.DQ),
|
||||||
|
StringInverseQ = SafeConvertToBase64(rsaParameters.InverseQ),
|
||||||
|
StringD = SafeConvertToBase64(rsaParameters.D)
|
||||||
|
};
|
||||||
|
return stringParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SafeConvertToBase64(byte[]? bytes) => bytes == null ? "" : Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
|
|
||||||
//private static string AsCsr(CertificateRequest request)
|
//private static string AsCsr(CertificateRequest request)
|
||||||
//{
|
//{
|
||||||
// // https://stackoverflow.com/questions/65943968/how-to-convert-a-csr-text-file-into-net-core-standard-certificaterequest-for-s
|
// // https://stackoverflow.com/questions/65943968/how-to-convert-a-csr-text-file-into-net-core-standard-certificaterequest-for-s
|
||||||
@ -149,7 +169,7 @@ namespace QRBee.Api.Services
|
|||||||
return _certificate;
|
return _certificate;
|
||||||
|
|
||||||
if (!Exists())
|
if (!Exists())
|
||||||
throw new CryptographicException("PrivateKey does not exist");
|
GeneratePrivateKey("QRBeeCA");
|
||||||
|
|
||||||
_certificate = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUseCertificatePassword);
|
_certificate = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUseCertificatePassword);
|
||||||
return _certificate;
|
return _certificate;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user