diff --git a/QRBee.Core/QRBee.Core.csproj b/QRBee.Core/QRBee.Core.csproj
index a0edeeb..d9267ae 100644
--- a/QRBee.Core/QRBee.Core.csproj
+++ b/QRBee.Core/QRBee.Core.csproj
@@ -6,6 +6,14 @@
latest
+
+ True
+
+
+
+ True
+
+
diff --git a/QRBee.Core/Security/IPrivateKeyHandler.cs b/QRBee.Core/Security/IPrivateKeyHandler.cs
index 2c8c0b7..4396bf3 100644
--- a/QRBee.Core/Security/IPrivateKeyHandler.cs
+++ b/QRBee.Core/Security/IPrivateKeyHandler.cs
@@ -18,13 +18,13 @@ namespace QRBee.Core.Security
///
///
/// Certificate request to be sent to CA in PEM format
- ReadableCertificateRequest GeneratePrivateKey(string? subjectName = null);
+ ReadableCertificateRequest GeneratePrivateKey(string subjectName = null);
///
/// Re-create certificate request if CA response was not received in time.
///
/// Certificate request to be sent to CA in PEM format
- ReadableCertificateRequest CreateCertificateRequest();
+ ReadableCertificateRequest CreateCertificateRequest(string subjectName);
///
/// Attach CA-generated public key certificate to the private key
diff --git a/QRBee/QRBee.Android/QRBee.Android.csproj b/QRBee/QRBee.Android/QRBee.Android.csproj
index 136e5b2..4f615ef 100644
--- a/QRBee/QRBee.Android/QRBee.Android.csproj
+++ b/QRBee/QRBee.Android/QRBee.Android.csproj
@@ -38,6 +38,7 @@
false
false
+ true
true
diff --git a/QRBee/QRBee.Android/Services/AndroidPrivateKeyHandler.cs b/QRBee/QRBee.Android/Services/AndroidPrivateKeyHandler.cs
index 9197da5..796cef3 100644
--- a/QRBee/QRBee.Android/Services/AndroidPrivateKeyHandler.cs
+++ b/QRBee/QRBee.Android/Services/AndroidPrivateKeyHandler.cs
@@ -13,49 +13,55 @@ namespace QRBee.Droid.Services
///
public class AndroidPrivateKeyHandler : IPrivateKeyHandler
{
- private X509Certificate2? _certificate;
+ private X509Certificate2 _certificate;
private readonly object _syncObject = new object();
- private const string FileName = "private_key.p12";
- protected string CommonName { get; set; }
+ private const string RawRsaKeyFileName = "rsa.key";
+ private const string SignedCertificateFileName = "private_key.p12";
+ private const string VeryBadNeverUsePrivateKeyPassword = "’³¶¾]Ô $"{System.Environment.SpecialFolder.LocalApplicationData}/{FileName}";
+ private string PrivateKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{SignedCertificateFileName}";
+ private string PrivateRsaKeyFileName => $"{System.Environment.SpecialFolder.LocalApplicationData}/{RawRsaKeyFileName}";
///
public bool Exists()
=> File.Exists(PrivateKeyFileName);
///
- public ReadableCertificateRequest GeneratePrivateKey(string? subjectName)
+ public ReadableCertificateRequest GeneratePrivateKey(string subjectName)
{
// locking used to make sure that only one thread generating a private key
lock (_syncObject)
{
- var pk = CreateSelfSignedClientCertificate(subjectName ?? CommonName);
- var pkcs12data = pk.Export(X509ContentType.Pfx, CertificatePassword);
- File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
+ if ( File.Exists(PrivateRsaKeyFileName) )
+ File.Delete(PrivateRsaKeyFileName);
- _certificate?.Dispose();
- _certificate = new X509Certificate2(pkcs12data, CertificatePassword);
+ using var rsa = RSA.Create(RSABits);
+ var bytes = rsa.ExportEncryptedPkcs8PrivateKey(VeryBadNeverUsePrivateKeyPassword, new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, EncryptionIterationCount));
+ File.WriteAllBytes(PrivateRsaKeyFileName, bytes);
}
- return CreateCertificateRequest();
+ return CreateCertificateRequest(subjectName);
}
///
- public ReadableCertificateRequest CreateCertificateRequest()
+ public ReadableCertificateRequest CreateCertificateRequest(string subjectName)
{
- var pk = LoadPrivateKey();
- var rsa = pk.GetRSAPublicKey();
+ if (File.Exists(PrivateRsaKeyFileName))
+ 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
{
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
- SubjectName = pk.SubjectName.Name
+ SubjectName = subjectName
};
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
@@ -128,7 +134,7 @@ namespace QRBee.Droid.Services
if (!Exists())
throw new CryptographicException("PrivateKey does not exist");
- _certificate = new X509Certificate2(PrivateKeyFileName, CertificatePassword);
+ _certificate = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUsePrivateKeyPassword);
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
// 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();
if (rsa == null)
throw new CryptographicException("Can't get PrivateKey");
var newPk = cert.CopyWithPrivateKey(rsa);
- var pkcs12data = newPk.Export(X509ContentType.Pfx, CertificatePassword);
+ var pkcs12data = newPk.Export(X509ContentType.Pfx, VeryBadNeverUsePrivateKeyPassword);
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
lock ( _syncObject )
diff --git a/QRBee/QRBee.iOS/QRBee.iOS.csproj b/QRBee/QRBee.iOS/QRBee.iOS.csproj
index 687f47d..76d084d 100644
--- a/QRBee/QRBee.iOS/QRBee.iOS.csproj
+++ b/QRBee/QRBee.iOS/QRBee.iOS.csproj
@@ -51,6 +51,7 @@
Entitlements.plist
None
-all
+ true
none
@@ -133,10 +134,10 @@
- 2.1.4
+ 2.1.5
-
-
+
+
2.4.1
diff --git a/QRBee/QRBee/QRBee.csproj b/QRBee/QRBee/QRBee.csproj
index b56f655..808d2a7 100644
--- a/QRBee/QRBee/QRBee.csproj
+++ b/QRBee/QRBee/QRBee.csproj
@@ -7,17 +7,19 @@
latest
+ True
latest
+ True
-
-
-
+
+
+
diff --git a/QRBee/QRBee/ViewModels/RegisterViewModel.cs b/QRBee/QRBee/ViewModels/RegisterViewModel.cs
index 48f4284..d4050d4 100644
--- a/QRBee/QRBee/ViewModels/RegisterViewModel.cs
+++ b/QRBee/QRBee/ViewModels/RegisterViewModel.cs
@@ -133,7 +133,7 @@ namespace QRBee.ViewModels
DateOfBirth = DateOfBirth.ToString("yyyy-MM-dd"),
Email = Email,
Name = Name,
- CertificateRequest = _privateKeyHandler.CreateCertificateRequest(),
+ CertificateRequest = _privateKeyHandler.CreateCertificateRequest(Email),
RegisterAsMerchant = false
};
diff --git a/QRBeeApi/QRBee.Api.csproj b/QRBeeApi/QRBee.Api.csproj
index 298d8da..3df1c32 100644
--- a/QRBeeApi/QRBee.Api.csproj
+++ b/QRBeeApi/QRBee.Api.csproj
@@ -7,6 +7,14 @@
3b7dc7f1-0b82-4746-b99b-73c43c8826e0
+
+ True
+
+
+
+ True
+
+
diff --git a/QRBeeApi/Services/Database/CertificateInfo.cs b/QRBeeApi/Services/Database/CertificateInfo.cs
new file mode 100644
index 0000000..8c21e68
--- /dev/null
+++ b/QRBeeApi/Services/Database/CertificateInfo.cs
@@ -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; }
+ }
+}
diff --git a/QRBeeApi/Services/Database/IStorage.cs b/QRBeeApi/Services/Database/IStorage.cs
index a839d15..62ca07f 100644
--- a/QRBeeApi/Services/Database/IStorage.cs
+++ b/QRBeeApi/Services/Database/IStorage.cs
@@ -30,5 +30,19 @@
///
/// Information to be inserted
Task PutTransactionInfo(TransactionInfo info);
+
+ ///
+ /// Inserts CertificateInfo into database
+ ///
+ /// Information to be inserted
+ ///
+ Task InsertCertificate(CertificateInfo info);
+
+ ///
+ /// Retrieve certificate information from database
+ ///
+ /// Identifier by which certificate information will be retrieved
+ /// Certificate information
+ Task GetCertificateInfo(string id);
}
}
diff --git a/QRBeeApi/Services/Database/Storage.cs b/QRBeeApi/Services/Database/Storage.cs
index 505814d..fa4a1df 100644
--- a/QRBeeApi/Services/Database/Storage.cs
+++ b/QRBeeApi/Services/Database/Storage.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Options;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Extensions.Options;
using MongoDB.Driver;
using QRBee.Api.Controllers;
@@ -100,5 +101,50 @@ namespace QRBee.Api.Services.Database
return cursor.Current.FirstOrDefault();
}
+
+ public async Task InsertCertificate(CertificateInfo info)
+ {
+ var collection = _database.GetCollection("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}");
+
+ }
+
+ ///
+ /// Try to find if the Certificate already exists in the database
+ ///
+ /// parameter by which to find CertificateInfo
+ /// null if certificate doesn't exist or CertificateInfo
+ private async Task TryGetCertificateInfo(string id)
+ {
+ var collection = _database.GetCollection("Transactions");
+ using var cursor = await collection.FindAsync($"{{ Id: \"{id}\" }}");
+ if (!await cursor.MoveNextAsync())
+ {
+ return null;
+ }
+
+ return cursor.Current.FirstOrDefault();
+ }
+
+ public async Task GetCertificateInfo(string id)
+ {
+ var certificate = await TryGetCertificateInfo(id);
+ return certificate ?? throw new ApplicationException($"Certificate with Id: {id} not found.");
+ }
}
}
diff --git a/QRBeeApi/Services/IQRBeeAPI.cs b/QRBeeApi/Services/IQRBeeAPI.cs
index 0d9f0ff..ad4c504 100644
--- a/QRBeeApi/Services/IQRBeeAPI.cs
+++ b/QRBeeApi/Services/IQRBeeAPI.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.AspNetCore.Mvc;
using QRBee.Core;
using QRBee.Core.Data;
diff --git a/QRBeeApi/Services/QRBeeAPIService.cs b/QRBeeApi/Services/QRBeeAPIService.cs
index e6fc023..0c7f098 100644
--- a/QRBeeApi/Services/QRBeeAPIService.cs
+++ b/QRBeeApi/Services/QRBeeAPIService.cs
@@ -1,4 +1,5 @@
using System.Globalization;
+using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using QRBee.Api.Services.Database;
@@ -49,7 +50,9 @@ namespace QRBee.Api.Services
var clientId = await _storage.PutUserInfo(info);
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
{
@@ -124,5 +127,17 @@ namespace QRBee.Api.Services
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
+ };
+ }
+
}
}
diff --git a/QRBeeApi/Services/ServerPrivateKeyHandler.cs b/QRBeeApi/Services/ServerPrivateKeyHandler.cs
index c5c9372..1b50a1d 100644
--- a/QRBeeApi/Services/ServerPrivateKeyHandler.cs
+++ b/QRBeeApi/Services/ServerPrivateKeyHandler.cs
@@ -14,11 +14,10 @@ namespace QRBee.Api.Services
private readonly object _syncObject = new object();
private const string FileName = "private_key.p12";
- protected string CommonName { get; set; }
private const int RSABits = 2048;
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}";
@@ -27,33 +26,38 @@ namespace QRBee.Api.Services
=> File.Exists(PrivateKeyFileName);
///
- public ReadableCertificateRequest GeneratePrivateKey(string? subjectName)
+ public ReadableCertificateRequest GeneratePrivateKey(string subjectName)
{
// locking used to make sure that only one thread generating a private key
lock (_syncObject)
{
- var pk = CreateSelfSignedServerCertificate(subjectName ?? CommonName);
- var pkcs12data = pk.Export(X509ContentType.Pfx, CertificatePassword);
+ var pk = CreateSelfSignedServerCertificate(subjectName);
+ var pkcs12data = pk.Export(X509ContentType.Pfx, VeryBadNeverUseCertificatePassword);
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
_certificate?.Dispose();
- _certificate = new X509Certificate2(pkcs12data, CertificatePassword);
+ _certificate = new X509Certificate2(pkcs12data, VeryBadNeverUseCertificatePassword);
}
- return CreateCertificateRequest();
+ return CreateCertificateRequest(subjectName);
}
///
- 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.
var pk = LoadPrivateKey();
var rsa = pk.GetRSAPublicKey();
+ if (rsa == null)
+ {
+ throw new ApplicationException("Object missing public key.");
+ }
+
var request = new ReadableCertificateRequest
{
RsaPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()),
- SubjectName = pk.SubjectName.Name
+ SubjectName = subjectName
};
var data = Encoding.UTF8.GetBytes(request.AsDataForSignature());
@@ -147,7 +151,7 @@ namespace QRBee.Api.Services
if (!Exists())
throw new CryptographicException("PrivateKey does not exist");
- _certificate = new X509Certificate2(PrivateKeyFileName, CertificatePassword);
+ _certificate = new X509Certificate2(PrivateKeyFileName, VeryBadNeverUseCertificatePassword);
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
// 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();
if (rsa == null)
throw new CryptographicException("Can't get PrivateKey");
var newPk = cert.CopyWithPrivateKey(rsa);
- var pkcs12data = newPk.Export(X509ContentType.Pfx, CertificatePassword);
+ var pkcs12data = newPk.Export(X509ContentType.Pfx, VeryBadNeverUseCertificatePassword);
File.WriteAllBytes(PrivateKeyFileName, pkcs12data);
lock ( _syncObject )