Posh-ACME icon indicating copy to clipboard operation
Posh-ACME copied to clipboard

Explore options to avoid storing PFX password

Open LainRobertson opened this issue 4 months ago • 8 comments

Issue

Currently, if you successfully call New-PACertificate then the PFX password is retained on storage in the order.json in base64 format, which is far too easily decoded.

Request

Can we please have an option to not store the password on storage?

Additional comments

There's likely multiple means of achieving this, but as someone who uses only the basic functionality of the module, what I'd like to see is:

  1. A switch added to New-PACertificate that aligns to an "opt-out" approach of not storing the password in order.json;
  2. A SecureString (and a plain text alternative if necessary) parameter added to Submit-Renewal that would hold the PFX password for renewals.

By choosing an opt-out design, it should not impact existing automation/orchestration while allowing for the request to be met.

This approach would leave the password orchestration to the calling automation wrapper, which is preferable.

LainRobertson avatar Aug 12 '25 02:08 LainRobertson

Hi @LainRobertson, thanks for reaching out. Realistically, I don't think it's going to be possible in the current 4.x version of the module to get rid of the PFX password without opting out of creating a PFX file entirely. There are a bunch of changes you can make to an order after it's complete that require re-creating the PFX file and the module needs the password to do that. It wouldn't be as simple as a new switch on New-PACertificate. And even if you don't need to change the order after creation, other people do and I'd have to support those workflows as well.

Besides, being able to decode the PFX password easily isn't really gaining you anything if you already have access to the rest of the files in the order folder. The unencrypted private key is right there next to the PFX. You don't need the password to access it. Creating the PFX is just a convenience for folks who need the cert in that format.

All that said, I'll definitely put it on the list of things to consider for 5.x.

In the mean time, there's technically a workaround you can use to effectively disable PFX creation already (thus making the PFX password value irrelevant). Use the New-PACertificate -CSRPath parameter set. It would mean manually generating your private key (and an associated CSR) in advance. But if the security of the key is what you're worried about, you should be doing that anyway and not even transferring it to the system generating the cert. When using -CSRPath, the module assumes it doesn't have access to the private key and skips everything having to do with the PFX file. A default PFX Password is still stored in the order object, but it's just ignored and the only cert files that end up in the order folder are variations of the public cert and chain.

Renewals can work with the original CSR as long as the private key (and domain list) doesn't change. But if you are generating a new key on each renewal, it would require a new CSR as well.

rmbolger avatar Aug 12 '25 05:08 rmbolger

Thanks @rmbolger ,

I use the PFX file and nothing else, so for the time being what I am doing is removing all the files from the order directory if the call to New-PACertificate was successful (after handling the PFX, that is). Of course, this precludes me then from being able to leverage the Submit-Renewal commandlet (primarily in the hope I could avoid the DNS authorisations), which is what I was hoping I might achieve via this request.

It sounds like my request isn't viable in which case I'll just stick to my existing process of only using New-PACertificate and then automating the DNS authorisations.

Thanks for the informative response.

Cheers, Lain

LainRobertson avatar Aug 12 '25 06:08 LainRobertson

If you're self-managing DNS authorizations anyway, have a look at the Custom Challenge Validation guide if you haven't already. You don't necessarily need New-PACertificate at all and can get away with the lower level functions that might simplify things.

Alternatively, you might also be able to turn whatever your process for managing DNS authorizations is into its own DNS plugin. Check out the Plugin Development Guide if you're interested even if you have no intention of making it public. The module supports custom plugins via an environment variable.

I just thought of one more thing. If what you end up doing is copying the PFX out to somewhere else anyway, you could take an extra step before you remove the files in the order folder and use Set-PAOrder to change the PFX password to something else. So like:

  • Copy the PFX with the real password wherever
  • Set-PAOrder -PfxPass(Secure) <blah>
  • gci (Get-PAOrder).Folder | ?{ $_.Name -notlike '*.json' } | Remove-Item

Theoretically you should be able to remove everything but the *.json files in the order folder and still have Submit-Renewal work. Though Submit-Renewal also really only works if you're automating the challenges with a plugin.

rmbolger avatar Aug 12 '25 06:08 rmbolger

Also, re-opening this to keep it on my radar for 5.x.

rmbolger avatar Aug 12 '25 06:08 rmbolger

Technically PFX files do not require a password. Could be an option where we use $cert = New-PACertificate without having that actually saving it to the filesystem. For example because it gets imported to local system store or Azure Key Vault.

Tim81 avatar Aug 14 '25 18:08 Tim81

I could've sworn I tried to make a no-password default way back when I was first writing the module. I don't remember exactly why it didn't work. It was something like either the library I was using to create the file required a password or Windows (at the time) didn't like importing one without a password.

rmbolger avatar Aug 15 '25 05:08 rmbolger

I believe it is possible like this:

Export-PfxCertificate -Cert $cert -FilePath "C:\path\to\cert.pfx" -Password (ConvertTo-SecureString -String "" -Force -AsPlainText)

The command requires the password parameter, but you can make empty I believe, as of Windows 10 / Windows Server 2016 (Powershell 5.1) and also Powershell 7

Tim81 avatar Aug 16 '25 14:08 Tim81

Afraid not, @Tim81. ConvertTo-SecureString won't accept an empty string param.

> ConvertTo-SecureString -String '' -Force -AsPlainText
ConvertTo-SecureString: Cannot bind argument to parameter 'String' because it is an empty string.

You'd have to do something like this instead.

$emptySS = [System.Net.NetworkCredential]::new('','').SecurePassword

It does seem that whatever was preventing me from allowing empty PFX passwords before is no longer a problem. So it's probably worth at least removing those validations so people can set an empty password if they want to.

As it pertains to this particular issue, it's somewhat irrelevant though. @LainRobertson wants a non-empty PFX password. He just doesn't want the password stored on disk once the cert files are created.

rmbolger avatar Aug 16 '25 18:08 rmbolger