How does keytool work: privateKeyEntry and trustedCertEntry? - ssl

Ok first I'll say what I know about how SSL works.
Most of all I need to understand how the client certificate works, because I was setting up mTls istio gateway and had trouble with it.
So, firstly I need key-pair, private and public. If I understood correctly, i create them next with the following command:
keytool -genkey -alias myKeyPair -keyalg RSA -keysize 2048 store.jks
If I look in the keystore there will be one PrivateKeyEntry. Next I create request for certificate:
keytool -certreq -keyalg RSA -alias myKeyPair -file request.csr -keystore store.jks
After a while, I received a signed client certificate client.cer and certificate trusted CA ca.cer.
Now there are questions:
First, i tried to send request from curl. But curl needs private key which stored in jks. I dont have openssl and cant get it, so i extract private key using java code. But the result was always the same: istio gateway: peer did not return a certificate
Second, I tried using java.net.http.HttpClient with SSLContext setting containing my jks. And it worked. But firstly I had to put ca.cer and client.cer in my jks. And this I cannot understand: why do I need to put ca.cer? Because without ca.cer stored in store.jks i have error certificate validation.
Also when I put client.cer in jks, keytool displays a warning: Certificate already exists in keystore under alias <myKeyPair>. Do you still want to add it? Why does it think privateKeyEntry and trustedCertEntry are the same?

Meta: a poor answer due to the vagueness of the question, but much too long for comments.
First, i tried to send request from curl. But curl needs private key which stored in jks. I dont have openssl and cant get it, so i extract private key using java code. But the result was always the same: istio gateway: peer did not return a certificate
Either your 'extract' was wrong or the way you provided it to curl was wrong, or both, and you described neither. First, to be clear, like any client doing client auth, curl actually needs the privatekey and certificate, and usually any applicable chain certificate(s). What file format(s) you must use, and whether a single combined file or separate files (added:) or none at all as it turns out, depends on the curl build you are using: curl supports seven SSL/TLS implementations, and the ways to provide client-key-and-cert vary among them. Use curl -V (uppercase) to see how it was built, then look at the portions of the man page, on your system or on the web, that apply to that implementation.
Second, I tried using java.net.http.HttpClient with SSLContext setting containing my jks. And it worked. But firstly I had to put ca.cer and client.cer in my jks. And this I cannot understand: why do I need to put ca.cer? Because without ca.cer stored in store.jks i have error certificate validation.
Possibly it is a 'chain' or intermediate CA cert. An SSL/TLS client (like a server) is required by the standard to send any chain cert(s) needed to connect the end-entity (client or server) cert to normally a 'root' in the receiver's truststore. (Since RFC5280, and confirmed by RFC8446, it is actually possible to use an anchor that isn't a root, but it is rare to do so.) Possibly it is even a combination of chain cert(s) and root (or anchor). (Some reliers sometimes can still build the chain to validate a cert when it wasn't correctly sent; browsers especially tend to do this, but most servers not.)
Look at it and see. If it is PEM format you can see with any text display or edit program how many certs it contains. If it is a single cert you can display the details with keytool -printcert -file $file -- or since you already imported it, keytool -list -v -alias name -keystore $ks [-storepass $pw]. If it is multiple certs in PEM format you can break it apart with an editor and display each one separately. If it is multiple certs in DER format you won't be able to handle that easily, but fortunately that format is rarely used. openssl x509 [-inform pem|der] -text -noout is also commonly used to display cert file details, but you say you can't use it.
Also when I put client.cer in jks, keytool displays a warning: Certificate already exists in keystore under alias . Do you still want to add it? Why does it think privateKeyEntry and trustedCertEntry are the same?
If you only did this once, and correctly, it shouldn't. Given separate cert
files, you should keytool -importcert the client cert to the alias of the privatekey entry you created, and used to create the -certreq, which in your example is myKeyPair, after you keytool -importcert any needed chain cert(s), and optionally the root or anchor, (each) to a different alias. (It is often convenient, but not required, to use simple words like root, imed, imed2 etc. as aliases.) Alternatively, if you have the whole chain as a single file, either PEM sequence or DER sequence, or 'p7b' (a dummy PKCS7 SignedData with no data and no signature, commonly used as a container for one or more certs, such as a chain) either PEM or DER, you should import that chain in a single operation.

Related

How do I create a usable certificate-store from several files

We have a process to request a signed cert from a CA and we get back 3 files:
cert.cer, cert.key, and cert.p12
I now need to build a valid/usable cert store from those files. I have copies of the CA & intermediate certs locally on my server. So I'm trying to import everything by using keytool. But I end up with a store full of about 100 certs plus the cert for my server. But when I try to use them I'm getting an error that the server cert is not valid unless the signing certs are also in the store. Basically there's no chain even though I the server cert says it was issued by the intermediate cert in the store. I use the following commands to import my certs and ca trusts.
keytool -v -importkeystore -srckeystore "cacerts.p12" -srcstorepass "$CA_PASS" -srcstoretype "pkcs12" -destkeystore "$KEYSTORE_NAME" -deststorepass "$STORE_PW" -deststoretype "jks";
keytool -importkeystore -v -srckeystore "$CERT_NAME.p12" -srcstorepass "$STORE_PW" -srcstoretype "pkcs12" -destkeystore "$KEYSTORE_NAME" -deststorepass "$STORE_PW" -deststoretype "jks";
I'm not sure what step I'm missing. This is an Ubuntu 20.04 server.
How do I create a usable certificate-store ..
Usable is the keyword here - what are you trying to use the keystore for? (usually - SSL, client authentication or WS-Security)
getting an error that the server cert is not valid unless the signing certs are also in the store
There are different files for different purpose:
cert.cer - a public key with a CA-signed certificate
cert.key - a private key
cert.p12 - a keystore, may contain the private key, may contain the public key with its certificate, usually contains both (private key, public key, certificate). So - better validate what does the p12 keystore really contain.
The PKCS#12 keystore usually can be used as it is, often no need to import into a separate JKS. However - depends on the software.
BTW - maybe you could get a keystore-explorer, an opensource gem software giving you a great overview when not understanding the details or cli options.
Basically there's no chain even though I the server cert says it was issued by the intermediate cert in the store
Depends on the usage, but the best practice is having the CA root or its intermediate certificates imported in the truststore.
To import a CA reply in the keytool, you simply import a CA reply (issued certificate) with the same alias name as its private key. I'm not sure if you can create a whole certificate chain this way, you may have a look at the mentioned keystore-explorer to be sure.

keytool error: java.lang.Exception: Public keys in reply and keystore don't match errors when importing crt

Before I start, I have looked at 2 other questions:
keytool error: java.lang.Exception: Public keys in reply and keystore don't match
And
java.lang.Exception: Public keys in reply and keystore don't match
But I believe that the error comes in the way I am generating the csr that I submit to my provider(Digicert). I will detail my commands below, notice that this is the way our department has always done this and up till this point I can't understand why this is not working (I am also not capacitated at all to do system administration things, but this landed on me)
First - Generating the keystore
keytool -genkey -alias aliasItem -keyalg RSA -sigalg SHA256withRSA -keysize 2048 -keypass <password> -dname "CN=server.domain.whatever, OU=IT, O=SOME NAME, L=City ST=State C=COUNTRY" -keystore keycerts -storepass <password>
I changed the important items as you might well assume for security concerns. Afterwards:
keytool -keycerts -keyalg RSA -sigalg SHA256withRSA -v -alias aliasItem -file outputfile.csr -keystore keycerts
After I get the csr, I submit it to my provider, there is no copy/paste error in this case since I import the file directly. They provide two .crt files, one from the service provider, and one for the server i am requesting it for. After I move these files to my server and attempt to import the service provider's .crt to the keystore I get an error, this is the command I use for importing the .crt to the keystore:
keytool -import -v -alias aliasItem -file <Provider>.crt -keystore keycerts
Which outputs the error:
keytool error: java.lang.Exception: Public keys in reply and keystore don't match
java.lang.Exception: Public keys in reply and keystore don't match
at sun.security.tools.KeyTool.establishCertChain(KeyTool.java:2688)
at sun.security.tools.KeyTool.installReply(KeyTool.java:1940)
at sun.security.tools.KeyTool.doCommands(KeyTool.java:855)
at sun.security.tools.KeyTool.run(KeyTool.java:194)
at sun.security.tools.KeyTool.main(KeyTool.java:188)
I have tried changing some parts of the scripts a total of 8 times now, all using the notes and documentation provided by me with no positive results. What strikes me as odd is that this server is identical in all aspects to another one of our test servers, for which I had done this before with no issues. I am still trying to do different things to solve my issue, but due to my limited knowledge in this I believe that there has got to be something I am doing from the beginning that might be wrong.
Any input will be greatly appreciated.
The second command for generating the car seems to be incorrect because "-keycerts" is an illegal parameter. IT must be "-certreq" .
Now, the error states that the private key does not match with the certificate which you are trying to install. Kindly check the below
a) Please make sure you are trying to use the same keystore file which you used to generate the csr
b) Please check while importing you are using the correct alias name. The alias should match with the one you mentioned while generating the keystore file.
c) Please make sure you are importing the correct server certificate and not the intermediate or root. Digicert must have provided you all the three files namely, Server certificate (CN of this certificate must match with the one when you generated the csr file and this is the one which needs to be imported), Intermediate and root.
If the above steps don't work then you have to generate the new csr and keystore file and ask Digicert to Reissue the certificates. They will do it for you free of cost.

Certificate chaining while signing a jar file

How to introduce one level of certificate chain while signing a jar file? What I am trying to do is,
Sign a jar with generated private key.
Generate a CSR (Certificate Signing Request)
Generate a CA signed certificate where CA is self sign CA
import CA in jcontrol and browser and run the java web start application.
but unfortunately it didn't work and gave security warning.
Please suggest some good approach.
TLDR: get the cert first
Java codesigning uses the PKCS7/CMS signature format, wbich is based on X.509/PKIX public-key certificates. The sequence you posted uses the privatekey as it if magically appeared, but that is impossible. I'm going to bet your sequence actually was:
‍0. keytool -genkeypair
Although the name focusses attention on 'generate keypair', this single command actually does three things: generate a keypair; create a dummy, self-signed certificate for the public half of that keypair; and store both the private half of the keypair and the dummy certificate together in a keystore (usually a file). Note the java.security.KeyStore API does not support storing a privatekey -- or a publickey or raw keypair -- by itself, only a privatekey combined with a certificate chain. (The dummy cert, by itself, constitutes a trivial chain.) For concreteness I will call this dummy cert A.
‍1. Sign a jar with generated private key = jarsigner
You specify to jarsigner a privatekey entry in a keystore, but as above that entry actually contains the cert chain as well, in this case the dummy cert. The PKCS7/CMS signature contains a raw PKC signature (RSA, DSA, or ECDSA) computed using the privatekey, plus the cert which matches that privatekey and thus can be used to verify the signature, plus any chain cert(s) needed to validate the signing certificate. You can see this if you look at the contents of the META-INF/<alias>.{RSA,DSA,EC} entry in the resulting jar.
‍2. Generate a CSR (Certificate Signing Request) = keytool -certreq
‍3. Generate a CA signed certificate where CA is self sign CA
How you did this depends on your CA, but doesn't matter here. To be exact, a CA may have a self-signed cert for the public half of the CA keypair, but as above it actually signs certs it issues using the private half. For concreteness I will call the cert issued by the CA for the signing key B, and the CA's own self-signed cert C.
‍4. import CA in jcontrol and browser and run the java web start application.
I'm not sure if you mean B or C here. But neither will help in verifying the signature done in step 1, because that signature contains and specifies A as the 'signer' cert, i.e. the cert to be used to verify the signature. B is a cert fort for the same publickey, and could be used to verify the signature if the signature specified cert B, but it doesn't, and can't because B didn't even exist when the signature was created.
The correct sequence is:
-genkeypair
-certreq
use CSR to get cert B from CA. You don't actually need C here, although you might get it for convenicnce or reference.
-importcert cert B into the privatekey entry. (If you defaulted the -genkeypair alias to mykey you can default -importcert also, otherwise you must specify the same one.) This replaces the dummy cert A with the 'real' cert B in the privatekey entry. (Be careful not to import cert B to a different alias, or different keystore; this often doesn't give an error. but the result won't work correctly.)
If the 'real' cert B needs one or more chain cert(s) to validate, as is (always?) true for certs from real public CAs like Digicert, GoDaddy, LetsEncrypt, etc, such chain cert(s) must be either included in step 4 (as a combined file, often a 'p7b' or 'p7c' file), or separately imported prior to step 4 into a separate trustedcert entry or entries. However, your in-house CA does not need any chain cert(s).
now jarsigner -- so the signature includes cert B (and its chain certs if any)
    A. separately, cert C must be included in the truststore(s) used by the client system(s); this can be done anytime: after, during or before steps 1-5. For webstart, I believe putting it in Java Control Panel should be sufficient; AFAIK webstart isn't validated by the browser. Applets may have been, but applets haven't worked in browsers for several years or in Oracle Java higher than 8.

Is Keytool certificate is for free?

In our application we are creating Client and server that talks with each other using SSL authentication. I done a POC which downloads certificate using following commnand:
keytool -genkey -keystore yourKEYSTORE -keyalg RSA
Need to know if the certificate created by above command is free to use ?
It's free to use but it is self-signed, which makes it basically useless unless you can go through all the steps required to get the peers to trust it.
Failing that, you need to generate a certificate request from this, get it signed, and import the resulting signed certificate into that keystore using the same alias you used here.

Using curl -with --cert

I'm using cUrl to request data from a corporate website site using a .cer certificate that they sent me.
This is the command:
cUrl --header "Content-Type: text/xml;charset=UTF-8" \
--data #bustaRequestISEE2015ConsultazioneAttestazione.xml \
-o bustaResponseISEE2015ConsultazioneAttestazione.xml \
--cert ./caaffabisrl.cer \
https://istitutonazionaleprevidenzasociale.spcoop.gov.it/PD
When I run it, I get this error message:
curl: (58) could not load PEM client certificate, OpenSSL error error:0906D06C:PEM routines:PEM_read_bio:no start line, (no key found, wrong pass phrase, or wro
ng file format?)
Is there anybody who can help me?
Tks, Cristiano.
It is not possible to connect to a TLS server with curl using only a client certificate, without the client private key. Either they forgot to send you the private key file, or, what they sent you was not the client certificate but the server certificate for verification.
The first thing I would try is using --cacert instead of --cert. That is, tell curl that this is the server's certificate that curl can use to verify that the server is who you think it is.
You can also try removing --cert and not using --cacert, and you will probably get an error that the server is not trusted. Then add the --insecure argument and see if that works. I would not keep that argument, as then you have no proof of who you are talking to.
My guess is that it is the server cert, and that using --cacert instead of --cert will solve the problem.
My guess is that your certificate file is a DER encoded binary certificate instead of base-64 encoded certificate. To covert the from binary to base-64, you can use OpenSSL.
openssl x509 -inform der -in certificate.cer -out certificate.pem
I always forget all the arguments and have the following site bookmarked, as it gives examples of how to convert pretty much any certificate format. https://www.sslshopper.com/ssl-converter.html
First, you need to specify whether you're expected to perform two-way TLS/SSL or MTLS (mutual TLS). This would typically be the reason for sending a certificate. If they sent the server certificate, but you can connect to the server with a browser, you can down load the certificate. If their server is configured to send the server certificate and CA chain, then you can get the entire chain in a single request using "openssl s_client -connect [hostname:port] -showcerts". Save the certs in the console to a file, copying the cert blob(s) to individual cert files (cert1.crt, cert2.crt). However, if they are expecting MTLS and attempting to send a client certificate to you, either you've already generated a private key and CSR (certificate signing request) and send them the CSR. They would have then signed a certificate with their CA certificate using the CSR. The cert they returned would then need to be paired with the private key used to generate the CSR. They should not be generating the public/private key pair and sending them over mail. The private key should be stored security on the one system used to establish the connection. If it's one-way (server ssl only), then your client system (assuming it's not the browser), needs a truststore file, with the CA certificate chain installed and set to trusted. If the platform is Java, read Java's keytool documentation. Note, a keystore is for your systems public/private keypair. A truststore is for the CA certificates that you trust to sign public certificates that your system should trust as being authentic. You need to read any of the PKI x509 overviews by DigiCert, SSLABS, Sectigo, etc.