0

I have a use case for a .NET application that stores certificates in a database. One of the requirements is for the application to reject certificates that contain private keys. The user will upload a certificate file (specifically .CER or .CRT) and the application will import it as an X509Certificate2 object so that I can check the HasPrivakeKey property.

I know that .PFX files can contain private keys, but is it possible for .CER or .CRT files to also contain private keys? If so, how can I generate a test certificate in order to test the application logic?

Timothy Schoonover
  • 3,195
  • 4
  • 29
  • 44

2 Answers2

4

First, .NET do not support PEM format with private key. But if such format is presented the following outcome is defined:

1) if certificate header/footer is first in the file, .NET will ignore the rest content of the file (e.g. private key information) and creates valid X509Certificate2 object without private key (because PKCS#1 and PKCS#8 keys are not supported by CryptoAPI functions which are called by a X509Certificate2 constructor. Though, there are functions to work with PKCS#1).

2) if private key header/footer is first in the file, .NET will raise exception about invalid certificate.

p.s. this combination is possible only when Base64 encoding is used and each section uses header and footer (e.g. -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----). It is impossible to combine them in binary form without using PKCS#12 container.


update: if you want to test it yourself, here is an example of such PEM file:

-----BEGIN CERTIFICATE-----
MIIEIDCCA+CgAwIBAgIUHSle8379VhDdbksPu2S6q+CkCMQwCQYHKoZIzjgEAzAjMSEwHwYDVQQD
ExhUb2tlbiBTaWduaW5nIFB1YmxpYyBLZXkwHhcNMTMwNzAzMTkzNDIzWhcNMTMwNzEwMTkzNDIz
WjAtMSswKQYDVQQDHiIAYgBiADEANAAxADkAYQAyAGMAZgBjADEAZQAwADAAOAAAMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQDbU9p4AwJy2RZxHYMXKalKv6K6cwrUB2RnFHZbelPgggfJyIZm
kL5pbB7u6tFYCBiNcMR6t8ItfVsi9iL33Uuluov7YZ3DPjRAVx4MZqXN3YR9bhzmOZpMgzKNxzoR
Kdhxy3qWYFAKdYZ9P1ln+9aUGJE3f1MuM7OPg1vWFUZ2VwIDAQABo4ICoDCCApwwDgYDVR0PAQH/
BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMIIB/wYDVR0gBIIB9jCCAfIwggHuBgorBgEEAYI3
MwMCMIIB3jCCAdoGCCsGAQUFBwICMIIBzB6CAcgATQBpAGMAcgBvAHMAbwBmAHQAIABkAG8AZQBz
ACAAbgBvAHQAIAB3AGEAcgByAGEAbgB0ACAAbwByACAAYwBsAGEAaQBtACAAdABoAGEAdAAgAHQA
aABlACAAaQBuAGYAbwByAG0AYQB0AGkAbwBuACAAZABpAHMAcABsAGEAeQBlAGQAIABpAG4AIAB0
AGgAaQBzACAAYwBlAHIAdABpAGYAaQBjAGEAdABlACAAaQBzACAAYwB1AHIAcgBlAG4AdAAgAG8A
cgAgAGEAYwBjAHUAcgBhAHQAZQAsACAAbgBvAHIAIABkAG8AZQBzACAAaQB0ACAAbQBhAGsAZQAg
AGEAbgB5ACAAZgBvAHIAbQBhAGwAIABzAHQAYQB0AGUAbQBlAG4AdABzACAAYQBiAG8AdQB0ACAA
dABoAGUAIABxAHUAYQBsAGkAdAB5ACAAbwByACAAcwBhAGYAZQB0AHkAIABvAGYAIABkAGEAdABh
ACAAcwBpAGcAbgBlAGQAIAB3AGkAdABoACAAdABoAGUAIABjAG8AcgByAGUAcwBwAG8AbgBkAGkA
bgBnACAAcAByAGkAdgBhAHQAZQAgAGsAZQB5AC4wUwYDVR0jBEwwSoAUaISoloVlkV/P4JGkgUGj
gzjrVSChJ6QlMCMxITAfBgNVBAMTGFRva2VuIFNpZ25pbmcgUHVibGljIEtleYIJAKs+FSwkyech
MB0GA1UdDgQWBBQQOhVxyI6GdpyHsij3PQU1ep0k7DAJBgcqhkjOOAQDAy8AMCwCFAPO2/xwhf37
xELxJhiMFEGvQXmgAhRNgAk/L2YWq1SlQ7Ax/XH5c8Ep0w==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANtT2ngDAnLZFnEdgxcpqUq/orpz
CtQHZGcUdlt6U+CCB8nIhmaQvmlsHu7q0VgIGI1wxHq3wi19WyL2IvfdS6W6i/thncM+NEBXHgxm
pc3dhH1uHOY5mkyDMo3HOhEp2HHLepZgUAp1hn0/WWf71pQYkTd/Uy4zs4+DW9YVRnZXAgMBAAEC
gYAKMnja0ZEAk/VGJxAcOJSlZAmFz6l2OC3D2SCzmhliO8lu6ULOa/ZeYmeBxisbg6zYjqCj7/04
LjbZhkYT7hcBNH6lns7yGZzkdly4y0Ud7tjsM+E31Y0Wb7jh/t3pvETUtTUxwhGT5nheiE3iDDj1
RQATdYxAL57Hr5R1+jc5SQJBAPjrJtZN21JJSlrpZIGB2KKrK6thy/oMWGsw1B3TyZWZt1Q06Fe3
MwTrJ1K4YWRyhRy9ib4yqQKMq0mcMqPCMGMCQQDhkTGDSG+lbZnhjop9YwmmJpxiaXZELphc9Tr8
Kf0f6vcfe4mh0OIwpatlqaZiCh5yQwv4GTuwGsRv199f8LJ9AkEA2qeuAPh5XUoWL8/vQrgt9Y7J
GI4a4PaxQM+utNjSrkBOQ4EKS+sYvQxYCZj/rH3QolN4yQO1ZRDucgXskd9GIwJBANk3n+2j6Nfu
trwuLxFWOSmGjxx6IMjB8jm6ckX5DWgaNkZcCgsJA3kDYQ2ylKZexjkUdcdCTWdmL3rg8JwMR2UC
QQCXhPLLIjtcdHzUHjy9dqzPyATduAmD23K7UPBDytFRyNcvUE+0Yfw3Lnvd/wATuUiFqHkhjD4v
qkICcfVum6Yi
-----END PRIVATE KEY-----

when you instantiate an X509Certificate2 object from this file, the call will succeed. Swap sections and you will see exception about invalid format.

Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • Do you happen to have any references? – Timothy Schoonover Sep 04 '15 at 16:45
  • Not sure if it is documented. I'm just a man who is experienced in Windows cryptography. I know about this behavior because there is `CryptStringToBinary` function call to convert Base64 to binary form and this function behaves as I described: only first section with header/footer is returned, the rest is ignored. – Crypt32 Sep 04 '15 at 16:50
1

An X509 certificate in PEM format is just a text file. It is not uncommon for people to append both the certificate and key to the same file, so you end up with something that looks like:

-----BEGIN CERTIFICATE-----
...certificate data...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
...key data...
-----END RSA PRIVATE KEY-----

Software expecting to read a private key will ignore everything outside the BEGIN RSA PRIVATE KEY/END RSA PRIVATE KEY lines, and software expecting to read a public certificate will ignore everything outside the BEGIN CERTIFICATE/END CERTIFICATE lines.

The easiest way to test for a private key in this case is just to look for the BEGIN RSA PRIVATE KEY marker.

I don't believe it is possible to concatenate DER encoded certificates in this fashion.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • If I have a .pfx file or a .cer + .pvk, can I use makecert or openssl to generate the PEM encode .cer file you describe above? I know I can get the RSA data using X509Certificate2.PrivateKey.ToXmlString() method and manually create the file, but I would prefer to have an automatic way. – Timothy Schoonover Sep 04 '15 at 13:27
  • I don't know that there's an automated way to create a file like this. In my experience, people just manually concatenate things. – larsks Sep 04 '15 at 13:39