Skip to content

Commit b9492a1

Browse files
committed
Automatic detection of decryption certificates
The Certificate parameter of Unprotect-Data is no longer mandatory. If a matching certificate is found in the Personal store of either the user or the machine (and the private key is accessible in either location), then that cert will automatically be used to decrypt. Also fixed a bug that was producing KeyData objects that were hashtables instead of PSCustomObjects when encrypting data with an ECDH certificate. (All of the code still works either way, but it was inconsistent.)
1 parent 9bfa49f commit b9492a1

File tree

3 files changed

+64
-7
lines changed

3 files changed

+64
-7
lines changed

Commands.ps1

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ function Unprotect-Data
265265
})]
266266
$InputObject,
267267

268-
[Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
268+
[Parameter(ParameterSetName = 'Certificate')]
269269
[object]
270270
$Certificate,
271271

@@ -313,13 +313,47 @@ function Unprotect-Data
313313
$key = $null
314314
$iv = $null
315315

316-
if ($null -ne $cert)
316+
if ($null -ne $Password)
317317
{
318-
$params = @{ Certificate = $cert }
318+
$params = @{ Password = $Password }
319319
}
320320
else
321321
{
322-
$params = @{ Password = $Password }
322+
if ($null -eq $cert)
323+
{
324+
$paths = 'Cert:\CurrentUser\My', 'Cert:\LocalMachine\My'
325+
326+
$cert = :outer foreach ($path in $paths)
327+
{
328+
foreach ($keyData in $InputObject.KeyData)
329+
{
330+
if ($keyData.Thumbprint)
331+
{
332+
$certObject = $null
333+
try
334+
{
335+
$certObject = Get-KeyEncryptionCertificate -Path $path -CertificateThumbprint $keyData.Thumbprint -RequirePrivateKey -ErrorAction $IgnoreError
336+
} catch { }
337+
338+
if ($null -ne $certObject)
339+
{
340+
$certObject
341+
break outer
342+
}
343+
}
344+
}
345+
}
346+
}
347+
348+
if ($null -eq $cert)
349+
{
350+
Write-Error -Message 'No decryption certificate for the specified InputObject was found.' -TargetObject $InputObject
351+
return
352+
}
353+
354+
$params = @{
355+
Certificate = $cert
356+
}
323357
}
324358

325359
try
@@ -1655,7 +1689,7 @@ function Protect-KeyDataWithEcdhCertificate
16551689
$encryptedKey = Protect-DataWithAes -PlainText $Key -Key $derivedKey -IV $ecdhIv -NoHMAC
16561690
$encryptedIv = Protect-DataWithAes -PlainText $IV -Key $derivedKey -IV $ecdhIv -NoHMAC
16571691

1658-
New-Object psobject @{
1692+
New-Object psobject -Property @{
16591693
Key = $encryptedKey.CipherText
16601694
IV = $encryptedIv.CipherText
16611695
EcdhPublicKey = $ecdh.PublicKey.ToByteArray()

ProtectedData.Tests.ps1

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,29 @@ Describe 'Certificate-based encryption / decryption (by certificate path)' {
361361
}
362362
}
363363

364+
Describe 'Certificate-based decryption (automatic detection of cert)' {
365+
Remove-TestCertificate
366+
367+
$certThumbprint = New-TestCertificate -Subject $testCertificateSubject
368+
369+
$protectedData = $stringToEncrypt | Protect-Data -Certificate Cert:\CurrentUser\My\$certThumbprint
370+
371+
It 'Successfully finds the matching certificate and decrypts the data' {
372+
$hash = @{}
373+
$scriptBlock = { $hash.Decrypted = Unprotect-Data $protectedData -ErrorAction Stop }
374+
375+
$scriptBlock | Should Not Throw
376+
$hash.Decrypted | Should Be $stringToEncrypt
377+
}
378+
379+
Remove-TestCertificate
380+
381+
It 'Gives a useful error message when no matching certificate is found' {
382+
$scriptBlock = { $null = Unprotect-Data $protectedData -ErrorAction Stop }
383+
$scriptBlock | Should Throw 'No decryption certificate for the specified InputObject was found'
384+
}
385+
}
386+
364387
Describe 'HMAC authentication of AES data' {
365388
$protectedData = $stringToEncrypt | Protect-Data -Password $passwordForEncryption
366389

ProtectedData.psd1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
@{
1010
ModuleToProcess = 'ProtectedData.psm1'
11-
ModuleVersion = '4.1.0'
11+
ModuleVersion = '4.1.1'
1212
GUID = 'fc6a2f6a-563d-422a-85b5-9638e45a370e'
1313
Author = 'Dave Wyatt'
1414
CompanyName = 'Home'
@@ -37,7 +37,7 @@
3737
# Indicates this is a pre-release/testing version of the module.
3838
IsPrerelease = 'False'
3939

40-
ReleaseNotes = 'Deprecated the -SkipCertificateVerification parameter. Made InputObject parameter positional for all relevant cmdlets.'
40+
ReleaseNotes = 'Added code to automatically detect an available decryption certificate.'
4141
}
4242
}
4343
}

0 commit comments

Comments
 (0)