.NET Framework x509Certificate2 Class, HasPrivateKey == true && PrivateKey == null? - c#

I'm attempting to work with an X509 certificate that was originally imported into the CurrentUser keystore on a Windows 10 computer using the "Certificates" snap-in of an MMC. The same procedure has been tested on a Windows 8.1 computer with the same result.
Using the standard PowerShell PKI module, I'm getting an X509Certificate2 object using Get-Item:
$my_cert = Get-Item Cert:\CurrentUser\My\ADAA82188A17THUMBPRINTXXXXXXXXXXX
The output of $my_cert | fl * is as follows:
PSPath : Microsoft.PowerShell.Security\Certificate::CurrentUser\My\XXXXXXXXXXXXXXXXXXX
PSParentPath : Microsoft.PowerShell.Security\Certificate::CurrentUser\My
PSChildName : XXXXXXXXXXXXXXXXXXX
PSDrive : Cert
PSProvider : Microsoft.PowerShell.Security\Certificate
PSIsContainer : False
EnhancedKeyUsageList : {Secure Email (1.3.6.1.5.5.7.3.4), IP security user (1.3.6.1.5.5.7.3.7), Encrypting File
System (1.3.6.1.4.1.311.10.3.4), Document Signing (1.3.6.1.4.1.311.10.3.12)...}
DnsNameList : {My Name}
SendAsTrustedIssuer : False
EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
PolicyId : {D52C406F-C279-4BF2-B7C2-EE704290DB3E}
Archived : False
Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid,
System.Security.Cryptography.Oid, System.Security.Cryptography.Oid...}
FriendlyName :
IssuerName : System.Security.Cryptography.X509Certificates.X500DistinguishedName
NotAfter : 4/15/2017 2:15:16 PM
NotBefore : 4/15/2016 2:15:16 PM
HasPrivateKey : True
PrivateKey :
PublicKey : System.Security.Cryptography.X509Certificates.PublicKey
RawData : {56, 130, 19, 252...}
SerialNumber : 4F0000002F700000000000000000000000
SubjectName : System.Security.Cryptography.X509Certificates.X500DistinguishedName
SignatureAlgorithm : System.Security.Cryptography.Oid
Thumbprint : XXXXXXXXXXXXXXXXXXX
Version : 3
Handle : 2241663016272
Issuer : CN=Issuing CA, DC=My, DC=Domain, DC=us
Subject : E=my.name#my.domain.us, CN=My Name
So HasPrivateKey == True, but PrivateKey == null. I've been trying to figure out how to gain access to the private key to perform encryption and decryption. The examples I've seen online all seem to indicate the PrivateKey property of the X509Certificate2 class should be readily available, but apparently I've missed something.
I've read similar questions here, such as Empty PrivateKey in x509certificate2, but none seem to resolve my issue. I've also read Eight tips for working with X.509 certificates in .NET by Paul Stovell, which was very enlightening, but ultimately didn't help. It did help me verify that the Private Key exists in the correct place and, as far as I can tell, with the correct permissions to be referenced by the x509Certificate2 class:
C:\Users\My.Name\AppData\Roaming\Microsoft\SystemCertificates\My\Keys
The name of the key file matches to the Subject Key Identifier on the certificate.
Edit:
The output of certutil -user -store my "Serial Number" is:
Serial Number: 4f000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Issuer: CN=Issuing CA, DC=My, DC=Domain, DC=us
NotBefore: 4/15/2016 2:15 PM
NotAfter: 4/15/2017 2:15 PM
Subject: E=my.name#my.domain.us, CN=My Name
Non-root Certificate
Template: Userv1, User v1
Cert Hash(sha1): ad ab 82 18 8a 17 4d 75 11 04 48 7c 43 43 d4 05 b9 74 c8 4c
Key Container = te-Userv1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Unique container name: fcbba1aa0xxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Provider = Microsoft Software Key Storage Provider
Encryption test passed
CertUtil: -store command completed successfully.
What "key" piece of information am I missing here? Why isn't the private key conveniently referenced from the X509Certificate2 object? How do I gain access to it?

Your certutil information shows Provider = Microsoft Software Key Storage Provider which means that the private key is stored under Windows CNG, instead of Windows CryptoAPI (CAPI).
.NET 4.6 added the GetRSAPrivateKey (extension) method to facilitate the otherwise breaking change of letting the PrivateKey property return something which was not an RSACryptoServiceProvider (or DSACryptoServiceProvider). If you have access to that method (I'm not sure what version of the framework PowerShell uses) then it would solve your problem.
Two things to be aware of, though:
GetRSAPrivateKey returns a unique Disposable object each time. You should put it in a using statement/manually call Dispose when finished with it (as opposed to cert.PrivateKey, which isn't unique, so shouldn't be Disposed).
The sign/verify//encrypt/decrypt methods have moved down to the RSA base class (albiet with slightly different, more forward-looking, signatures).

This may indicate one of the following:
1) the private key is stored in the Key Storage Provider (rather than legacy crypto service provider) which is poorly supported by .NET and not supported by PrivateKey property of X509Certificate2 class at all. You can check this by running the following command:
certutil -user -store my "<CertSerialNumber>"
2) the private key is missing.
HasPrivateKey property doesn't necessary reflect the actual picture and may True for non-existent keys or False for existing keys. Run the certutil command above to make sure if the key is presented.
In the case if private key is presented, but the bindings are broken, you can try to restore bindings by running the following command:
certutil -user -repairstore my "<CertSerialNumber>"

I resolved the problem.
For some reason, .NET framework can't import private keys from files, however, it can import from the built-in windows store, this is because the PrivateKey method only supports RSA and DSA keys are per the Microsoft Spec: read the notes under "Remarks" section.
Anyhow, to get a PrivateKey object to return the key info, You need to do the following:
1) Import your P12 file into the Windows Keystore by double-clicking it.
2) Select "Import" when prompted into "Current User"
3) Make sure you select "Make Key Exportable",
if this option is not available then your certificate has no private
key
.
4) Select "Automatically place into store"
5) Write the following code to retrieve your certificate:
Dim CertDataInfo As System.Security.Cryptography.X509Certificates.X509Certificate2
Dim Store As New System.Security.Cryptography.X509Certificates.X509Store("MY", Security.Cryptography.X509Certificates.StoreLocation.CurrentUser)
Store.Open(Security.Cryptography.X509Certificates.OpenFlags.ReadOnly)
Console.writeline (Store.Certificates.Count)
For I = 0 To Store.Certificates.Count - 1
If Store.Certificates.Item(I).FriendlyName = "Heider Sati's Cert(48F57XTHVE)" Then
CertDataInfo = Store.Certificates.Item(I)
End If
Console.writeline ("Cert: " & Store.Certificates.Item(I).FriendlyName)
Next
Store.Close()
If CertDataInfo.PrivateKey Is Nothing Then
MsgBox("NULL")
Else
MsgBox("YES")
End If
6) Run the code, if you get a YES then the private key is not NULL (or NOTHING), which is what you are looking for.
If you load the same certificate directly from the file, the Private key will always be NOTHING / NULL , but the HasPrivateKey will say YES, which means (I know there is a key, but yet, I don't know how to understand it. When you import it into the Windows store, then Windows does translate it into a .NET-Compatible format.
I hope this helps.

Related

Grant user permission to the private key

installing my WCF service I also install certificate with private key.
Since I will be running service as a different user, that user needs access to the private key. I extensively read other stackoverflow questions and they all suggest permission on private key file in file system needs to adjusted.
I do this by,
private static void AddUserPermissions(X509Certificate2 certificate, NTAccount user, StoreLocation storeLocation)
{
RSACryptoServiceProvider rsaProvider = (RSACryptoServiceProvider)certificate.PrivateKey;
// Find file
string keyPath = FindKeyLocation(rsaProvider.CspKeyContainerInfo.UniqueKeyContainerName, storeLocation);
FileInfo keyFileInfo = new FileInfo(keyPath);
// Create new FileSecurity
FileSecurity keyFileSecurity = keyFileInfo.GetAccessControl();
keyFileSecurity.AddAccessRule(new FileSystemAccessRule(user, FileSystemRights.Read, AccessControlType.Allow));
// Apply file security to the file
keyFileInfo.SetAccessControl(keyFileSecurity);
}
When I run my program and inspect the private key file I can see, for example "Network Service" has been added to the permissions list.
Great, that's working, but when WCF tries to use private key, it cannot access it.
Looking at certlm, certificate -> All Tasks -> Manage Private Keys..
I can see that my user is not on the list. Adding my user through GUI solves the issue, but I need to do it in code!!
Crypto Service Provider (Microsoft RSA SChannel Cryptographic Provider)
The keys are located in C:\ProgramData\Application Data\Microsoft\Crypto\RSA\MachineKeys and setting a normal file permission here is reflected in certlm.msc.
Crypto Next Generation (Microsoft Key Storage Provider)
The keys are located in C:\ProgramData\Application Data\Microsoft\Crypto\Keys and setting a normal file permission here is reflected in certlm.msc.
Summary
Ensure you modify the permissions on the right file in the right location.
Anyone who gets this far looking for a solution for ECDSA certificate keys, I found a way!
string keyUniqueName = (certificate.GetECDsaPrivateKey() as ECDsaCng)?.Key.UniqueName
?? (certificate.GetRSAPrivateKey() as RSACng)?.Key.UniqueName
?? throw new NotSupportedException("No ECDSA or RSA key found");
In the example code from the question, the FindKeyLocation gets passed rsaProvider.CspKeyContainerInfo.UniqueKeyContainerName, instead you would pass keyUniqueName as taken from my example.
One more good note, the accepted answer from Daniel Fisher lennybacon correctly documents where keys are stored based on where they were generated/installed. But you should use Environment.SpecialFolder.CommonApplicationData or Environment.SpecialFolder.ApplicationData for key file paths.
Also, I found my keys in c:\ProgramData\Microsoft\Crypto\, not c:\ProgramData\Application Data\Microsoft\Crypto\.

The requested operation is not supported in CngKey.Create

I'm trying to generate a self-signed certificate on the fly (programmatically) in a C# assembly (targeting .NET 4.0), to serve as a root CA to generate other certificates. The certificate doesn't need to be persisted in the Windows certificate store, I'll export it as a file.
Reading through this question (and in particular, #dthorpe's answer), I decided to give a try to CLR Security.
The CLR Security library put an extension method on CngKey class to generate a self-signed certificate, but I couldn't succeed in creating an instance of CngKey with:
var key = CngKey.Create(CngAlgorithm.Sha1); //same with Sha256, Sha512 and MD5
//or
var key = CngKey.Create(CngAlgorithm.Sha1, null, new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowExport,
KeyUsage = CngKeyUsages.AllUsages,
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
});
Any of these lines raises the exception:
System.Security.Cryptography.CryptographicException was unhandled
HResult=-2146893783
Message=The requested operation is not supported.
Source=System.Core
StackTrace:
at System.Security.Cryptography.NCryptNative.CreatePersistedKey(SafeNCryptProviderHandle provider, String algorithm, String name, CngKeyCreationOptions options)
at System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm, String keyName, CngKeyCreationParameters creationParameters)
at System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm)
at Tests.Program.Main(String[] args) at Program.cs:line 51
Searching through SO and the internet, I've checked the following:
I'm running a Windows 7 box (so it supports RPC as per MSDN)
Tried on a Windows Server 2012 box, same error
The process is running as admin (so it have access to all cert storages, anyway)
The services CNG Key Isolation and Remote Procedure Call (RPC) are running
Any help would be appreciated.
Small off-topic: during google search for this question found a site with HRESULT descriptions and handy search tool on SO and MSDN (I simply googled for your HRESULT code -2146893783)
I found a topic on MSDN which contains code failing with similar HRESULT, and the author provides a link to MSDN article about CNG:
NCRYPT_ALGORITHM_GROUP_PROPERTY
L"Algorithm Group"
A null-terminated Unicode string that contains the name of the object's algorithm group. This property only applies to keys. The following identifiers are returned by the Microsoft key storage provider:
NCRYPT_RSA_ALGORITHM_GROUP
"RSA", The RSA algorithm group.
NCRYPT_DH_ALGORITHM_GROUP
"DH", The Diffie-Hellman algorithm group.
NCRYPT_DSA_ALGORITHM_GROUP
"DSA", The DSA algorithm group.
NCRYPT_ECDSA_ALGORITHM_GROUP
"ECDSA", The elliptic curve DSA algorithm group.
NCRYPT_ECDH_ALGORITHM_GROUP
"ECDH", The elliptic curve Diffie-Hellman algorithm group.
Also I found an article on MSDN about CNG Key Storage Providers, which contains similar list of the algorithms:
Diffie-Hellman (DH)
Secret agreement and key exchange, 512 to 4096 in 64-bit increments
Digital Signature Algorithm (DSA)
Signatures, 512 to 1024 in 64-bit increments
Elliptic Curve Diffie-Hellman (ECDH)
Secret agreement and key exchange, P256, P384, P521
Elliptic Curve Digital Signature Algorithm (ECDSA)
Signatures, P256, P384, P521
RSA
Asymmetric encryption and signing, 512 to 16384 in 64-bit increments
So, as you've said that you've tried only Sha1, Sha256, Sha512 and MD5, maybe you simply use another algorithm from list available? You can find there ones mentioned above:
RSA
ECDsa
P256
P384
P521
ECDiffieHellman
P256
P384
P521
Here other developers successfully created one of them and was able to export it:
var cngKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256, null,
new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport });

Identification of private key in Pkcs11Interop.PDF library

I am trying to setup up an example application with the Pkcs11Interop.PDF extension. I am unfortunately getting a System.ArgumentNullException setting the ckaId. What would work here? I tried different numbers here which all gave me a System.ArgumentOutOfRangeException.
The setup is using the VirtualCryptoki-64-1.0.6.7.exe application to simulate a Smartcard.
The troubling call is:
pkcs11RsaSignature = new Pkcs11RsaSignature(libraryPath, tokenSerial, tokenLabel, pin, ckaLabel, ckaId, hashAlgorithm);
The hash algorithm I use is:
HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
Update after using the pkcs11-tool:
The content of the virtual card is:
C:\Program Files\OpenSC Project\OpenSC\tools>pkcs11-tool.exe --module "C:\windows\System32\vcki.dll" --list-slots --list-objects --login --pin 1234
Available slots:
Slot 0 (0xd47db04d): Virtual Smart Card Reader
token label: Virtual SC-A0101010101
token manuf: Cryptware
token model: VirtualSmartCard
token flags: rng, login required, PIN initialized, token initialized, other flags=0x200
serial num :
Using slot 0 with a present token (0xd47db04d)
Certificate Object, type = X.509 cert
label: ibisit
ID: 4a656e73204b6175666d616e6e
Public Key Object; RSA 1024 bits
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: encrypt, verify
Private Key Object; RSA
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: decrypt, sign
warning: PKCS11 function C_GetAttributeValue(ALWAYS_AUTHENTICATE) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
Public Key Object; RSA 1024 bits
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: encrypt, verify
Private Key Object; RSA
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: decrypt, sign
warning: PKCS11 function C_GetAttributeValue(ALWAYS_AUTHENTICATE) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
The parameters I use are:
string libraryPath = #"C:\Windows\System32\vcki.dll";
string tokenSerial = null;
string tokenLabel = #"Virtual SC-A0101010101";
string pin = #"1234";
string ckaLabel = #"ibisit";
string ckaId = "4a656e73204b6175666d616e6e";
HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
I am unfortunatly getting a Net.Pkcs11Interop.PDF.ObjectNotFoundException setting the ckaId. Changing the pin gives me a different exception so I am definetely accessing the right device here.
Update after switching to SoftHSM:
jariq stated that too many objects with the same id were there problem here. Unfortunately the Virtual Key Explorer would not let me delete any object so I switched to SoftHSM (which I was using at the beginning). I am getting "Certificate with label "ibis-it key" and id "A1B2" was not found". I suppose the problem here is that this virtual card only holds a RSA keypair not a certificate.
The output of the pkcs11-tool is:
C:\Program Files (x86)\OpenSC Project\OpenSC\tools>pkcs11-tool.exe --module "C:\SoftHSM\lib\libsofthsm.dll" --list-slots --list-objects --login --pin smart
Available slots:
Slot 0 (0x0): SoftHSM
token label: SoftHSM
token manuf: SoftHSM
token model: SoftHSM
token flags: rng, login required, PIN initialized, token initialized, other flags=0x40
serial num : 1
Using slot 0 with a present token (0x0)
Public Key Object; RSA 2048 bits
label: ibis-it key
ID: a1b2
Usage: verify
Private Key Object; RSA
label: ibis-it key
ID: a1b2
Usage: sign
The Problem is that SoftHSM only imports PKCS#8 (RSA) key pairs, so there will never be a certificate here. I suppose you have been using it with RSA key pairs but not with certificates.
Parameters passed to the constructor of Net.Pkcs11Interop.PDF.Pkcs11RsaSignature class identify following things:
which PKCS#11 library should be used (libraryPath)
which token/smartcard stores the private key (tokenSerial and/or tokenLabel)
which private key should be used for signing (ckaLabel and/or ckaId)
which hash algorithm should be used during signature creation (hashAlgorithm)
If you know which PKCS#11 library should be used to access the smartcard then you can determine correct values for the rest of the parameters i.e. by running pkcs11-tool utility which is bundled with OpenSC middleware. Please find below the exact command and the output generated for my testing card (important parts are highlighted with bold text):
C:\Program Files (x86)\OpenSC Project\OpenSC\tools>pkcs11-tool.exe --module cardos11.dll --list-slots --list-objects --login --pin 11111111
Available slots:
Slot 0 (0x1): SCM Microsystems Inc. SCR33x USB Smart Card Reader 0
token label : Pkcs11Interop
token manufacturer : www.atos.net/cardos
token model : CardOS V4.3B
token flags : rng, login required, PIN initialized, token initialized, other flags=0x800
hardware version : 102.63
firmware version : 200.8
serial num : 7BFF2737350B262C
Using slot 0 with a present token (0x1)
Private Key Object; RSA
label: John Doe
ID: ec5e50a889b888d600c6e13cb0fdf0c1
Usage: sign
Certificate Object, type = X.509 cert
label: John Doe
ID: ec5e50a889b888d600c6e13cb0fdf0c1
Based on this output these are the correct values of individual parameters for this card:
libraryPath="cardos11.dll"
tokenSerial="7BFF2737350B262C" and/or tokenLabel="Pkcs11Interop"
ckaLabel="John Doe" and/or ckaId="ec5e50a889b888d600c6e13cb0fdf0c1"
Hope this helps.
Update for ObjectNotFoundException:
You are getting ObjectNotFoundException because there are two private keys with the exactly same label and ID stored in your token and therefore Pkcs11RsaSignature class cannot be sure which one should be used for signature creation. Just delete or rename one of them and it should be working.
Update for SoftHSM:
You can import PKCS#8 private key to SoftHSM with softhsm.exe tool:
C:\SoftHSM\bin>softhsm.exe --import doe.key --slot 0 --label "John Doe" --pin 11111111 --id "ec5e50a889b888d600c6e13cb0fdf0c1"
The key pair has been imported to the token in slot 0.
You can import DER encoded X.509 certificate to SoftHSM with pkcs11-tool.exe tool:
C:\SoftHSM\bin>"c:\Program Files (x86)\OpenSC Project\OpenSC\tools\pkcs11-tool.exe" --module libsofthsm.dll --login --pin 11111111 --write-object doe.der --type cert --label "John Doe" --id "ec5e50a889b888d600c6e13cb0fdf0c1"
Using slot 0 with a present token (0x0)
Created certificate:
Certificate Object, type = X.509 cert
label: John Doe
ID: ec5e50a889b888d600c6e13cb0fdf0c1
Just make sure you will import the certificate with the same ID as the ID of private key.

"The specified network password is not correct" exception in X509Certificate2 constructor

I have a console application which loads an X509 Certificate from a byte array as follows:
var cert = new X509Certificate2(certificateContent, // byte[]
password, // string
X509KeyStorageFlags.PersistKeySet);
certificateContent is a byte[] representing the contents of a pfx file. This code works fine for a number of certificates I've tested. There is one certificate I'm testing, though, that causes this line to throw a CryptographicException with the message "The specified network password is not correct.", even though the password provided is correct.
The weird part is that I can use the same code in LinqPad to create a certificate from the same pfx file with the same password, and it works okay.
I've checked the call site in the console application in the debugger, and verified that the correct values are being passed in.
What could cause this constructor to throw this exception in a console app, but not in LinqPad using the same data, and work fine in both places for other certificates?
More Details
The certificates are stored in a database in Base64. The Console app reads the certificate from the DB, converts it from Base64 to a byte[], and then tries to create the X509Certificate2 object as above.
There are three certificates I've been testing with:
My personal Client Authentication certificate provided by my employer's CA.
A test certificate created by a colleague using his own self-signed CA.
My own test certificate created by myself using a self-signed CA.
Certificates 1 and 2 work as expected in both the console app and LinqPad.
Certificate 3 loads fine in LinqPad, but generates the error above if I try to use it in the console app.
There are two significant differences between certs 2 & 3.
Cert2 expires in 2016 and Cert3 expires in 2039
The private key associated with cert2 is 2048 bit. Cert3 is 1024 bits.
Could either of these differences result in the "specified network password is not correct" error? And why would all 3 certs work fine in LinqPad, but only 1 throw the error in the Console app?
In my case I had a self-signed certificate which I exported through MMC (microsoft management console) with a password. Loading this certificate went fine on my own machine (win10), but gave the exception with message "The specified network password is not correct" on a server machine (not win10).
What solved it in my case? When I exported the certificate I set a password using AES256-SHA256, but this certificate encryption was not supported on the server machine (after reading this dotnet runtime issue). When I changed it to TripleDES-SHA1 it worked fine locally and on the server as well.
Quote from the dotnet runtime issue:
WindowsCryptographicException: An internal error occurred probably means that PFXImportCertStore failed, returning null and calling SetLastError with NTE_FAIL.
Things that I'd speculate can cause it:
The PFX was created with a different encryption password and
integrity password, and the integrity password is what was provided
to the constructor. But that wouldn't be the case if the same PFX
file works on any Windows machine.
The PFX was created using the
PKCS12_PROTECT_TO_DOMAIN_SIDS and there's some sort of problem
talking to AD on the other side. I don't think we support this kind
of PFX. The PFX was generated using the guidance from
https://www.rfc-editor.org/rfc/rfc7292#appendix-B, namely, to use
PBES2+PBKDF2 for encrypting the private keys instead of
pbeWithSHAAnd3-KeyTripleDES-CBC In this case, the PFX will open on
Windows 10, but not prior versions. (At least, I think that's the
behavior I recall hitting with this case). It also, IIRC, won't open
on macOS.
PKCS#12 v1.1 also talks about encrypting and/or signing a
PFX using asymmetric cryptography rather than a password-based
scheme. But I don't know if even Win10 supports that, since I don't
see any controls for it in PFXExportCertStoreEx. So the only thing
that I can quickly think of that makes sense is you have a "new" PFX
and your test environment is Windows 10, but your production
environment is Win2012R2.
Hopefully this will help somebody:
User "S C" points out the following requirement for certificate passwords on Windows XP and Windows Server 2003.
0 < password.Length < 32
I have seen conflicting reports on whether 32 is allowed. I can confirm that I was using a 32 character password (an MD5 hash), and truncating it to 30 characters fixed the issue.
According to uGeeen's answer here, certificates created on Windows Server 2003 or Windows XP should have a password, or they will throw this exception.
Public Function sign(keystore As String, level As Integer, src As String, name As String,dest As String, sig As String, pass As String)
'Dim store As System.Security.Cryptography.X509Certificates.X509Store = New System.Security.Cryptography.X509Certificates.X509Store
'store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly)
'Dim sel As System.Security.Cryptography.X509Certificates.X509Certificate2Collection
' If sig <> "" And pass <> "" Then
Try
Dim y As Int16 = 200
' For i As Integer = 0 To sel.Count - 1
Dim pdfReader As PdfReader = New PdfReader(src)
Dim signedPdf = New FileStream(dest, FileMode.Create)
Try
Dim cert As X509Certificate2 = New X509Certificate2(sig, pass)
Dim cp As Org.BouncyCastle.X509.X509CertificateParser = New Org.BouncyCastle.X509.X509CertificateParser()
Dim chain As Org.BouncyCastle.X509.X509Certificate() = New Org.BouncyCastle.X509.X509Certificate() {cp.ReadCertificate(cert.RawData)}
Dim stamper As PdfStamper
stamper = PdfStamper.CreateSignature(pdfReader, signedPdf, "0"c, Nothing, True)
Dim signatureAppearance As PdfSignatureAppearance = stamper.SignatureAppearance
'signatureAppearance.SignatureGraphic = Image.GetInstance(pathToSignatureImage)
signatureAppearance.SetVisibleSignature(name)
signatureAppearance.CertificationLevel = level
Dim externalSignature As IExternalSignature = New X509Certificate2Signature(cert, "SHA-1")
' Dim digest As IExternalSignature = New BouncyCastleDigest
' signatureAppearance.s
'signatureAppearance.SetVisibleSignature(New Rectangle(50,50,50,
signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION
MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, Nothing, Nothing, Nothing, 0, CryptoStandard.CADES)
' MakeSignature.
Catch ex As Exception
MsgBox("Signature File Password is not correct for the user Id :" & error_userid)
'Exit Function
End Try
Catch ex As Exception
'MsgBox(ex.Message)
End Try
' End If
Return 0
End Function
The following code works for me.
var fileName = Path.Combine(rootPath, "cert.pfx");
if(!File.Exists(fileName)) {
throw new FileNotFoundException("Signing Certificate is missing!");
}
var cert = new X509Certificate2(fileName,"123456789");
where "123456789" is my password

Certificates - More than 1 serial number?

weird :
I use this code to find certificate via c# by this code :
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var certificates = store.Certificates;
X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindBySerialNumber, "the Serial Number", false);
So I tested to see by an existing certificate :
but it didn't worked (as mentioned in the linked question). ( Also tried , remove spaces , capital etc... - non worked).
But when I played with it a bit I found another serial location :
this time just pasted the exact phrase "4e a1 ae 96 ec 1a b6 82 4e f6 23 9a 16 04 1a 51" and it worked .
What is going on here ?
why there are 2 different serial numbers and what are the differences between them ?
p.s. , However , not all certificates has the "Authority Key Identifier " , so there's still a problem finding by other properties...
I Think found a solution. ( I can't believe it )
copying the thumbprint number via copy+paste and pasted in cmd :
what the hell is this char ?
So I typed it manually char by char - and all good (+remove the spaces).
But still why there are 2 serials ?
The authority key identifier is an additional extension you can add to a certificate which indicates the issuer certificate which signed it. It isn't required because there is already the Issuer field which indicates the subject name of the issuer certificate but having the extension allows to more accurately specify it.

Categories

Resources