Reputation: 312
So we have a code which creates a private key and a certificate from an authority and we try to create a Powershell function to make PFX file containing the cert and private key together. The problem is that the resulting PFX always contains only the cert and not the private key. The input files are fine, because I can create PFX file which has cert and private key via openssl this way
openssl pkcs12 -export -out mycertificate.pfx -inkey private.key -in cert.cer
the key is in base64 format with header -----BEGIN PRIVATE KEY----- and lines padded to 64 chars. The certificate is DER PKCS12
I am checking the result with openssl.exe pkcs12 -in cert.pfx -info -nodes
and the PFX file created via Powershell does not have a private key but it does when created with openssl. Why is that and where is an error in this function? I have consulted ChatGPT and Gemini AI but their suggestions didn't work.
$certContent = Get-Content -Path $CertPath -Raw
# Ensure certificate is in PEM format
if ($certContent -notmatch '-----BEGIN CERTIFICATE-----') {
<#DebugLog "Certificate not in PEM format, attempting to convert..."
$certBytes = [System.IO.File]::ReadAllBytes($CertPath)
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($certBytes)
$pemCert = "-----BEGIN CERTIFICATE-----`n"
$pemCert += [Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
$pemCert += "`n-----END CERTIFICATE-----"
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::CreateFromPem($pemCert)
#the following line seems to work too and all the above lines can be replaced by just this one. Right?
#$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath)
}
else {
# Certificate is already in PEM format
DebugLog "Certificate is already in PEM format"
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::CreateFromPem($certContent)
}
# Load private key
if (-not (Test-Path -Path $KeyPath)) {
throw "Private key file not found at '$KeyPath'"
}
$keyPem = Get-Content -Path $KeyPath -Raw
# Detect private key type
$rsa = $null
if ($keyPem -match '-----BEGIN PRIVATE KEY-----') {
$keyBase64 = ($keyPem -replace '-----BEGIN PRIVATE KEY-----', '') -replace '-----END PRIVATE KEY-----', ''
$keyBase64 = $keyBase64 -replace '\s', '' # Remove any whitespace or newlines
$keyBytes = [Convert]::FromBase64String($keyBase64)
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportPkcs8PrivateKey($keyBytes, [ref]0)
}
elseif ($keyPem -match '-----BEGIN RSA PRIVATE KEY-----') {
$keyBase64 = ($keyPem -replace '-----BEGIN RSA PRIVATE KEY-----', '') -replace '-----END RSA PRIVATE KEY-----', ''
#$keyBase64 = $keyBase64 -replace '\s', '' # Remove any whitespace or newlines
$keyBytes = [Convert]::FromBase64String($keyBase64)
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportRSAPrivateKey($keyBytes, [ref]0)
}
else {
throw "Unsupported private key format in file '$KeyPath'"
}
# Ensure the private key is correctly bound to the certificate
try {
$certWithKey = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
$cert.RawData,
$rsa,
[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
)
#$certWithKey = $cert.CopyWithPrivateKey($rsa)
#$certWithKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::CopyWithPrivateKey($cert, $rsa)
}
catch {
throw "Failed to associate the private key with the certificate: $_"
}
# Export to PFX with password (if given)
if ($PfxPassword) {
$pfxBytes = $certWithKey.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, $PfxPassword)
}
else {
$pfxBytes = $certWithKey.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx)
}
# Save PFX to file
[System.IO.File]::WriteAllBytes($OutputPath, $pfxBytes)
Upvotes: 0
Views: 66
Reputation: 13588
If you cannot use openssl.exe
, because it is not a built-in tool, you can use certutil.exe
instead. You can create your PFX file like this:
certutil.exe -mergePFX cert.cer mycertificate.pfx
Make sure your private key is in the same directory as cert.cer
and has the same name, but with the .key
extension, e. g. cert.key
.
Upvotes: 1