I am hoping I missed something obvious as I have tried 3 different approaches to generate a PFX/PKCS12 byte array so that the X509Certificate2 class will initiate.
Problem: Code is throwing an exception:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The specified network password is not correct'
Goal: Generate a X509Certificate2 that includes the private key that passes the following code with the organization's best practice for self-signed certificates in development:
public static X509Certificate2 GetSigningCertificate(byte[] rawBytes)
{
X509Certificate2 certificate;
try
{
certificate= new X509Certificate2(rawBytes);
}
catch (Exception ex)
{
throw new Exception(Errors.MalformedCertificate);
}
if (!certificate.HasPrivateKey)
{
throw new Exception(Errors.PrivateKeyIsMissing);
}
return certificate;
}
Code Limitations: Due to a political/organization policy the code, above, that accepts the byte[] cannot be modified by me directly as it is controlled by another team (owners). If after all possible options are exhausted I will then put a formal request document to discuss code changes with good cause.
Platform: Windows 10 Enterprise (1803)
Compile Targets: netstandard2 & .Net 4.7.1
Automation Tooling Approach 1: Using OpenSSL
openssl.exe rand -out C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.rnd -base64 4096
openssl.exe genrsa -rand "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.rnd" -passout pass:"RfTjWnZr4u7x!A%E" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" 2048
openssl.exe genrsa -rand "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.rnd" -passout pass:"RfTjWnZr4u7x!A%E" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key-encrypted.pem" -des3 2048
openssl.exe rsa -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -pubout -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-privatekey-corresponding-public-key.pem"
openssl.exe rsa -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -RSAPublicKey_out -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-privatekey-corresponding-rsa-public-key.pem"
openssl.exe req -x509 -days 90 -passin pass:"RfTjWnZr4u7x!A%E" -key "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key-encrypted.pem" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-certificate.cer" -subj "/C=US/ST=CA/L=Newport Beach/O=AutoNow Inc/OU=Application Development/CN=*.autonow.com"
openssl.exe req -new -key "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-certificate-signature.csr" -subj "/C=US/ST=CA/L=Newport Beach/O=AutoNow Inc/OU=Application Development/CN=*.autonow.com"
openssl.exe pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -name "" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pfx" -passin pass:"RfTjWnZr4u7x!A%E" -inkey "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key-encrypted.pem" -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-certificate.cer" -passout pass:"VkXp2s5v8x/A?D(G"
openssl.exe pkcs12 -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pem" -passin pass:"VkXp2s5v8x/A?D(G" -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pfx" -passout pass:"VkXp2s5v8x/A?D(G" -clcerts -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider"
Automation Tooling Approach 2: Using Powershell cmdlets
$VerbosePreference = "Continue"
$certStorePath = "Cert:\CurrentUser\My"
$friendlyName = "Customer Support Administration Tool SelfSigned Certificate"
$name = "*.autonow.com"
$dnsname = "services-dev.autonow.com, *.autonow.com, localhost"
$notBefore = $(Get-Date).Date.AddDays(-90)
$notAfter = $(Get-Date).Date.AddDays(90)
$pfxPassword = ConvertTo-SecureString -String "^adhd.Customer.Support.Administration.Tool.20190214" -Force -AsPlainText
$selfSignedCertificate = New-SelfSignedCertificate `
-Subject $name `
-DnsName $dnsname `
-KeyAlgorithm RSA `
-KeyLength 2048 `
-NotBefore $notBefore `
-NotAfter $notAfter `
-CertStoreLocation $certStorePath `
-FriendlyName $friendlyName `
-HashAlgorithm SHA256 `
-KeyUsage DigitalSignature, KeyEncipherment, DataEncipherment `
-KeyExportPolicy Exportable `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") `
$selfSignedCertificatePath = Join-Path -Path $certStorePath -ChildPath "$($selfSignedCertificate.Thumbprint)".ToUpper()
Write-Debug $selfSignedCertificatePath
# Create temporary certificate path
$tmpPath = "C:\Vault\OpenSSL\Certificates\API\customer-support-administration"
If(!(test-path $tmpPath))
{
$created = New-Item -ItemType Directory -Force -Path $tmpPath
}
Write-Debug $tmpPath
# Set certificate password here
$pfxFilePath = Join-Path -Path $tmpPath -ChildPath "customer-support-administration.pfx"
Write-Debug $pfxFilePath
$cerFilePath = Join-Path -Path $tmpPath -ChildPath "customer-support-administration.cer"
Write-Debug $cerFilePath
# Create pfx certificate
$exportedPfx = Export-PfxCertificate -Cert $selfSignedCertificatePath -FilePath $pfxFilePath -Password $pfxPassword -Force -CryptoAlgorithmOption AES256_SHA256
$exportedCer = Export-Certificate -Cert $selfSignedCertificatePath -FilePath $cerFilePath -Type CERT -Force
#
# Get Raw Bytes idea 1
#
[Byte[]]$pfxBytes = [System.IO.File]::ReadAllBytes($exportedPfx.FullName)
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$pfxBytes), $pfxPassword
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The certificate is incompatible with the security requirements.")
}
# Read directly from the file for the raw bytes
$pfxBase64 = [Convert]::ToBase64String($pfxBytes)
# moment of truth instantiate the certificate using the security package code for idea 1
[Byte[]]$pfxBytes = [Convert]::FromBase64String($exportBase64)
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$pfxBytes),$pfxPassword
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The rehydrated certificate is incompatible with the security requirements.")
}
#
# Get Raw Bytes idea 2
#
$pfxKeyFlags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bor
[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor
[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::UserKeySet
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$pfx.Import($pfxBytes, $pfxPassword, $pfxKeyFlags)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The certificate is incompatible with the security requirements.")
}
$exportedBytes = $pfx.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, $pfxPassword)
$exportBase64 = [Convert]::ToBase64String($exportedBytes)
# test the instantiate
[Byte[]]$newPfxBytes = [Convert]::FromBase64String($exportBase64)
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList ($newPfxBytes,$pfxPassword)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The rehydrated certificate is incompatible with the security requirements.")
}
# moment of truth instantiate the certificate using the security package code
[Byte[]]$newPfxBytes = [Convert]::FromBase64String($exportBase64)
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$newPfxBytes)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The rehydrated certificate is incompatible with the security requirements.")
}
The end result is still an exception is thrown.
New-Object : Exception calling ".ctor" with "1" argument(s): "The specified network password is not correct.
"
At line:2 char:8
+ $pfx = New-Object System.Security.Cryptography.X509Certificates.X509C ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
The output of idea #2 base64:
PS C:\WINDOWS\system32> $exportBase64
MIIKwAIBAzCCCnwGCSqGSIb3DQEHAaCCCm0EggppMIIKZTCCBfYGCSqGSIb3DQEHAaCCBecEggXjMIIF3zCCBdsGCyqGSIb3DQEMCgECoIIE9jCCBPIwHAYKKoZIhvcNAQwBAzAOBAgi31AW/5cI5gICB9A
EggTQVgPWXArFCY2eg2jv+hDLZsc+i3wEqw4Cy2CYgcj86WnRE0n/zVcNzi9lyH5jK1zpBfTpAnfTO4WNG8cdQ5qCtOy9TjkTyrGYrXywrGHgLYmHpsg43uMMWcihWO5+zek4YP3MQDtyuQT2+hCaviFwFu
TovlaLHjGlNcYAz7AlL/6sJ54EzTjo8zfKW08zaR1GsEeP9odYIlgVEltJEgsW69Ed7v1uNs5+vprEecVvegTr5o3LzV9UflF8ye4wv65ZtjJdb+9uTHeYLwje0woWvWfGAQ1KBZTWyJeIWEGhs04vqhKbO
Dak+KmvjYh2U3BYYLKlvCDtYulFAeZlDZFZXPii+8ND279wFZmF53nzPqzjg1rlsSjNZLREZ3FIFkYkBFBDPqrQQ56OKcnh+YStFhOrXz+Q3Sc0PrUMABWUuUqeTxjE9YEpng0wQ4ocHETICLfhofcvyqAD
Zb03is15Xzr6V/Z1SJ/pZzVhN7ov9PmW/LNqD7d+hCiemIvt2GAaO1FFtKaFCVvcO4jvfalyrpFwDOBvnEFR6OAQryyYT79jqYgGFP42Y7Acs7Jju/vizkNxo8szZvABUh3UgEli72AzcNDjSOxEkdU6yzS
ycYbh+26Cwi1KLmCB/4nTErUt2s4XtczRdA4Km+0pN8xpDyHrWf+Z4StKPCsxjwCHfIB+Y9ol1LT31k/MZ9o5TX/YKxp0gBqqfemrkkSVKDzEtk/KZGsVVkusVstpqGMdsEacdf8KoRI06yrgP6SQedwexk
Zm4Zhv6VUXOumPne0ZtMTSoXb8cuQ4PbmI056LYySLqdk2b89snVTE5QL/f+t3USY45MBcp67Boti2aFq0VYrkSSaw3GO2W9PfRZk+Uw8ubt1eO1MFB57dUSy6GL38SWWuqIrwCsmRJCwMaMUSIu+L2UOcn
N409rIMFEB908r3iMZoOUS1sDTxQotKW/6rx3LZKp8vS8rdTr4j++Ka22JjOCwBWFtExSoTgQ6YegG4wCy3PrbXq96a3/kStpqo04eeblfrqD1o1lNd/hJDqnALiszI2EJW+I/Ig61bHOpYKj8+S5M2Wen5
LtB+UOzcfTzc5zwGFhDQ9zjbkZVbvl1NvCU0pcJwjayKOFhdBDMJh9uP+60i27/9Zi7xeC/ivsT5fAEMh7JJVbjKLtQAjQNf5Bkj5gSvMjdEVjRUPVG2x4OJ5wHb6/wT5ciMjipV7w4mQqn45uMtmIHUpsk
TOx7SQAzLQfn1suvDStiS95Y1pbK6wKMV/86SvtvPjxQHZ3/LNq9i4j7gc8Np9/g250vcm+UeEXn4v3iWl3DhjXwXjCdnmNXPjguCawX2BUdhE32VERBNXU8d4KJitqDXSb2T/geaRRec8tMYtgjtiewO+q
vB2eLCdDR/SXBJyfjTyzBZ36SxP8VyD0GIMAdGpUjU395DW0sWzrNmNZMBAnMHF2wG3iLpsdViJfMzCH2Nf//kxey8qJeL1wAXq+9v9p1aFWVfBOSe6adhomGj8q/hApZI6zq0BdFhYYfuNS21et2fHtLcs
RWbav1DiObDWD3ktB3+t0TxNyaaHmGxKtVDG1KS1JDrddk+Aam9xynw0IgGExWfrqwO6nvePiGHMil6BJIaKpP4ocQP/muUu/YxgdEwEwYJKoZIhvcNAQkVMQYEBAEAAAAwWwYJKoZIhvcNAQkUMU4eTAB7
AEQAMwBEAEYAQgBEADUAQwAtADgAMQBFADcALQA0ADUAMQA0AC0AOAAxADYARgAtAEUAOQAxADgAOAA3ADcANAA2ADkAOAA5AH0wXQYJKwYBBAGCNxEBMVAeTgBNAGkAYwByAG8AcwBvAGYAdAAgAFMAbwB
mAHQAdwBhAHIAZQAgAEsAZQB5ACAAUwB0AG8AcgBhAGcAZQAgAFAAcgBvAHYAaQBkAGUAcjCCBGcGCSqGSIb3DQEHBqCCBFgwggRUAgEAMIIETQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQI6JcEqu
Pyqx8CAgfQgIIEIDLZj9eiSr1DM8afM1CKB+VOPgMzvhrvmE1H1bjrAVGUp3R996v4xyOfsTvLHLgzTX0z1E/RXNA4CBU4TdHm8ptWw3jj5dHlEzOyoWMftSacouDLP7x3M3weea5Lv5BvmYKe8ZqQzNUbj
dhnVWIH3JuFKTm+CKM8nC/HELOdCC98v/hdv0b/1fy3QzXV+QCuJ9s2rfEBGmxmbgzYmRPlRHPzwN5r4qFlrDbIrbSk++6K4FJ3UTP7PD/HjLvyLRp6vzYKsd5igHbR/Qk4qpUAmWNJcyQVzoZTY034UKNX
3y6E+qHiL8r5VlsNUB3c8TizXZas71NJqqDG8yaPiaUE/HZyVZaps0zcKgbjNeyOTWdcjXN186gdAvAUIp4qm36Udo23oEcHtDtbi+Ia+IG8ca9B37cO0fbUgbNF8ePcjNYHiNKlRkZhwwALn+WjuW//bqV
6a20pINZKCNAXD34wGH2T/eFSKcgfjPqYwRbovEvQOki0sMKT8i5kXbJx5nvVEb4g3hO9k7kz05MUOYCu7FQg/J6l3BPMCKR02lOr0KWIrlyK+MzNJEQ1S6OboEF0rLSTHUHUTScHjxU4q6FonC7+dJY5pL
iCa0WzhvH6oCieY0v6mYnnOlwxK5dxllC8KKdDV4nJYha9DBivBbJEpVUEygZ/4WemGPdqExYcuB9euQ0RFpO7tVk4NAdZfnMfP8gAi8lGSGIyn1lz7t0AxyUPr0QZZr7wgpK3doTSr7j3G/n1DOKZUkuBN
CqhVO9muTOGlhblXCT4NMdRGZI+qJj+/0OEJXinvRGc7pMtMhbL6G8rSGWev7NZfmktHdC/W1VgopMzJsavpVw98WCWjz/gQYt3jc5RFCehH9NpiUFWCSbJTYqlXOfbEB3koIspX7+0hpYxCLLm0Lw+O0PK
vRHsL6AjnsyZITwnnizC/nhW4GfFKyu7ZQ/SdKq2oOWZL5nXc+tMV2KQsOzf/U1Wlu6jmlqGwS5ZOHhcGm91YLdHsBQ+KnI64ehvGQrb/CEgGz76LH/p7QhRno8c6XZt8wGQTfvX351nj8Doytx7gf1wboT
ox3VyQyQxm9lyYWhN1HlCkyJsx9hiWoOIyK4iKUyHX/1rZLRxXFPaOTBbunjSGvDY+bI6IaYtMXIqy8DkWy8KR4g/zmoKcsY8SLt8eNdM2HVX7FO3hjYnPZOhfiFZU9cBifBf+R4BIJE+8OOMtpC2adJK5J
Lia1tZdjkfMyOIfYBNLNP5GXKPKnrEd3YOG8b4cnzs9MFNgT7VLg/5Lgz8EOlRaMwRm1NObTHqAX1nVjmxbdKYvsSuJ5lATkGFgvMYC7xqnY2jkQDOGgMIKzpwMjHAbvYYmPMIYdnrd8lxbJDYyjBdG+i4h
tRfuXKlpPr/CSiQppD+HRndqLf4XzA7MB8wBwYFKw4DAhoEFGHc0Fq9VcSMZ3WjK2pi+Xp/V5kqBBRv6dOxMFKx5SsNoj74lWmA1Ua6QwICB9A=
What I was shown works is these generic steps:
openssl genrsa -out privatekey.pem 1024
openssl req -new -x509 -key privatekey.pem -out publickey.cer -days 10000
openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer
The command above does produce a pfx that will pass the constructor
# use openssl generic pfx
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList "C:\Vault\OpenSSL\Certificates\API\public_privatekey.pfx"
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The openssl generic certificate is incompatible with the security requirements.")
}
If I reference the PFX file it does pass the constructor and works!? If I pass the raw file bytes again it works?! Where in the processes of what I wrote for automation fails while the overly generic/simple one works?
# use openssl generic pfx
[Byte[]]$pfxBytes = [System.IO.File]::ReadAllBytes("C:\Vault\OpenSSL\Certificates\API\public_privatekey.pfx")
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$pfxBytes)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The openssl generic certificate is incompatible with the security requirements.")
}
-- Update --
Using the OpenSSL way to use non-encrypted on genrsa and the pkcs12 commands to have a -passin pass: and -passout pass: so they'll be treated as blank. This seems to work however my colleagues and myself are curious if not having an encrypted private key and export password on the pfx is best practice. If not how do we get it up to par?
openssl.exe rand -out C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.rnd -base64 4096
openssl.exe genrsa -rand "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.rnd" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" 2048
openssl.exe genrsa -rand "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.rnd" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key-encrypted.pem" 2048
openssl.exe rsa -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -pubout -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-privatekey-corresponding-public-key.pem"
openssl.exe rsa -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -RSAPublicKey_out -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-privatekey-corresponding-rsa-public-key.pem"
openssl.exe req -x509 -days 90 -key "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-certificate.cer" -subj "/C=US/ST=CA/L=Newport Beach/O=AutoNow Inc/OU=Application Development/CN=*.autonow.com"
openssl.exe req -new -key "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-certificate-signature.csr" -subj "/C=US/ST=CA/L=Newport Beach/O=AutoNow Inc/OU=Application Development/CN=*.autonow.com"
openssl.exe pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -name "" -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pfx" -inkey "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-private-key.pem" -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration-certificate.cer" -passout pass:
openssl.exe pkcs12 -out "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pem" -in "C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pfx" -clcerts -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -passout pass: -passin pass:
# This now works
[Byte[]]$pfxBytes = [System.IO.File]::ReadAllBytes("C:\Vault\OpenSSL\Certificates\API\customer-support-administration\customer-support-administration.pfx")
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$pfxBytes)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The certificate is incompatible with the security requirements.")
}
$base64Pfx = [System.Convert]::ToBase64String($pfxBytes)
# moment of truth instantiate the certificate using the security package code for updated openssl commands
[Byte[]]$pfxBytes = [Convert]::FromBase64String($base64Pfx)
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$pfxBytes)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The rehydrated certificate is incompatible with the security requirements.")
}
$pfx | select *
and
$base64Pfx = [System.Convert]::ToBase64String($pfxBytes)
# moment of truth instantiate the certificate using the security package code for idea 1
[Byte[]]$pfxBytes = [Convert]::FromBase64String($base64Pfx)
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$pfxBytes)
If (-not $pfx.HasPrivateKey)
{
throw [System.Exception]::new("The rehydrated certificate is incompatible with the security requirements.")
}
Just going by the error message along, you are passing in raw bytes of a certificate without a password...
It looks like you are creating a PFX file that requires a password when using X509Certificate2.
Try creating a PFX that doesn't have a password set it.
and for the updated question:
It depends (as almost everything in software development).
It's a security issue around the management of the certificate file(s).
If the certificate file can "escape" or be visible wider than you what you want then it's best to add a password.
If you can lock down access to the certificate file then should be OK.
If you want to support a password the C# code will need to change to pass in the password as well as the byte[] to the X509Certificate2 constructor.
If you do use a password then then the security management issue now shifts to your password storage.