From 551a44c38dfae2c3b837e0654a3e19852f1cad43 Mon Sep 17 00:00:00 2001 From: Andrey Shabarshov Date: Wed, 16 Feb 2022 17:35:00 +0000 Subject: [PATCH] Dependency Injection container added. SecurityService work stated, not finished. Minor UI improvements. BROKEN --- QRBee.Core/Data/ClientToMerchantResponse.cs | 7 +- QRBee.Core/Data/MerchantToClientRequest.cs | 11 +- QRBee.Core/Security/IPrivateKeyHandler.cs | 8 +- QRBee.Core/Security/PrivateKeyHandlerBase.cs | 39 +++++++ QRBee/QRBee.Android/MainActivity.cs | 19 +++- QRBee/QRBee.Android/QRBee.Android.csproj | 5 +- .../QRBee.Android/Resources/values/colors.xml | 6 +- .../Services/AndroidSecurityService.cs | 56 ++++++++++ .../QRBee.Android/Services/SecurityService.cs | 40 ------- QRBee/QRBee/App.xaml | 3 +- QRBee/QRBee/App.xaml.cs | 35 +++++- QRBee/QRBee/AppShell.xaml | 2 - QRBee/QRBee/QRBee.csproj | 1 + .../QRBee/Services/ClientPrivateKeyHandler.cs | 23 ++++ QRBee/QRBee/ViewModels/ClientPageViewModel.cs | 104 ++++++++++++++---- .../QRBee/ViewModels/MerchantPageViewModel.cs | 21 +++- QRBee/QRBee/ViewModels/RegisterViewModel.cs | 16 +-- QRBee/QRBee/Views/ClientPage.xaml | 13 +-- QRBee/QRBee/Views/ClientPage.xaml.cs | 2 +- QRBee/QRBee/Views/MainPage.xaml | 2 - QRBee/QRBee/Views/MerchantPage.xaml.cs | 2 +- QRBee/QRBee/Views/RegisterPage.xaml.cs | 2 +- QRBeeApi/Services/SecurityService.cs | 86 ++++++++++----- QRBeeApi/Services/ServerPrivateKeyHandler.cs | 21 ++++ 24 files changed, 385 insertions(+), 139 deletions(-) create mode 100644 QRBee.Core/Security/PrivateKeyHandlerBase.cs create mode 100644 QRBee/QRBee.Android/Services/AndroidSecurityService.cs delete mode 100644 QRBee/QRBee.Android/Services/SecurityService.cs create mode 100644 QRBee/QRBee/Services/ClientPrivateKeyHandler.cs create mode 100644 QRBeeApi/Services/ServerPrivateKeyHandler.cs diff --git a/QRBee.Core/Data/ClientToMerchantResponse.cs b/QRBee.Core/Data/ClientToMerchantResponse.cs index a54b65a..cb2a8d8 100644 --- a/QRBee.Core/Data/ClientToMerchantResponse.cs +++ b/QRBee.Core/Data/ClientToMerchantResponse.cs @@ -36,7 +36,9 @@ /// Convert ClientToMerchantResponse to string to be used as QR Code source (along with client signature) /// /// Converted string - public string AsQRCodeString() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}"; + public string AsQRCodeString() => $"{AsDataForSignature()}|{ClientSignature}"; + + public string AsDataForSignature() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}"; /// /// Convert from string @@ -56,7 +58,8 @@ { MerchantRequest = MerchantToClientRequest.FromString(string.Join("|", s.Skip(2))), ClientId = s[0], - TimeStampUTC = DateTime.ParseExact(s[1], "O", null) + TimeStampUTC = DateTime.ParseExact(s[1], "O", null), + ClientSignature = s[3] }; return res; diff --git a/QRBee.Core/Data/MerchantToClientRequest.cs b/QRBee.Core/Data/MerchantToClientRequest.cs index 42e7a8b..6f843ce 100644 --- a/QRBee.Core/Data/MerchantToClientRequest.cs +++ b/QRBee.Core/Data/MerchantToClientRequest.cs @@ -44,7 +44,9 @@ namespace QRBee.Core.Data /// Convert MerchantToClientRequest to string to be used as QR Code source (along with merchant signature) /// /// String conversion - public string AsQRCodeString() => $"{MerchantTransactionId}|{MerchantId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:O}"; + public string AsQRCodeString() => $"{AsDataForSignature()}|{MerchantSignature}"; + + public string AsDataForSignature() => $"{MerchantId}|{MerchantTransactionId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:O}"; /// /// Convert from string @@ -55,9 +57,9 @@ namespace QRBee.Core.Data public static MerchantToClientRequest FromString(string input) { var s = input.Split('|'); - if (s.Length != 5) + if (s.Length != 6) { - throw new ApplicationException("Expected 5 elements"); + throw new ApplicationException("Expected 6 elements"); } var res = new MerchantToClientRequest @@ -66,7 +68,8 @@ namespace QRBee.Core.Data MerchantTransactionId = s[1], Name = s[2], Amount = Convert.ToDecimal(s[3], CultureInfo.InvariantCulture), - TimeStampUTC = DateTime.ParseExact(s[4],"O",null) + TimeStampUTC = DateTime.ParseExact(s[4],"O",null), + MerchantSignature = s[5] }; diff --git a/QRBee.Core/Security/IPrivateKeyHandler.cs b/QRBee.Core/Security/IPrivateKeyHandler.cs index 873f67e..c0c38d0 100644 --- a/QRBee.Core/Security/IPrivateKeyHandler.cs +++ b/QRBee.Core/Security/IPrivateKeyHandler.cs @@ -17,14 +17,14 @@ namespace QRBee.Core.Security /// Generate new private key and store it /// /// - /// Certificate request to be sent to CA - byte [] GeneratePrivateKey(string? subjectName = null); + /// Certificate request to be sent to CA in PEM format + string GeneratePrivateKey(string? subjectName = null); /// /// Re-create certificate request if CA response was not received in time. /// - /// - byte[] CreateCertificateRequest(); + /// Certificate request to be sent to CA in PEM format + string CreateCertificateRequest(); /// /// Attach CA-generated public key certificate to the private key diff --git a/QRBee.Core/Security/PrivateKeyHandlerBase.cs b/QRBee.Core/Security/PrivateKeyHandlerBase.cs new file mode 100644 index 0000000..19acba8 --- /dev/null +++ b/QRBee.Core/Security/PrivateKeyHandlerBase.cs @@ -0,0 +1,39 @@ +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace QRBee.Core.Security +{ + /// + /// Private key handler for API server + /// + public class PrivateKeyHandlerBase : IPrivateKeyHandler + { + + protected string CommonName { get; set; } + protected string CertificatePassword { get; set; } + public bool Exists() + { + throw new NotImplementedException(); + } + + public string GeneratePrivateKey(string? subjectName = null) + { + throw new NotImplementedException(); + } + + public string CreateCertificateRequest() + { + throw new NotImplementedException(); + } + + public void AttachCertificate(X509Certificate2 cert) + { + throw new NotImplementedException(); + } + + public X509Certificate2 LoadPrivateKey() + { + throw new NotImplementedException(); + } + } +} diff --git a/QRBee/QRBee.Android/MainActivity.cs b/QRBee/QRBee.Android/MainActivity.cs index c98ddc1..5ec7a1f 100644 --- a/QRBee/QRBee.Android/MainActivity.cs +++ b/QRBee/QRBee.Android/MainActivity.cs @@ -6,11 +6,15 @@ using Android.Runtime; using Android.OS; using Android.Support.V4.Content; using AndroidX.Core.App; +using Microsoft.Extensions.DependencyInjection; using Plugin.Fingerprint; +using QRBee.Core.Security; +using QRBee.Droid.Services; +using QRBee.Services; namespace QRBee.Droid { - [Activity(Label = "QRBee", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )] + [Activity(Label = "QRBee", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait)] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) @@ -23,7 +27,7 @@ namespace QRBee.Droid CrossFingerprint.SetCurrentActivityResolver(()=>Xamarin.Essentials.Platform.CurrentActivity); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); - LoadApplication(new App()); + LoadApplication(new App(AddServices)); ZXing.Mobile.MobileBarcodeScanner.Initialize(Application); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == (int) Permission.Granted) @@ -37,11 +41,22 @@ namespace QRBee.Droid } } + public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); } + + private static void AddServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + ; + } + } } \ No newline at end of file diff --git a/QRBee/QRBee.Android/QRBee.Android.csproj b/QRBee/QRBee.Android/QRBee.Android.csproj index 2673c98..d4ca956 100644 --- a/QRBee/QRBee.Android/QRBee.Android.csproj +++ b/QRBee/QRBee.Android/QRBee.Android.csproj @@ -59,6 +59,9 @@ + + 6.0.0 + 2.1.4 @@ -80,7 +83,7 @@ - + diff --git a/QRBee/QRBee.Android/Resources/values/colors.xml b/QRBee/QRBee.Android/Resources/values/colors.xml index d9f6e0b..4acb009 100644 --- a/QRBee/QRBee.Android/Resources/values/colors.xml +++ b/QRBee/QRBee.Android/Resources/values/colors.xml @@ -1,7 +1,7 @@ #FFFFFF - #3F51B5 - #303F9F - #FF4081 + #FFC928 + #FFC928 + #FFC928 diff --git a/QRBee/QRBee.Android/Services/AndroidSecurityService.cs b/QRBee/QRBee.Android/Services/AndroidSecurityService.cs new file mode 100644 index 0000000..83f3ede --- /dev/null +++ b/QRBee/QRBee.Android/Services/AndroidSecurityService.cs @@ -0,0 +1,56 @@ +using System; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using QRBee.Core.Security; + +namespace QRBee.Droid.Services +{ + internal class AndroidSecurityService : SecurityServiceBase + { + + public AndroidSecurityService(IPrivateKeyHandler privateKeyHandler) + : base(privateKeyHandler) + { + } + + /// + public override X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey) + { + throw new ApplicationException("Client never issues certificates"); + } + + private const string CertHeader = "-----BEGIN CERTIFICATE-----"; + private const string CertFooter = "-----END CERTIFICATE-----"; + + /// + public override X509Certificate2 Deserialize(string pemData) + { + var start = pemData.IndexOf(CertHeader); + if (start == -1) + throw new ApplicationException("Invalid certificate format"); + start = start + CertHeader.Length; + + var end = pemData.IndexOf(CertFooter); + if (end == -1) + throw new ApplicationException("Invalid certificate format"); + + var base64 = pemData.Substring(start, end - start); // contains new lines, but it does not matter + var data = Convert.FromBase64String(base64); + + return new X509Certificate2(data); + } + + /// + public override string Serialize(X509Certificate2 cert) + { + // https://stackoverflow.com/questions/43928064/export-private-public-keys-from-x509-certificate-to-pem + var builder = new StringBuilder(); + builder.AppendLine(CertHeader); + builder.AppendLine(Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks)); + builder.AppendLine(CertFooter); + + return builder.ToString(); + } + } + +} diff --git a/QRBee/QRBee.Android/Services/SecurityService.cs b/QRBee/QRBee.Android/Services/SecurityService.cs deleted file mode 100644 index 9a2c525..0000000 --- a/QRBee/QRBee.Android/Services/SecurityService.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text.RegularExpressions; -using QRBee.Core.Security; - -namespace QRBee.Api.Services -{ - internal class SecurityService : SecurityServiceBase - { - - public SecurityService(IPrivateKeyHandler privateKeyHandler) - : base(privateKeyHandler) - { - } - - /// - public override X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey) - { - throw new ApplicationException("Client never issues certificates"); - } - - /// - public override X509Certificate2 Deserialize(string pemData) - { - throw new NotImplementedException(); - //return X509Certificate2.CreateFromPem(pemData); - } - - /// - public override string Serialize(X509Certificate2 cert) - { - throw new NotImplementedException(); - // https://stackoverflow.com/questions/43928064/export-private-public-keys-from-x509-certificate-to-pem - //var pem = PemEncoding.Write("CERTIFICATE", cert.RawData); - //return new string(pem); - } - } - -} diff --git a/QRBee/QRBee/App.xaml b/QRBee/QRBee/App.xaml index d2dda44..8f84ed0 100644 --- a/QRBee/QRBee/App.xaml +++ b/QRBee/QRBee/App.xaml @@ -7,7 +7,8 @@ --> - #2196F3 + + #FFC928