mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
Dependency Injection container added. SecurityService work stated, not finished. Minor UI improvements. BROKEN
This commit is contained in:
parent
21f16b862c
commit
551a44c38d
@ -36,7 +36,9 @@
|
|||||||
/// Convert ClientToMerchantResponse to string to be used as QR Code source (along with client signature)
|
/// Convert ClientToMerchantResponse to string to be used as QR Code source (along with client signature)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns> Converted string</returns>
|
/// <returns> Converted string</returns>
|
||||||
public string AsQRCodeString() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}";
|
public string AsQRCodeString() => $"{AsDataForSignature()}|{ClientSignature}";
|
||||||
|
|
||||||
|
public string AsDataForSignature() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert from string
|
/// Convert from string
|
||||||
@ -56,7 +58,8 @@
|
|||||||
{
|
{
|
||||||
MerchantRequest = MerchantToClientRequest.FromString(string.Join("|", s.Skip(2))),
|
MerchantRequest = MerchantToClientRequest.FromString(string.Join("|", s.Skip(2))),
|
||||||
ClientId = s[0],
|
ClientId = s[0],
|
||||||
TimeStampUTC = DateTime.ParseExact(s[1], "O", null)
|
TimeStampUTC = DateTime.ParseExact(s[1], "O", null),
|
||||||
|
ClientSignature = s[3]
|
||||||
};
|
};
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@ -44,7 +44,9 @@ namespace QRBee.Core.Data
|
|||||||
/// Convert MerchantToClientRequest to string to be used as QR Code source (along with merchant signature)
|
/// Convert MerchantToClientRequest to string to be used as QR Code source (along with merchant signature)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>String conversion</returns>
|
/// <returns>String conversion</returns>
|
||||||
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}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert from string
|
/// Convert from string
|
||||||
@ -55,9 +57,9 @@ namespace QRBee.Core.Data
|
|||||||
public static MerchantToClientRequest FromString(string input)
|
public static MerchantToClientRequest FromString(string input)
|
||||||
{
|
{
|
||||||
var s = input.Split('|');
|
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
|
var res = new MerchantToClientRequest
|
||||||
@ -66,7 +68,8 @@ namespace QRBee.Core.Data
|
|||||||
MerchantTransactionId = s[1],
|
MerchantTransactionId = s[1],
|
||||||
Name = s[2],
|
Name = s[2],
|
||||||
Amount = Convert.ToDecimal(s[3], CultureInfo.InvariantCulture),
|
Amount = Convert.ToDecimal(s[3], CultureInfo.InvariantCulture),
|
||||||
TimeStampUTC = DateTime.ParseExact(s[4],"O",null)
|
TimeStampUTC = DateTime.ParseExact(s[4],"O",null),
|
||||||
|
MerchantSignature = s[5]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -17,14 +17,14 @@ namespace QRBee.Core.Security
|
|||||||
/// Generate new private key and store it
|
/// Generate new private key and store it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="subjectName"></param>
|
/// <param name="subjectName"></param>
|
||||||
/// <returns>Certificate request to be sent to CA</returns>
|
/// <returns>Certificate request to be sent to CA in PEM format</returns>
|
||||||
byte [] GeneratePrivateKey(string? subjectName = null);
|
string 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></returns>
|
/// <returns>Certificate request to be sent to CA in PEM format</returns>
|
||||||
byte[] CreateCertificateRequest();
|
string CreateCertificateRequest();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attach CA-generated public key certificate to the private key
|
/// Attach CA-generated public key certificate to the private key
|
||||||
|
|||||||
39
QRBee.Core/Security/PrivateKeyHandlerBase.cs
Normal file
39
QRBee.Core/Security/PrivateKeyHandlerBase.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace QRBee.Core.Security
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Private key handler for API server
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,11 +6,15 @@ using Android.Runtime;
|
|||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Support.V4.Content;
|
using Android.Support.V4.Content;
|
||||||
using AndroidX.Core.App;
|
using AndroidX.Core.App;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Plugin.Fingerprint;
|
using Plugin.Fingerprint;
|
||||||
|
using QRBee.Core.Security;
|
||||||
|
using QRBee.Droid.Services;
|
||||||
|
using QRBee.Services;
|
||||||
|
|
||||||
namespace QRBee.Droid
|
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
|
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||||
{
|
{
|
||||||
protected override void OnCreate(Bundle savedInstanceState)
|
protected override void OnCreate(Bundle savedInstanceState)
|
||||||
@ -23,7 +27,7 @@ namespace QRBee.Droid
|
|||||||
CrossFingerprint.SetCurrentActivityResolver(()=>Xamarin.Essentials.Platform.CurrentActivity);
|
CrossFingerprint.SetCurrentActivityResolver(()=>Xamarin.Essentials.Platform.CurrentActivity);
|
||||||
|
|
||||||
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
|
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
|
||||||
LoadApplication(new App());
|
LoadApplication(new App(AddServices));
|
||||||
ZXing.Mobile.MobileBarcodeScanner.Initialize(Application);
|
ZXing.Mobile.MobileBarcodeScanner.Initialize(Application);
|
||||||
|
|
||||||
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == (int) Permission.Granted)
|
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)
|
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
|
||||||
{
|
{
|
||||||
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services
|
||||||
|
.AddSingleton<ISecurityService,AndroidSecurityService>()
|
||||||
|
.AddSingleton<ILocalSettings, LocalSettings>()
|
||||||
|
.AddSingleton<IQRScanner, QRScannerService>()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,6 +59,9 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
|
||||||
|
<Version>6.0.0</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Plugin.Fingerprint">
|
<PackageReference Include="Plugin.Fingerprint">
|
||||||
<Version>2.1.4</Version>
|
<Version>2.1.4</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@ -80,7 +83,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Services\LocalSettings.cs" />
|
<Compile Include="Services\LocalSettings.cs" />
|
||||||
<Compile Include="Services\QRScannerService.cs" />
|
<Compile Include="Services\QRScannerService.cs" />
|
||||||
<Compile Include="Services\SecurityService.cs" />
|
<Compile Include="Services\AndroidSecurityService.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Resources\AboutResources.txt" />
|
<None Include="Resources\AboutResources.txt" />
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="launcher_background">#FFFFFF</color>
|
<color name="launcher_background">#FFFFFF</color>
|
||||||
<color name="colorPrimary">#3F51B5</color>
|
<color name="colorPrimary">#FFC928</color>
|
||||||
<color name="colorPrimaryDark">#303F9F</color>
|
<color name="colorPrimaryDark">#FFC928</color>
|
||||||
<color name="colorAccent">#FF4081</color>
|
<color name="colorAccent">#FFC928</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
56
QRBee/QRBee.Android/Services/AndroidSecurityService.cs
Normal file
56
QRBee/QRBee.Android/Services/AndroidSecurityService.cs
Normal file
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
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-----";
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey)
|
|
||||||
{
|
|
||||||
throw new ApplicationException("Client never issues certificates");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override X509Certificate2 Deserialize(string pemData)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//return X509Certificate2.CreateFromPem(pemData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -7,7 +7,8 @@
|
|||||||
-->
|
-->
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<Color x:Key="Primary">#2196F3</Color>
|
<!--2296F3 -->
|
||||||
|
<Color x:Key="Primary">#FFC928</Color>
|
||||||
<Style TargetType="Button">
|
<Style TargetType="Button">
|
||||||
<Setter Property="TextColor" Value="White"></Setter>
|
<Setter Property="TextColor" Value="White"></Setter>
|
||||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||||
|
|||||||
@ -1,21 +1,52 @@
|
|||||||
using QRBee.Services;
|
using QRBee.Services;
|
||||||
using QRBee.Views;
|
using QRBee.Views;
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using QRBee.Core.Security;
|
||||||
|
using QRBee.ViewModels;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Xaml;
|
|
||||||
|
|
||||||
namespace QRBee
|
namespace QRBee
|
||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
|
|
||||||
public App()
|
public App(Action<IServiceCollection> addPlatformServices = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
SetupServices(addPlatformServices);
|
||||||
|
|
||||||
DependencyService.Register<MockDataStore>();
|
DependencyService.Register<MockDataStore>();
|
||||||
MainPage = new AppShell();
|
MainPage = new AppShell();
|
||||||
}
|
}
|
||||||
|
protected static IServiceProvider ServiceProvider { get; set; }
|
||||||
|
|
||||||
|
void SetupServices(Action<IServiceCollection> addPlatformServices = null)
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
// Add platform specific services
|
||||||
|
addPlatformServices?.Invoke(services);
|
||||||
|
|
||||||
|
// TODO: Add core services here
|
||||||
|
services
|
||||||
|
.AddSingleton<IPrivateKeyHandler, ClientPrivateKeyHandler>()
|
||||||
|
;
|
||||||
|
|
||||||
|
// Add ViewModels
|
||||||
|
services
|
||||||
|
.AddTransient<MerchantPageViewModel>()
|
||||||
|
.AddTransient<ClientPageViewModel>()
|
||||||
|
.AddTransient<RegisterViewModel>()
|
||||||
|
.AddTransient<LoginViewModel>()
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
ServiceProvider = services.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BaseViewModel GetViewModel<TViewModel>() where TViewModel : BaseViewModel => ServiceProvider.GetService<TViewModel>();
|
||||||
|
|
||||||
protected override void OnStart()
|
protected override void OnStart()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -30,8 +30,6 @@
|
|||||||
</Shell.Resources>
|
</Shell.Resources>
|
||||||
|
|
||||||
<TabBar>
|
<TabBar>
|
||||||
<!-- <ShellContent Title="About" Icon="icon_about.png" Route="AboutPage" ContentTemplate="{DataTemplate local:AboutPage}" /> -->
|
|
||||||
<!-- <ShellContent Title="Browse" Icon="icon_feed.png" ContentTemplate="{DataTemplate local:ItemsPage}" /> -->
|
|
||||||
<ShellContent Title="LoginPage" ContentTemplate="{DataTemplate local:LoginPage}"/>
|
<ShellContent Title="LoginPage" ContentTemplate="{DataTemplate local:LoginPage}"/>
|
||||||
</TabBar>
|
</TabBar>
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2244" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2244" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||||
|
|||||||
23
QRBee/QRBee/Services/ClientPrivateKeyHandler.cs
Normal file
23
QRBee/QRBee/Services/ClientPrivateKeyHandler.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using QRBee.Core.Security;
|
||||||
|
|
||||||
|
namespace QRBee.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Private key handler for Client side
|
||||||
|
/// </summary>
|
||||||
|
public class ClientPrivateKeyHandler : PrivateKeyHandlerBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private const string VeryBadAndInsecureCertificatePassword = "Rî‹T=›'ÄζgÚrʯю™pudF";
|
||||||
|
|
||||||
|
public ClientPrivateKeyHandler(ILocalSettings settings)
|
||||||
|
{
|
||||||
|
CertificatePassword = VeryBadAndInsecureCertificatePassword;
|
||||||
|
|
||||||
|
var clientSettings = settings.LoadSettings();
|
||||||
|
CommonName = clientSettings?.ClientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
using QRBee.Core.Data;
|
using QRBee.Core.Data;
|
||||||
|
using QRBee.Core.Security;
|
||||||
using QRBee.Services;
|
using QRBee.Services;
|
||||||
using QRBee.Views;
|
using QRBee.Views;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -8,17 +10,26 @@ namespace QRBee.ViewModels
|
|||||||
{
|
{
|
||||||
internal class ClientPageViewModel: BaseViewModel
|
internal class ClientPageViewModel: BaseViewModel
|
||||||
{
|
{
|
||||||
public bool _isVisible;
|
private readonly IQRScanner _scanner;
|
||||||
|
private readonly ISecurityService _securityService;
|
||||||
|
public bool _isAcceptDenyButtonVisible;
|
||||||
|
public bool _isQrVisible;
|
||||||
|
public bool _isScanButtonVisible;
|
||||||
|
|
||||||
|
|
||||||
public string _amount;
|
public string _amount;
|
||||||
private string _qrCode;
|
private string _qrCode;
|
||||||
private MerchantToClientRequest _merchantToClientRequest;
|
private MerchantToClientRequest _merchantToClientRequest;
|
||||||
private readonly ClientPage _clientPage;
|
|
||||||
|
|
||||||
public ClientPageViewModel(Views.ClientPage clientPage)
|
public ClientPageViewModel(IQRScanner scanner, ISecurityService securityService)
|
||||||
{
|
{
|
||||||
|
_scanner = scanner;
|
||||||
|
_securityService = securityService;
|
||||||
ScanCommand = new Command(OnScanButtonClicked);
|
ScanCommand = new Command(OnScanButtonClicked);
|
||||||
GenerateQrCommand = new Command(OnGenerateQrClicked);
|
AcceptQrCommand = new Command(OnAcceptQrCommand);
|
||||||
_clientPage = clientPage;
|
DenyQrCommand = new Command(OnDenyQrCommand);
|
||||||
|
|
||||||
|
IsScanButtonVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command ScanCommand
|
public Command ScanCommand
|
||||||
@ -26,23 +37,33 @@ namespace QRBee.ViewModels
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command GenerateQrCommand
|
public Command AcceptQrCommand
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Command DenyQrCommand
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private async void OnScanButtonClicked(object sender)
|
private async void OnScanButtonClicked(object sender)
|
||||||
{
|
{
|
||||||
|
QrCode = null;
|
||||||
|
IsQrVisible = false;
|
||||||
|
IsAcceptDenyButtonVisible = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var scanner = DependencyService.Get<IQRScanner>();
|
var result = await _scanner.ScanQR();
|
||||||
var result = await scanner.ScanQR();
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_merchantToClientRequest = MerchantToClientRequest.FromString(result);
|
_merchantToClientRequest = MerchantToClientRequest.FromString(result);
|
||||||
Amount = $"{_merchantToClientRequest.Amount:N2}";
|
Amount = $"{_merchantToClientRequest.Amount:N2}";
|
||||||
IsVisible = true;
|
IsAcceptDenyButtonVisible = true;
|
||||||
|
IsScanButtonVisible = false;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@ -64,17 +85,45 @@ namespace QRBee.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible
|
public bool IsAcceptDenyButtonVisible
|
||||||
{
|
{
|
||||||
get => _isVisible;
|
get => _isAcceptDenyButtonVisible;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == _isVisible)
|
if (value == _isAcceptDenyButtonVisible)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_isVisible = value;
|
_isAcceptDenyButtonVisible = value;
|
||||||
OnPropertyChanged(nameof(IsVisible));
|
OnPropertyChanged(nameof(IsAcceptDenyButtonVisible));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQrVisible
|
||||||
|
{
|
||||||
|
get => _isQrVisible;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _isQrVisible)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isQrVisible = value;
|
||||||
|
OnPropertyChanged(nameof(IsQrVisible));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsScanButtonVisible
|
||||||
|
{
|
||||||
|
get => _isScanButtonVisible;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _isScanButtonVisible)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isScanButtonVisible = value;
|
||||||
|
OnPropertyChanged(nameof(IsScanButtonVisible));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +132,6 @@ namespace QRBee.ViewModels
|
|||||||
get => _qrCode;
|
get => _qrCode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// _qrCode = $"{Amount}/{Name}";
|
|
||||||
if (_qrCode == value)
|
if (_qrCode == value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -96,12 +144,11 @@ namespace QRBee.ViewModels
|
|||||||
/// Reaction on GenerateQR button clicked
|
/// Reaction on GenerateQR button clicked
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj"></param>
|
||||||
public async void OnGenerateQrClicked(object obj)
|
public async void OnAcceptQrCommand(object obj)
|
||||||
{
|
{
|
||||||
|
|
||||||
var answer = await _clientPage.DisplayAlert("Confirmation", "Would you like to accept the offer?", "Yes", "No");
|
var answer = await Application.Current.MainPage.DisplayAlert("Confirmation", "Would you like to accept the offer?", "Yes", "No");
|
||||||
if (!answer)
|
if (!answer) return;
|
||||||
return;
|
|
||||||
|
|
||||||
var response = new ClientToMerchantResponse
|
var response = new ClientToMerchantResponse
|
||||||
{
|
{
|
||||||
@ -111,10 +158,23 @@ namespace QRBee.ViewModels
|
|||||||
MerchantRequest = _merchantToClientRequest
|
MerchantRequest = _merchantToClientRequest
|
||||||
|
|
||||||
};
|
};
|
||||||
// TODO Create merchant signature.
|
// TODO Create client signature.
|
||||||
|
var clientSignature = _securityService.Sign(Encoding.UTF8.GetBytes(response.AsDataForSignature()));
|
||||||
|
response.ClientSignature = Convert.ToBase64String(clientSignature);
|
||||||
|
|
||||||
QrCode = response.AsQRCodeString();
|
QrCode = response.AsQRCodeString();
|
||||||
|
IsQrVisible = true;
|
||||||
|
IsAcceptDenyButtonVisible = false;
|
||||||
|
IsScanButtonVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDenyQrCommand(object obj)
|
||||||
|
{
|
||||||
|
QrCode = null;
|
||||||
|
IsQrVisible = false;
|
||||||
|
IsAcceptDenyButtonVisible = false;
|
||||||
|
IsScanButtonVisible = true;
|
||||||
|
Amount = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
using QRBee.Core.Data;
|
using QRBee.Core.Data;
|
||||||
|
using QRBee.Core.Security;
|
||||||
using QRBee.Services;
|
using QRBee.Services;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@ -9,6 +11,9 @@ namespace QRBee.ViewModels
|
|||||||
{
|
{
|
||||||
internal class MerchantPageViewModel : BaseViewModel
|
internal class MerchantPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
|
private readonly IQRScanner _scanner;
|
||||||
|
private readonly ILocalSettings _settings;
|
||||||
|
private readonly ISecurityService _securityService;
|
||||||
private bool _isVisible;
|
private bool _isVisible;
|
||||||
private decimal _amount;
|
private decimal _amount;
|
||||||
private string _qrCode;
|
private string _qrCode;
|
||||||
@ -16,8 +21,11 @@ namespace QRBee.ViewModels
|
|||||||
public Command GenerateQrCommand { get; }
|
public Command GenerateQrCommand { get; }
|
||||||
public Command ScanCommand{ get; }
|
public Command ScanCommand{ get; }
|
||||||
|
|
||||||
public MerchantPageViewModel()
|
public MerchantPageViewModel(IQRScanner scanner, ILocalSettings settings, ISecurityService securityService)
|
||||||
{
|
{
|
||||||
|
_scanner = scanner;
|
||||||
|
_settings = settings;
|
||||||
|
_securityService = securityService;
|
||||||
ScanCommand = new Command(OnScanButtonClicked);
|
ScanCommand = new Command(OnScanButtonClicked);
|
||||||
GenerateQrCommand = new Command(OnGenerateQrClicked);
|
GenerateQrCommand = new Command(OnGenerateQrClicked);
|
||||||
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
||||||
@ -28,15 +36,13 @@ namespace QRBee.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var scanner = DependencyService.Get<IQRScanner>();
|
var result = await _scanner.ScanQR();
|
||||||
var result = await scanner.ScanQR();
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var client = new HttpClient(GetInsecureHandler());
|
var client = new HttpClient(GetInsecureHandler());
|
||||||
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
|
||||||
|
|
||||||
var service = new Core.Client.Client(localSettings.QRBeeApiUrl, client);
|
var service = new Core.Client.Client(_settings.QRBeeApiUrl, client);
|
||||||
var paymentRequest = PaymentRequest.FromString(result);
|
var paymentRequest = PaymentRequest.FromString(result);
|
||||||
|
|
||||||
//QrCode = null;
|
//QrCode = null;
|
||||||
@ -132,7 +138,10 @@ namespace QRBee.ViewModels
|
|||||||
Amount = Amount,
|
Amount = Amount,
|
||||||
TimeStampUTC = DateTime.UtcNow
|
TimeStampUTC = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
// TODO Create merchant signature.
|
|
||||||
|
var merchantSignature = _securityService.Sign(Encoding.UTF8.GetBytes(trans.AsDataForSignature()));
|
||||||
|
trans.MerchantSignature = Convert.ToBase64String(merchantSignature);
|
||||||
|
|
||||||
QrCode = trans.AsQRCodeString();
|
QrCode = trans.AsQRCodeString();
|
||||||
IsVisible = true;
|
IsVisible = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,14 @@ namespace QRBee.ViewModels
|
|||||||
{
|
{
|
||||||
internal class RegisterViewModel: BaseViewModel
|
internal class RegisterViewModel: BaseViewModel
|
||||||
{
|
{
|
||||||
|
private readonly ILocalSettings _settings;
|
||||||
private string _password1;
|
private string _password1;
|
||||||
private string _password2;
|
private string _password2;
|
||||||
public RegisterViewModel()
|
public RegisterViewModel(ILocalSettings localSettings)
|
||||||
{
|
{
|
||||||
|
_settings = localSettings;
|
||||||
RegisterCommand = new Command(OnRegisterClicked);
|
RegisterCommand = new Command(OnRegisterClicked);
|
||||||
|
|
||||||
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
|
||||||
var settings = localSettings.LoadSettings();
|
var settings = localSettings.LoadSettings();
|
||||||
|
|
||||||
Name = settings.Name;
|
Name = settings.Name;
|
||||||
@ -96,14 +97,13 @@ namespace QRBee.ViewModels
|
|||||||
{
|
{
|
||||||
//TODO when to dispose the client?
|
//TODO when to dispose the client?
|
||||||
var client = new HttpClient(GetInsecureHandler());
|
var client = new HttpClient(GetInsecureHandler());
|
||||||
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
|
||||||
|
|
||||||
var service = new Core.Client.Client(localSettings.QRBeeApiUrl,client);
|
var service = new Core.Client.Client(_settings.QRBeeApiUrl,client);
|
||||||
|
|
||||||
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 = localSettings.LoadSettings();
|
var settings = _settings.LoadSettings();
|
||||||
|
|
||||||
//save local settings
|
//save local settings
|
||||||
settings.CardHolderName = CardHolderName;
|
settings.CardHolderName = CardHolderName;
|
||||||
@ -117,7 +117,7 @@ namespace QRBee.ViewModels
|
|||||||
settings.Name = Name;
|
settings.Name = Name;
|
||||||
settings.PIN = Pin;
|
settings.PIN = Pin;
|
||||||
|
|
||||||
await localSettings.SaveSettings(settings);
|
await _settings.SaveSettings(settings);
|
||||||
|
|
||||||
var request = new RegistrationRequest
|
var request = new RegistrationRequest
|
||||||
{
|
{
|
||||||
@ -132,9 +132,9 @@ namespace QRBee.ViewModels
|
|||||||
var response = await service.RegisterAsync(request);
|
var response = await service.RegisterAsync(request);
|
||||||
|
|
||||||
// Save ClientId to LocalSettings
|
// Save ClientId to LocalSettings
|
||||||
settings = localSettings.LoadSettings();
|
settings = _settings.LoadSettings();
|
||||||
settings.ClientId = response.ClientId;
|
settings.ClientId = response.ClientId;
|
||||||
await localSettings.SaveSettings(settings);
|
await _settings.SaveSettings(settings);
|
||||||
|
|
||||||
var page = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
|
var page = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
|
||||||
await page.DisplayAlert("Success", "You have been registered successfully", "Ok");
|
await page.DisplayAlert("Success", "You have been registered successfully", "Ok");
|
||||||
|
|||||||
@ -6,11 +6,9 @@
|
|||||||
xmlns:common="clr-namespace:ZXing.Common;assembly=zxing.portable"
|
xmlns:common="clr-namespace:ZXing.Common;assembly=zxing.portable"
|
||||||
x:DataType="viewmodels:ClientPageViewModel"
|
x:DataType="viewmodels:ClientPageViewModel"
|
||||||
x:Class="QRBee.Views.ClientPage">
|
x:Class="QRBee.Views.ClientPage">
|
||||||
|
|
||||||
<ContentPage.Content>
|
<ContentPage.Content>
|
||||||
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
|
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
|
||||||
<!-- <Label Text="Hello this is a test page." -->
|
|
||||||
<!-- VerticalOptions="CenterAndExpand" -->
|
|
||||||
<!-- HorizontalOptions="CenterAndExpand" /> -->
|
|
||||||
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
|
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
|
||||||
<StackLayout Orientation="Vertical">
|
<StackLayout Orientation="Vertical">
|
||||||
<Label VerticalOptions="FillAndExpand" Text="Amount:"/>
|
<Label VerticalOptions="FillAndExpand" Text="Amount:"/>
|
||||||
@ -21,7 +19,8 @@
|
|||||||
BarcodeFormat="QR_CODE"
|
BarcodeFormat="QR_CODE"
|
||||||
BarcodeValue="{Binding QrCode}"
|
BarcodeValue="{Binding QrCode}"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
VerticalOptions="FillAndExpand">
|
VerticalOptions="FillAndExpand"
|
||||||
|
IsVisible="{Binding IsQrVisible}">
|
||||||
<forms:ZXingBarcodeImageView.BarcodeOptions>
|
<forms:ZXingBarcodeImageView.BarcodeOptions>
|
||||||
<common:EncodingOptions Width="300" Height="300" />
|
<common:EncodingOptions Width="300" Height="300" />
|
||||||
</forms:ZXingBarcodeImageView.BarcodeOptions>
|
</forms:ZXingBarcodeImageView.BarcodeOptions>
|
||||||
@ -31,10 +30,10 @@
|
|||||||
|
|
||||||
<StackLayout Orientation="Vertical" VerticalOptions="End" Margin="0,0,0,10">
|
<StackLayout Orientation="Vertical" VerticalOptions="End" Margin="0,0,0,10">
|
||||||
<StackLayout Orientation="Horizontal">
|
<StackLayout Orientation="Horizontal">
|
||||||
<Button Text="Accept" HorizontalOptions="FillAndExpand" BackgroundColor="DarkGreen" IsVisible="{Binding IsVisible}" Command="{Binding GenerateQrCommand}"/>
|
<Button Text="Accept" HorizontalOptions="FillAndExpand" BackgroundColor="DarkGreen" IsVisible="{Binding IsAcceptDenyButtonVisible}" Command="{Binding AcceptQrCommand}"/>
|
||||||
<Button Text="Deny" HorizontalOptions="FillAndExpand" BackgroundColor="DarkRed" IsVisible="{Binding IsVisible}"/>
|
<Button Text="Deny" HorizontalOptions="FillAndExpand" BackgroundColor="DarkRed" IsVisible="{Binding IsAcceptDenyButtonVisible}" Command="{Binding DenyQrCommand}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Button Text="Scan me" HorizontalOptions="FillAndExpand" BackgroundColor="Aqua" TextColor="Red" Command="{Binding ScanCommand}"/>
|
<Button Text="Scan merchant QR Code" HorizontalOptions="FillAndExpand" BackgroundColor="Aqua" TextColor="Red" Command="{Binding ScanCommand}" IsVisible="{Binding IsScanButtonVisible}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|||||||
@ -11,8 +11,8 @@ namespace QRBee.Views
|
|||||||
{
|
{
|
||||||
public ClientPage()
|
public ClientPage()
|
||||||
{
|
{
|
||||||
|
BindingContext = App.GetViewModel<ClientPageViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
this.BindingContext = new ClientPageViewModel(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,6 +8,4 @@
|
|||||||
<views:ClientPage Title="Client"/>
|
<views:ClientPage Title="Client"/>
|
||||||
<views:MerchantPage Title="Merchant"/>
|
<views:MerchantPage Title="Merchant"/>
|
||||||
</TabbedPage.Children>
|
</TabbedPage.Children>
|
||||||
|
|
||||||
<!-- <ContentPage Title="Merchant" /> -->
|
|
||||||
</TabbedPage>
|
</TabbedPage>
|
||||||
@ -8,7 +8,7 @@ namespace QRBee.Views
|
|||||||
{
|
{
|
||||||
public MerchantPage()
|
public MerchantPage()
|
||||||
{
|
{
|
||||||
BindingContext = new MerchantPageViewModel();
|
BindingContext = App.GetViewModel<MerchantPageViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace QRBee.Views
|
|||||||
public RegisterPage()
|
public RegisterPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
this.BindingContext = new RegisterViewModel();
|
BindingContext = App.GetViewModel<RegisterViewModel>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
using System.Security.Cryptography;
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using QRBee.Core.Security;
|
using QRBee.Core.Security;
|
||||||
|
|
||||||
@ -16,46 +18,70 @@ namespace QRBee.Api.Services
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey)
|
public override X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey)
|
||||||
{
|
{
|
||||||
if (!IsValidSubjectName(subjectName))
|
throw new ApplicationException("Client never issues certificates");
|
||||||
throw new CryptographicException("Invalid subject name");
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/60930065/generate-and-sign-certificate-in-different-machines-c-sharp
|
|
||||||
|
|
||||||
using var publicKey = RSA.Create();
|
|
||||||
|
|
||||||
publicKey.ImportSubjectPublicKeyInfo(rsaPublicKey, out var nBytes);
|
|
||||||
//TODO: check that nBytes is within allowed range
|
|
||||||
|
|
||||||
var request = new CertificateRequest("CN=" + subjectName, publicKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
|
||||||
|
|
||||||
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
|
|
||||||
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.NonRepudiation, false));
|
|
||||||
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
|
|
||||||
|
|
||||||
// create a new certificate
|
|
||||||
using var caPrivateKey = PrivateKeyHandler.LoadPrivateKey();
|
|
||||||
var certificate = request.Create(
|
|
||||||
caPrivateKey,
|
|
||||||
DateTimeOffset.UtcNow.AddSeconds(-1), // user can use it now
|
|
||||||
DateTimeOffset.UtcNow.AddDays(30), // user need to login every 30 days
|
|
||||||
Guid.NewGuid().ToByteArray());
|
|
||||||
|
|
||||||
return certificate;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate Client certificate request (i.e. without KeyCertSign usage extension)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="distinguishedName"></param>
|
||||||
|
/// <param name="rsa"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static CertificateRequest CreateRequest(X500DistinguishedName distinguishedName, RSA rsa)
|
||||||
|
{
|
||||||
|
var request = new CertificateRequest(
|
||||||
|
distinguishedName,
|
||||||
|
rsa,
|
||||||
|
HashAlgorithmName.SHA256,
|
||||||
|
RSASignaturePadding.Pkcs1
|
||||||
|
);
|
||||||
|
|
||||||
|
request.CertificateExtensions.Add(
|
||||||
|
new X509KeyUsageExtension(
|
||||||
|
X509KeyUsageFlags.DataEncipherment
|
||||||
|
| X509KeyUsageFlags.KeyEncipherment
|
||||||
|
| X509KeyUsageFlags.DigitalSignature,
|
||||||
|
//| X509KeyUsageFlags.KeyCertSign,
|
||||||
|
false));
|
||||||
|
|
||||||
|
|
||||||
|
// request.CertificateExtensions.Add(
|
||||||
|
// new X509EnhancedKeyUsageExtension(
|
||||||
|
// new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string CertHeader = "-----BEGIN CERTIFICATE-----";
|
||||||
|
private const string CertFooter = "-----END CERTIFICATE-----";
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override X509Certificate2 Deserialize(string pemData)
|
public override X509Certificate2 Deserialize(string pemData)
|
||||||
{
|
{
|
||||||
return X509Certificate2.CreateFromPem(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string Serialize(X509Certificate2 cert)
|
public override string Serialize(X509Certificate2 cert)
|
||||||
{
|
{
|
||||||
// https://stackoverflow.com/questions/43928064/export-private-public-keys-from-x509-certificate-to-pem
|
// https://stackoverflow.com/questions/43928064/export-private-public-keys-from-x509-certificate-to-pem
|
||||||
var pem = PemEncoding.Write("CERTIFICATE", cert.RawData);
|
var builder = new StringBuilder();
|
||||||
return new string(pem);
|
builder.AppendLine(CertHeader);
|
||||||
|
builder.AppendLine(Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks));
|
||||||
|
builder.AppendLine(CertFooter);
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
QRBeeApi/Services/ServerPrivateKeyHandler.cs
Normal file
21
QRBeeApi/Services/ServerPrivateKeyHandler.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using QRBee.Core.Security;
|
||||||
|
|
||||||
|
namespace QRBee.Api.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Private key handler for API server
|
||||||
|
/// </summary>
|
||||||
|
public class ServerPrivateKeyHandler : PrivateKeyHandlerBase
|
||||||
|
{
|
||||||
|
private const string CACommonName = "QRBee-CA";
|
||||||
|
|
||||||
|
private const string VeryBadAndInsecureCertificatePassword = "U…Š)+œ¶€=ø‘ c¬Í↨ð´áY/ÿ☼æX";
|
||||||
|
|
||||||
|
public ServerPrivateKeyHandler()
|
||||||
|
{
|
||||||
|
CertificatePassword = VeryBadAndInsecureCertificatePassword;
|
||||||
|
CommonName = CACommonName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user