Why does makecert not make a *valid* certificate? - c#

I want to create an X509 certificate for testing purposes. This certificate has to be shared by 3 developers on their local machines (i.e. all share the same certificate so we can use the same thumbprint).
So the private key in this certificate has to be exportable.
I create a certificate with the following command:
makecert -r -pe -n "CN=mytestsite.local" -b 01/01/2000 -e 01/01/2036 -ss my -sr localMachine -sky exchange localhost.cer
This certificate works fine, but the trouble is that the isValid argument has to be false when calling Certificates.Find...
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Find(
X509FindType.FindByThumbprint,
Config.PdfCertificateThumbprint,
false //********************* This has to be false.
).OfType<X509Certificate>().FirstOrDefault();
As soon as I set that IsValid property to True, my certificate is no longer returned by the Find method. Why would makecert generate an "invalid" certificate? Or how do I figure out why the certificate is deemed invalid?

Well, it's because it's not issued by a "Trusted Certificate Authority" like the "real" ssl certificates used on the internet. (for example issued by VeriSign)
What you can do locally to work is to add the certificate manually in the Trusted Certificates for your user and/or local machine. But this procedure must be done for everyone using it until you will obtain a valid SSL certificate issued by a CA (certificate authority).
But your question points to the scenario where it's for dev purposes only so what you can do is either manually add the certificate to Trusted or you can override the certificate validation mechanism in .Net and write code that will consider your certificate valid.

You might want to experiment with the following setting that can be used in client config to bypass the certificate validation process:
<serviceCertificate>
<authentication certificateValidationMode="None"
revocationMode="NoCheck" />
</serviceCertificate>

Related

recreate a certificate with the same credentials with makecert

I have a question, I have created my own certificate with this command :
1) makecert -r -pe -n "CN=MyTest" -sky exchange -sv MyTest.pvk MyTest.cer
2) pvk2pfx -pvk MyTest.pvk -spc MyTest.cer -pfx MyTest.pfx
So I have created a simple console application for crypt an decrypt text with this certificate, all work fine... example:
1) Crypt from text "1" I get "RLSym/wwReReo3GMM27ueIcMFRWHAB1AELnFVERnYuMbjBJi0QrW+oV2ADdJQ8VoZlShun0="
2) Decrypt from "RLSym/wwReReo3GMM27ueIcMFRWHAB1AELnFVERnYuMbjBJi0QrW+oV2ADdJQ8VoZlShun0="
I get "1".
Now I ricreate the certificate with the same command and password and use new one, when I try to Decrypt text create with The first certificate I receive an error, why? It is not possible to ricreate the certificate with the same credential and option if I lose the first one certificate? all my data saved on database is lost?
Thx.

Self-signed certificate for 127.0.0.1 not accepted by Chrome

Before Chrome 58, I was able to use a self-signed certificate with CN=127.0.0.1 and have Chrome accept it as long as it was in my Trusted Root certificate store.
Chrome 58 stopped accepting self-signed certificates that do not have alternative names. From my searching it seems that providing the alternative name should fix the issue, so I used this PowerShell command:
New-SelfSignedCertificate -Subject "Testing" -DnsName "127.0.0.1", "localhost" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(50)
I then placed a copy of that certificate in Trusted Root Authorities.
Now, if I browse to https://127.0.0.1 I get "NET::ERR_CERT_COMMON_NAME_INVALID", but it does accept the certificate if I browse to https://localhost.
I'm wondering if there is any way to generate a certificate from New-SelfSignedCertificate that will work the way it used to before Chrome 58, ie. just by adding it to Trusted Root.
Mainly I'm trying to avoid either having to add the certificate manually in Chrome's own certificate list, having to change any settings in Chrome to allow insecure localhost certificates, or having to create 2 certificates (1 to be root and 1 to be signed by root). I'm guessing I can get it to work with one of those workarounds but those were not necessary before, so I'd just like to confirm if Chrome 58 changed something else (other than requiring the subject alternative names) that breaks the process I was used to.

How to proper validate SSL certificate with X509Certificate2 on Mono and multiple platforms

I have to validate several SSL certificates in a none-browser application for HttpRequests and websocket connections which should run on IOS, Android and Linux.
When a connection via HTTPS happens I receive an array of X509Certificate2 objects where the bottom one is the server certificate and the most top one the root CA (hopefully). As an example, when I connect to https://google.com I receive 3 X509Certificate2 with the following SubjectName.Name:
0 : "CN=google.com, O=Google Inc, L=Mountain View, S=California, C=US"
1 : "CN=Google Internet Authority G2, O=Google Inc, C=US"
2 : "CN=GeoTrust Global CA, O=GeoTrust Inc., C=US"
I now need to verify the certificate validation with the given information and the following verifications:
Chain-of-trust verification
Hostname verification
Certificate revocation verification
What I tried and not understood and failed:
When I call the X509Certificate2.Verify() method on each certificate independently it returns false everytime. I also do not understand why it can return anything else then false because the verification happens independently. Instead the complete chain, meaning all certificates, should be checked as far as I understood the theory.
I then used the X509Chain class:
foreach (X509Certificate2 cert in allthreecerts)
{
X509Chain chain = new X509Chain();
X509ChainPolicy chainPolicy = new X509ChainPolicy()
{
RevocationMode = X509RevocationMode.Offline,
RevocationFlag = X509RevocationFlag.EntireChain
};
chain.ChainPolicy = chainPolicy;
if (!chain.Build(cert))
{
foreach (X509ChainElement chainElement in chain.ChainElements)
{
foreach (X509ChainStatus chainStatus in chainElement.ChainElementStatus)
{
Debug.WriteLine(chainStatus.StatusInformation);
}
}
}
}
This prints out RevocationStatusUnknown and OfflineRevocation for each certificate.
Again, I do not understand why this should work since a chain is build which each certificate independently. Should not be the parent certificate be the Issue of the child certificate all down to the root CA?
What I think I need somehow but do not know how.
In order to verify the certificate revocation all clients need to provide a certificate revocation list. Where do I get such a list and where to I load it and tell the chain to use this local list?
The same problem is the Chain-of-trust verification since the last certification should be a root certification and must be one of the clients trusted root CA's. So I have to load, for example, all root CA which are shipped with Firefox and check if the root CA is one of them? What is the best way to do that?
[Update]
I made a copy paste mistake in the google.com example where I pasted two identical certificate subject names.
When a connection via HTTPS happens I receive an array of X509Certificate2 objects where the bottom one is the server certificate and the most top one the root CA (hopefully).
If you really did get root ca certificate from SSL server then the SSL server is not configured correctly. It should return its server certificate and the whole chain Except the root CA certificate.
When I call the X509Certificate2.Verify() method on each certificate independently it returns false everytime.
As stated in the documentation of X509Certificate2.Verify method This method builds a simple chain for the certificate and applies the base policy to that chain. Now what is the base policy I do not know. But on of the default values of ChainPolicy is RevocationMode = X509RevocationMode.Online. As for Mono the source of this method can be found here. The source of Mono implementation of X509Chain can be found here.
This prints out RevocationStatusUnknown and OfflineRevocation for each certificate.
What else should it print when you specified RevocationMode = X509RevocationMode.Offline and there are no CRLs or OCSP responses in cache (probably)?
In order to verify the certificate revocation all clients need to provide a certificate revocation list. Where do I get such a list and where to I load it and tell the chain to use this local list?
Each certificate (well except root ca certificate) in the chain contains a link (or links) to CRL. .NET and most likely Mono too has an implementation that will find link to CRL in the certificate, will download it and will check if the certificate is not revoked.
The same problem is the Chain-of-trust verification since the last certification should be a root certification and must be one of the clients trusted root CA's. So I have to load, for example, all root CA which are shipped with Firefox and check if the root CA is one of them? What is the best way to do that?
No, RevocationFlag = X509RevocationFlag.EntireChain will do that for you using some store that mono uses. I don't know if it is the Firefox store but on Linux it has its own store and you can import root ca certificates from Firefox store. Check ChainElements and see for yourself what certificates did it find.
I would suggest that you build the chain with SSL server certificate only (as this will check all certs in the chain upwards) with RevocationMode = X509RevocationMode.Online and RevocationFlag = X509RevocationFlag.EntireChain. I would also try to set ExtraStore property of X509Chain to a list of certificate you got from SSL server. After the Build method I would chceck ChainStatus property of X509Chain object which is an array of statuses. I would pick all statuses that have not set X509ChainStatus.Status to NoError. If there will be any such status I would throw and log each X509ChainStatus.Status and X509ChainStatus.StatusInformation.
HTH

How to get Thumbprint or Public Key of Issuer Certificate?

We have created a self signed CA certificate which we use to sign other certificates for SSL purposes. These certificates will be installed in other servers which we do not have access to and will be strictly to communicate with other clients like mobile applications.
When these clients (written in .NET) make a request to the servers using HTTPS we get the "Invalid certificate received from server" error because the CA cert is not a trusted CA on that client.
We want to bypass this security using the ServicePointManager.ServerCertificateValidationCallback, but only if the certificate being used was signed by our CA certificate.
I can check the certificate.Issuer, but that can easily be spoofed by anyone. How can I get the Thumbprint or Public Key of the Issuer certificate of the invalid certificate? If I can get access to that I can easily compare it to the one I know is valid and ignore the certificate error and continue on with the request.
UPDATE
I think I am getting closer. It looks like what we're looking to do is not doable so went a slightly different direction.
Using the X509Chain we can verify whether the certificate is a child of the CA using the code below:
var caCert = new X509Certificate2(#"[path]\MyCA.cer");
var newChain = new X509Chain();
newChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
newChain.ChainPolicy.ExtraStore.Add(caCert);
var res = newChain.Build(certInQuestion);
Build() still returns false (as expected because the CA is not trusted on the client), but now newChain.ChainStatus[0].Status is returning UntrustedRoot. Based on my testing this means the chain validated because if I supply a different CA Certificate it fails with InvalidChain.
In conclusion, that tells me that if the Status is UntrustedRoot, the certificate was created with our CA certificate and thus it's valid, anything else it's a fake one!
Are my assumptions correct?
That's the wrong solution. You should install your self-signed certificate as a trusted CA certificate in all clients, or better still just get it signed by a CA that is already trusted. Don't write code for this.
Here are some links with info and some tools to generate free public and private keys:
https://www.igolder.com/pgp/
https://www.igolder.com/pgp/generate-key/
I think EJP's solution is the most acceptable. #EJP, can you give us some examples of applications that can help us become a "tiny CA".
I'm not entirely sure this this is what you're looking for but it might push you in the right direction. Here's a PowerShell script I use to find a cert that I just created and extracted using MAKECERT.EXE & CERTMGR.EXE:
# get certificate thumbprint
$appCertificate = Get-PfxCertificate -FilePath $certificateFullPath
Write-Host " .. adding certificate to local machine root" -ForegroundColor Gray
& $ExeCertManager /add $certificateFullPath /s /r localMachine root
Write-Host " Certificate installed on local machine" -ForegroundColor Gray
Write-Host " .. exporting private key for certificate" -ForegroundColor Gray
Get-ChildItem cert:\\localmachine\my | Where-Object {$_.Thumbprint -eq $appCertificate.Thumbprint} | ForEach-Object {
$CertPfxName = (Get-Item -Path $certificateFullPath).BaseName
}
It seems that a possible solution would be that you could email the certificate, according to this answer: https://stackoverflow.com/a/4473799/674700.

SSLStream example - how do I get certificates that work?

I'm using the SSLStream example from msdn here. The client code "seems" to work fine, as I can connect to google and it at least gets past authentication, but the server doesn't.
From the comments from the msdn page, I used the procedure on this page to generate my own private key, but it just doesn't work. I get an exception of System.NotSupportedException: The server mode SSL must use a certificate with the associated private key. So I'm pretty sure whatever I'm doing is wrong.
So my question is simple: how do I get/generate keys that will work for my own little example program from msdn? It can be self-signed, whatever, but I'm too new to SSL to even know what exactly I need. All I want to do is to run the example as-given, except for specifying my own certificates for my local server. And it'd be great to know what I'd have to install on my 2nd machine if I just want to communicate between the two of them too (so it's not a 100% localhost example).
Personally I see this as a flaw in the example document. It should say "to run this, you need to do A, B, C, etc," but it doesn't.
You can get the example to work even with self-signed certificates. I've extracted the commands from the makecert tutorial that you're using with minor modifications:
makecert -sv RootCATest.pvk -r -n "CN=FakeServerName" RootCATest.cer
makecert -ic RootCATest.cer -iv RootCATest.pvk -n "CN=FakeServerName" -sv TempCert.pvk -pe -sky exchange TempCert.cer
cert2spc TempCert.cer TempCert.spc
pvkimprt -pfx TempCert.spc TempCert.pvk
makecert and cert2psc can be found in your Microsoft SDKs\Window\v7.0A\Bin folder.
The pvkImport.exe installer can be downloaded here (Provided by #Jospeph and VirusTotal verified). This used to be downloadable from the Microsoft Site, but they have since taken it down. Alternatively, #Dweeberly pointed us to a new Microsoft-provided replacement, pvk2pfx.
For this next step make sure that you select to EXPORT the private key when the dialog from pvkimprt comes up:
pvkimprt -pfx TempCert.spc TempCert.pvk
pvkimprt will prompt you for a password when you elect to include the private key. You will need to provide this password later when you import the generated .pfx file into the personal store of your server machine
Next, import RootCATest.cer into your Computer store's Trusted Root Certification Authorities (on both the server and client). Notice that the certificate is issued to FakeServerName. This must match the server name that the SslTcpClient expects: sslStream.AuthenticateAsClient(serverName), where serverName is the value of the second argument passed to SslTcpClient.exe.
When your client connects, the server presents a certificate that tells the client "I'm FakeServerName". The client will accept this claim if the client machine trusts the CA that issued the certificate, which is achieved by importing RootCATest.cer into the client's Trusted Root Certification Authorities.
Finally, you need to import the private key that the server is going to use into the server machine's Personal store. This step is important because it addresses The server mode SSL must use a certificate with the associated private key.. This is achieved by importing the .pfx file that you generated earlier. Make sure that you change the file type filter to "all files" so that you can see the .pfx file that you generated:
The sample code provided by MSDN uses port 443 (which is the standard ssl port). Since I created console applications, I changed the port used by the sample classes to 8080:
SslTcpServer:
TcpListener listener = new TcpListener(IPAddress.Any, 8080);
SslTcpClient:
TcpClient client = new TcpClient(machineName, 8080);
Here's the output:
you would launch your server like this:
SslTcpServer.exe TempCert.cer
from the client, you would connect like this:
SslTcpClient.exe <ip to your server> FakeServerName
generate your certificate using this command:
makecert -r -pe -n "CN=localhost" -m 12 -sky CertSubject -ss my serverCert.cer
and then from client connect to the server like this (assuming we are using MSDN example you mentioned):
SslTcpClient.RunClient ("localhost", "CertSubject");
you will get validation errors in ValidateServerCertificate() call - but that's expected - you are using self-signed certificate. Just return true there.
UPDATE:
I disagree with Tung's suggestion of adding self-signed certificate into the client's Trusted Root Certification Authorities. I think it can cause issues later on if you plan to distribute/support your software. For example, client might reinstall windows, or move his profile to another PC, or whatever - and understanding WHY your software suddenly stopped working will be a pain (again, i'm talking long-term - a year or two from now, when you completely forget this little "trick").
Instead i would rather suggest to "hardcode" your certificate (by comparing subject and thumbprint) into client's logic, something like this:
X509Certificate2 certificate = (X509Certificate2)cert;
if (certificate.Subject.StartsWith("CN=FAKE_SERVER_WHATEVER") &&
!string.IsNullOrEmpty(certificate.Thumbprint) &&
certificate.Thumbprint.ToLower() == "11c4446c572a9918ced3618728b91b3a07982787")
{
return true;
}
return false;
As the Microsoft link to download pvkimprt is broken and I am a fan of OpenSSL here I leave two solutions with OpenSSL.
VARIANT #1 - Self Signed Certificate
First you will need download OpenSSL and this configuration file. #Tung has said you can use perfectly self-signed certificate. Copy the downloaded configuration file in the same folder where you will run OpenSSL commands.
Lets generate the private key and certificate of Certification Authority:
openssl req -x509 -config openssl.cnf -newkey rsa:4096 -sha256 -out ssl-cacert.pem -keyout ssl-cakey.pem -outform PEM
*Use -nodes parameter to omit the passphrase, but for safety reasons personally I do not recommend it.
If you desire inspect the information of CA certificate, execute the follow command:
openssl x509 -purpose -in ssl-cacert.pem -inform PEM
Lets create the certificate request, Common Name must be set with the machine name:
openssl req -config openssl.cnf -newkey rsa:2048 -keyout ssl-serverkey.pem -sha256 -out ssl-server.csr -outform PEM
*Same note for -nodes parameter.
If you want inspect the certificate request information execute the command:
openssl req -text -noout -verify -in ssl-server.csr
Sign the certificate request with the generated CA certificate:
openssl x509 -req -days 365 -CA ssl-cacert.pem -CAkey ssl-cakey.pem -CAcreateserial -in ssl-server.csr -out ssl-server-certificate.pem
Lets make the self-signed certificate with PFX format:
openssl pkcs12 -export -out ssl-certificate.pfx -inkey ssl-serverkey.pem -in ssl-server-certificate.pem -certfile ssl-cacert.pem -name "SSL Self Signed Certificate"
Now you should import the .pfx certificate.
Double click on ssl-certificate.pfx file.
Select "Local Machine" option and Next.
Type the password and select the checkbox "Mark this key as exportable."
Select the radio button "Place all certificates in the following store".
Select Personal store and click in Next.
With this steps must work.
VARIANT #2 - Generate CA Certificate and Server Certificate
Personally I prefer this solution over the first because only I have to distribute the Root CA certificate to the clients.
First download this configuration file.
We will generate the Root CA certificate with the corresponding private key:
openssl req -x509 -config openssl.cnf -newkey rsa:4096 -sha256 -keyout ssl-cakey.pem -out ssl-cacert.pem -outform PEM
Lets check certificate properties:
openssl x509 -purpose -in ssl-cacert.pem -inform PEM
The information must show should look like this:
Certificate purposes:
SSL client : No
SSL client CA : Yes
SSL server : No
SSL server CA : Yes
Netscape SSL server : No
Netscape SSL server CA : Yes
S/MIME signing : No
S/MIME signing CA : Yes
S/MIME encryption : No
S/MIME encryption CA : Yes
CRL signing : Yes
CRL signing CA : Yes
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : Yes
Time Stamp signing : No
Time Stamp signing CA : Yes
-----BEGIN CERTIFICATE-----
MIIGLjCCBBagAwIBAgIJANCzs7UBFJMpMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
...
im1yDnB5nPwkPwZ9eRmlzIc6OaLZcfbFfSeSw8/ipKZcEJ1u+EFrB0JhuSbeLXtQ
N/8=
-----END CERTIFICATE-----
Create the certificate request with the following command:
openssl req -config openssl.cnf -newkey rsa:2048 -sha256 -keyout ssl-serverkey.pem -out ssl-servercert.csr -outform PEM
It's very important set the Common Name with the machine name of server.
Verify the information of this certificate request:
openssl req -text -noout -verify -in ssl-servercert.csr
The information shows must have the following format, check that the CN field in the section Subject is the name of server machine.
verify OK
Certificate Request:
Data:
Version: 0 (0x0)
Subject: C=US, ST=..., L=..., O=..., OU=..., CN=SERVERNAME
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:aa:92:bd:87:75:18:6c:c0:23:3f:0b:5a:46:1a:
...
fe:13
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Key Identifier:
7E:7D:79:F4:CD:71:0E:90:3A:9A:F8:3F:83:7D:89:90:4D:D4:F0:12
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment, Data Encipherment
Signature Algorithm: sha256WithRSAEncryption
34:e1:b4:db:b2:87:cc:11:3e:85:3c:ed:ac:8d:d9:43:ae:b0:
...
56:84:29:f9
Create the certificates folder:
mkdir certificates
Create the database index file:
Windows: type NUL > index.txt
Unix: touch index.txt
Create the serial.txt file where is stored the current serial number:
echo '01' > serial.txt
Create the server certificate signing the certificate request for 2 years with the command. You will be prompted the pass phrase of CA certificate depending if you used -nodes parameter:
openssl ca -config openssl.cnf -days 730 -policy signing_policy -extensions v3_req -out ssl-servercert.pem -infiles ssl-servercert.csr
Then is displayed a text with the format:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'US'
stateOrProvinceName :ASN.1 12:'...'
localityName :ASN.1 12:'...'
organizationName :ASN.1 12:'...'
organizationalUnitName:ASN.1 12:'...'
commonName :ASN.1 12:'SERVERNAME'
Certificate is to be certified until Jul 4 23:26:59 2018 GMT (730 days)
Sign the certificate? [y/n]:
Select y and will prompted the follow text, select y one more time:
1 out of 1 certificate requests certified, commit? [y/n]
Export the generated certificate to PFX format:
openssl pkcs12 -export -out ssl-certificate.pfx -inkey ssl-serverkey.pem -in ssl-servercert.pem -name "SSL Signed Certificate"
You will need do the follow steps to enable SSL without problem:
On Server Machine:
Import the Root CA certificate (ssl-cacert.pem file) on Trusted Root Certification Authorities store selecting Computer account.
Import Server Certificate for SSL (ssl-certificate.pfx file) on Personal store selecting Computer account.
On Client Machines:
In each client machine you will need import the Root CA certificate (ssl-cacert.pem file) on Trusted Root Certification Authorities store selecting Computer account.
Feel free to make any changes or suggestions.

Categories

Resources