Skip to content

Commit

Permalink
Enable user protected keys in certificate store (#757)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcurland authored Sep 18, 2024
1 parent 24dd31a commit ea23724
Show file tree
Hide file tree
Showing 20 changed files with 111 additions and 20 deletions.
6 changes: 5 additions & 1 deletion src/Sign.Cli/CertificateStoreCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal sealed class CertificateStoreCommand : Command
internal Option<string?> CryptoServiceProviderOption { get; } = new(["--crypto-service-provider", "-csp"], CertificateStoreResources.CspOptionDescription);
internal Option<string?> PrivateKeyContainerOption { get; } = new(["--key-container", "-k"], CertificateStoreResources.KeyContainerOptionDescription);
internal Option<bool> UseMachineKeyContainerOption { get; } = new(["--use-machine-key-container", "-km"], getDefaultValue: () => false, description: CertificateStoreResources.UseMachineKeyContainerOptionDescription);
internal Option<bool> InteractiveOption { get; } = new(["--interactive", "-i"], getDefaultValue: () => false, description: CertificateStoreResources.InteractiveDescription);

internal Argument<string?> FileArgument { get; } = new("file(s)", Resources.FilesArgumentDescription);

Expand All @@ -38,6 +39,7 @@ internal CertificateStoreCommand(CodeCommand codeCommand, IServiceProviderFactor
AddOption(CryptoServiceProviderOption);
AddOption(PrivateKeyContainerOption);
AddOption(UseMachineKeyContainerOption);
AddOption(InteractiveOption);
AddArgument(FileArgument);

this.SetHandler(async (InvocationContext context) =>
Expand All @@ -59,6 +61,7 @@ internal CertificateStoreCommand(CodeCommand codeCommand, IServiceProviderFactor
string? cryptoServiceProvider = context.ParseResult.GetValueForOption(CryptoServiceProviderOption);
string? privateKeyContainer = context.ParseResult.GetValueForOption(PrivateKeyContainerOption);
bool useMachineKeyContainer = context.ParseResult.GetValueForOption(UseMachineKeyContainerOption);
bool isInteractive = context.ParseResult.GetValueForOption(InteractiveOption);
// Certificate fingerprint is required in case the provided certificate container contains multiple certificates.
if (string.IsNullOrEmpty(certificateFingerprint))
Expand Down Expand Up @@ -105,7 +108,8 @@ internal CertificateStoreCommand(CodeCommand codeCommand, IServiceProviderFactor
privateKeyContainer,
certificatePath,
certificatePassword,
useMachineKeyContainer);
useMachineKeyContainer,
isInteractive);
await codeCommand.HandleAsync(context, serviceProviderFactory, certificateStoreServiceProvider, fileArgument);
});
Expand Down
9 changes: 9 additions & 0 deletions src/Sign.Cli/CertificateStoreResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Sign.Cli/CertificateStoreResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="InteractiveDescription" xml:space="preserve">
<value>Allow user interactions (such as a dialog box) when a private key is accessed.</value>
</data>
<data name="CertificateFileOptionDescription" xml:space="preserve">
<value>PFX, P7B, or CER file containing a certificate and potentially a private key.</value>
<comment>{Locked="PFX", "P7B", "CER"} are file extensions.</comment>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Zprostředkovatel kryptografických služeb obsahující kontejner privátního klíče. Vyžaduje /k a volitelně /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Název kontejneru privátního klíče.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Kryptografiedienstanbieter, der den privaten Schlüsselcontainer enthält. Erfordert /k und optional /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Name des privaten Schlüsselcontainers.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Proveedor de servicios criptográficos que contiene el contenedor de claves privadas. Requiere /k y, opcionalmente, /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Nombre de contenedor de clave privada.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Fournisseur de services de chiffrement contenant le conteneur de clé privée. Nécessite /k et éventuellement /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Nom de conteneur de clé privée.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Provider del servizio di crittografia contenente il contenitore di chiavi private. Richiede /k e facoltativamente /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Nome del contenitore di chiavi private.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">秘密キー コンテナーを含む暗号化サービス プロバイダー。/k および必要に応じて /km が必要です。</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">秘密キー コンテナー名。</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">프라이빗 키 컨테이너를 포함하는 암호화 서비스 공급자입니다. /k 및 선택적으로 /km이 필요합니다.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">프라이빗 키 컨테이너 이름입니다.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Dostawca usług kryptograficznych zawierający kontener kluczy prywatnych. Wymaga opcji /k i opcjonalnie /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Nazwa kontenera kluczy prywatnych.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Provedor de Serviços Criptográficos que contém o contêiner de chave privada. Requer /k e, opcionalmente, /km.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Nome do contêiner de chave privada.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Поставщик служб шифрования, содержащий контейнер закрытого ключа. Требуется /k и /km (необязательно).</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Имя контейнера закрытого ключа.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">Özel anahtar kapsayıcısını içeren Şifreleme Hizmeti Sağlayıcısı. /k ve alternatif olarak /km gerektirir.</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">Özel anahtar kapsayıcısı adı.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">包含私钥容器的加密服务提供程序。需要 /k 和 /km (可选)。</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">私钥容器名称。</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.zh-Hant.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<target state="translated">包含私密金鑰容器的加密服務提供者。需要 /k 並選擇性地 /km。</target>
<note>{Locked="/k", "/km"} are command line options.</note>
</trans-unit>
<trans-unit id="InteractiveDescription">
<source>Allow user interactions (such as a dialog box) when a private key is accessed.</source>
<target state="new">Allow user interactions (such as a dialog box) when a private key is accessed.</target>
<note />
</trans-unit>
<trans-unit id="KeyContainerOptionDescription">
<source>Private key container name.</source>
<target state="translated">私密金鑰容器名稱。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal sealed class CertificateStoreService : ISignatureAlgorithmProvider, ICe
private readonly string? _certificatePath;
private readonly string? _certificatePassword;
private readonly bool _isPrivateMachineKeyContainer;
private readonly bool _isInteractive;

private readonly ILogger<CertificateStoreService> _logger;

Expand All @@ -36,7 +37,8 @@ internal CertificateStoreService(
string? privateKeyContainer,
string? certificatePath,
string? certificatePassword,
bool isPrivateMachineKeyContainer)
bool isPrivateMachineKeyContainer,
bool isInteractive)
{
ArgumentNullException.ThrowIfNull(serviceProvider, nameof(serviceProvider));
ArgumentException.ThrowIfNullOrEmpty(certificateFingerprint, nameof(certificateFingerprint));
Expand All @@ -46,6 +48,7 @@ internal CertificateStoreService(
_cryptoServiceProvider = cryptoServiceProvider;
_privateKeyContainer = privateKeyContainer;
_isPrivateMachineKeyContainer = isPrivateMachineKeyContainer;
_isInteractive = isInteractive;
_certificatePath = certificatePath;
_certificatePassword = certificatePassword;

Expand All @@ -65,7 +68,7 @@ public async Task<RSA> GetRsaAsync(CancellationToken cancellationToken)
// Get RSA from a cryptographic service provider
if (!string.IsNullOrEmpty(_privateKeyContainer) && !string.IsNullOrEmpty(_cryptoServiceProvider))
{
var cngKeyFlags = CngKeyOpenOptions.Silent;
var cngKeyFlags = _isInteractive ? CngKeyOpenOptions.None : CngKeyOpenOptions.Silent;

if (_isPrivateMachineKeyContainer)
{
Expand Down Expand Up @@ -177,4 +180,4 @@ private bool TryFindCertificate(StoreLocation storeLocation, string expectedFing
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal class CertificateStoreServiceProvider : ISignatureProvider
private readonly string? _certificateFilePath;
private readonly string? _certificateFilePassword;
private readonly bool _isMachineKeyContainer;
private readonly bool _isInteractive;

private readonly object _lockObject = new();
private CertificateStoreService? _certificateStoreService;
Expand All @@ -33,6 +34,7 @@ internal class CertificateStoreServiceProvider : ISignatureProvider
/// <param name="certificateFilePath">Optional path to the PFX, P7B, or CER file with the certificate.</param>
/// <param name="certificateFilePassword">Optional password used to open the provided certificate.</param>
/// <param name="isMachineKeyContainer">Optional Flag used to denote per-machine key container should be used.</param>
/// <param name="isInteractive">Optional Flag used to denote when user interactions are expected during key retrieval.</param>
/// <exception cref="ArgumentException">Thrown when a required argument is empty not valid.</exception>
internal CertificateStoreServiceProvider(
string certificateFingerprint,
Expand All @@ -41,7 +43,8 @@ internal CertificateStoreServiceProvider(
string? privateKeyContainer,
string? certificateFilePath,
string? certificateFilePassword,
bool isMachineKeyContainer)
bool isMachineKeyContainer,
bool isInteractive)
{
ArgumentException.ThrowIfNullOrEmpty(certificateFingerprint, nameof(certificateFingerprint));

Expand All @@ -58,6 +61,7 @@ internal CertificateStoreServiceProvider(
_cryptoServiceProvider = cryptoServiceProvider;
_privateKeyContainer = privateKeyContainer;
_isMachineKeyContainer = isMachineKeyContainer;
_isInteractive = isInteractive;
_certificateFilePath = certificateFilePath;
_certificateFilePassword = certificateFilePassword;
}
Expand Down Expand Up @@ -98,7 +102,8 @@ private CertificateStoreService GetService(IServiceProvider serviceProvider)
_privateKeyContainer,
_certificateFilePath,
_certificateFilePassword,
_isMachineKeyContainer);
_isMachineKeyContainer,
_isInteractive);
}

return _certificateStoreService;
Expand Down
Loading

0 comments on commit ea23724

Please sign in to comment.