Skip to content

Commit

Permalink
ACME: implement optional pre-generated account key for account pre-ap…
Browse files Browse the repository at this point in the history
…proval
  • Loading branch information
webprofusion-chrisc committed Oct 16, 2024
1 parent 81e8a6c commit ae70e64
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 27 deletions.
32 changes: 22 additions & 10 deletions src/Certify.Providers/ACME/Anvil/AnvilACMEProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
Expand Down Expand Up @@ -501,11 +501,7 @@ public async Task<ActionResult<AccountDetails>> AddNewAccountAndAcceptTOS(ILog l

try
{
if (
(!string.IsNullOrEmpty(importAccountURI) && string.IsNullOrEmpty(importAccountKey))
||
(string.IsNullOrEmpty(importAccountURI) && !string.IsNullOrEmpty(importAccountKey))
)
if (!string.IsNullOrEmpty(importAccountURI) && string.IsNullOrEmpty(importAccountKey))
{
return new ActionResult<AccountDetails>("To import account details both the existing account URI and account key in PEM format are required. ", false);
}
Expand Down Expand Up @@ -545,13 +541,29 @@ public async Task<ActionResult<AccountDetails>> AddNewAccountAndAcceptTOS(ILog l
{
IKey accKey = null;

if (_newContactUseCurrentAccountKey && !string.IsNullOrEmpty(_settings.AccountKey))
if (!string.IsNullOrEmpty(importAccountKey))
{
accKey = KeyFactory.FromPem(_settings.AccountKey);
try
{
// using a custom or pre-generated account key
SetAcmeContextAccountKey(importAccountKey);
}
catch
{
return new ActionResult<AccountDetails>("The provided account key was invalid or not supported. A PEM (text) format RSA or ECDA private key is required.", false);
}
}
else
{
// use current account key if enabled or new one will be generated
if (_newContactUseCurrentAccountKey && !string.IsNullOrEmpty(_settings.AccountKey))
{
accKey = KeyFactory.FromPem(_settings.AccountKey);
}

// start new account context, create new account (with new key, if not enabled)
_acme = new AcmeContext(_serviceUri, accKey, _httpClient, accountUri: _settings.AccountUri != null ? new Uri(_settings.AccountUri) : null);
// start new account context, create new account (with new key, if not enabled)
_acme = new AcmeContext(_serviceUri, accKey, _httpClient, accountUri: _settings.AccountUri != null ? new Uri(_settings.AccountUri) : null);
}

try
{
Expand Down
48 changes: 31 additions & 17 deletions src/Certify.UI.Shared/Windows/EditAccountDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:res="clr-namespace:Certify.Locales;assembly=Certify.Locales"
Title="{x:Static res:SR.Account_Edit_SectionTitle}"
Width="514"
Height="416"
Width="592"
Height="466"
ResizeMode="CanResizeWithGrip"
TitleCharacterCasing="Normal"
WindowStartupLocation="CenterOwner"
Expand All @@ -22,7 +22,6 @@

<TabControl
x:Name="AccountTab"
Height="auto"
Margin="0,0,0,0"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Stretch"
Expand All @@ -31,14 +30,12 @@
DockPanel.Dock="Top"
TabStripPlacement="Top">
<TabItem
Height="32"
MinWidth="140"
Controls:HeaderedControlHelper.HeaderFontSize="12"
Header="Account Settings"
IsSelected="true">
<DockPanel Margin="8,0,0,0" LastChildFill="True">
<DockPanel Margin="8,0,0,0">
<TextBlock
Width="472"
HorizontalAlignment="Left"
VerticalAlignment="Top"
DockPanel.Dock="Top"
Expand Down Expand Up @@ -79,7 +76,6 @@
TextWrapping="Wrap" />
</StackPanel>
<TextBlock
Width="490"
Margin="0,0,8,0"
DockPanel.Dock="Top"
Style="{StaticResource Instructions}"
Expand Down Expand Up @@ -112,11 +108,10 @@
</TabItem>

<TabItem
Height="32"
MinWidth="140"
Controls:HeaderedControlHelper.HeaderFontSize="12"
Header="Advanced">
<ScrollViewer MaxHeight="300">
<ScrollViewer MaxHeight="350">
<DockPanel Margin="8,0,8,0">
<TextBlock DockPanel.Dock="Top" Style="{StaticResource Subheading}">Mode &amp; Preferences</TextBlock>
<StackPanel DockPanel.Dock="Top">
Expand Down Expand Up @@ -203,8 +198,8 @@
Text="{Binding Item.EabKeyAlgorithm}"
TextWrapping="Wrap" />
</StackPanel>
<TextBlock DockPanel.Dock="Top" Style="{StaticResource SubheadingWithMargin}">Import Account Details</TextBlock>
<TextBlock DockPanel.Dock="Top" Style="{StaticResource Instructions}">If you need to import an account from another client instead of registering a new account with the CA you can specify the Account URI and Account Key (PEM encoded private key):</TextBlock>
<TextBlock DockPanel.Dock="Top" Style="{StaticResource SubheadingWithMargin}">Import or Custom Account Details</TextBlock>
<TextBlock DockPanel.Dock="Top" Style="{StaticResource Instructions}">If you need to import an account from another client instead of registering a new account with the CA, or you need to customize or pre-generate the account key, you can optionally specify the Account URI and/or Account Key (PEM encoded private key):</TextBlock>

<StackPanel
Margin="0,0,0,4"
Expand Down Expand Up @@ -242,6 +237,22 @@
AcceptsReturn="True"
Text="{Binding Item.ImportedAccountKey}"
TextWrapping="Wrap" />


</StackPanel>
<StackPanel
x:Name="AccountKeyGen"
Margin="0,0,0,4"
DockPanel.Dock="Top"
Orientation="Vertical">

<TextBlock Style="{StaticResource Instructions}">For account pre-approval and other advanced scenarios you can optionally pre-generate a custom keypair for your ACME account:</TextBlock>
<Button
x:Name="AccountKeyGenerate"
Margin="12,0,0,0"
HorizontalAlignment="Left"
Click="AccountKeyGenerate_Click"
Content="Generate" />
</StackPanel>
<StackPanel
x:Name="AccountRollover"
Expand All @@ -263,18 +274,21 @@
</TabControl>


<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<DockPanel Margin="0,8,0,0" DockPanel.Dock="Bottom">
<Button
x:Name="Save"
Margin="12,0,0,0"
Margin="0,0,12,0"
Click="Save_Click"
Content="{x:Static res:SR.Register_Contact}" />
Content="{x:Static res:SR.Register_Contact}"
DockPanel.Dock="Right" />
<Button
x:Name="Cancel"
Width="100"
Margin="280,0,0,0"
Margin="12,0,0,0"
HorizontalAlignment="Left"
Click="Cancel_Click"
Content="{x:Static res:SR.Cancel}" />
</StackPanel>
Content="{x:Static res:SR.Cancel}"
DockPanel.Dock="Left" />
</DockPanel>
</DockPanel>
</Controls:MetroWindow>
32 changes: 32 additions & 0 deletions src/Certify.UI.Shared/Windows/EditAccountDialog.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Certify.Models;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Crypto.EC;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO.Pem;

namespace Certify.UI.Windows
{
Expand Down Expand Up @@ -162,5 +170,29 @@ private async void AccountKeyChange_Click(object sender, RoutedEventArgs e)
MainViewModel.ShowNotification(result.Message, Shared.NotificationType.Error);
}
}

private void AccountKeyGenerate_Click(object sender, RoutedEventArgs e)
{
// generate a new EC key
var generator = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
var generatorParams = new ECKeyGenerationParameters(CustomNamedCurves.GetOid("P-256"), new SecureRandom());
generator.Init(generatorParams);
var keyPair = generator.GenerateKeyPair();
var keyData = Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private).ToAsn1Object().GetEncoded();

// convert to PEM`
var pem = "";
using (var sr = new StringWriter())
{
var pemObj = new PemObject("PRIVATE KEY", keyData);
var pemWriter = new PemWriter(sr);
pemWriter.WriteObject(pemObj);
pem = sr.ToString();
}

Item.ImportedAccountKey = pem;

BindingOperations.GetBindingExpressionBase((TextBox)AccountKey, TextBox.TextProperty).UpdateTarget();
}
}
}

0 comments on commit ae70e64

Please sign in to comment.