I want to generate a self signed trusted certificate and a csr and sign the csr with trusted certificate created. I am trying it with keytool. In the first step of creating a trusted certificate using the below command
keytool -genkey -alias mytrustCA -keyalg RSA -keystore keystore.jks -keysize 1024
where it puts the certificate into keystore. How can I store it to a file ? and when I list the contents using
keytool -list -v -keystore cert/test.keystore
Certificate created with above "genkey" command creates with entry type as "PrivateKeyEntry", how can create a trusted Cert Entry ?
In your first command, you have used the -genkey option to generate the keystore named keystore.jks.
To export the certificate in .CER format file, you will need to use the -export option of the keytool.
An example is:
keytool -v -export -file mytrustCA.cer -keystore keystore.jks -alias mytrustCA
This will generate a file named mytrustCA.cer
To generate a certificate request to send to a CA for obtaining a signed certificate, you will need to use the -certreq option of keytool.
An example is:
keytool -v -certreq -keystore keystore.jks -alias mytrustCA
This will ask for the keystore password and on successful authentication, it will show the certificate request as given below (a sample).
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBtDCCAR0CAQAwdDELMAkGA1UEBhMCSU4xFDASBgNVBAgTC01haGFyYXNodHJhMQ8wDQYDVQQH
EwZNdW1iYWkxEjAQBgNVBAoTCU1pbmRzdG9ybTEUMBIGA1UECxMLRW5naW5lZXJpbmcxFDASBgNV
BAMTC1JvbWluIElyYW5pMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqOLEumwLHlzIUAPD6
Ab1pVp84mhSNCCcUKInZbSdiDYnKSr46EjEw0PtZOVPJbM4ZG3bZsOboYr0YfViJi41o4yJICFAZ
8wCQQxPK/4N8MPV7C5WDH28kRKGH/Pc2e7CxV+as573I34QmkINk7fEyERMDwP/WgmrcKZgL0sfy
ewIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAlcpjOUZFP9ixskXSA7HNlioWwjbL9f9rQskJ9rK8
kGLJ1td+mqqm20yo/JrKCzZjOMqr/aL6Zw2dkoyU34T9HnR2Bs3SgKn6wlYsYEVvVBk71Ec6PeTi
e+fhfNQEHsj4wuB4qixO3s1jtsLDy+DpTzYguszczwxXGFVNuk+y2VY=
-----END NEW CERTIFICATE REQUEST-----
You will need to send this Certificate REquest or paste it into the Digital Certificate signer webpage. Alternately, you can even redirect this output to a file instead of the console as follows:
keytool -v -certreq -keystore keystore.jks -alias mytrustCA > mycertreq.txt
This is a command line example without any interactive prompts, may be easier to use this way and document all commands in a text file.
Create JavaKeyStore file and a self-signed certificate key
keytool -genkey -alias server -keyalg RSA -keysize 2048 -sigalg SHA256withRSA -storetype JKS \
-keystore my.server.com.jks -storepass mypwd -keypass mypwd \
-dname "CN=my.server.com, OU=EastCoast, O=MyComp Ltd, L=New York, ST=, C=US" \
-ext "SAN=dns:my.server.com,dns:www.my.server.com,ip:11.22.33.44" \
-validity 7200
keytool -keystore my.server.com.jks -storepass mypwd -list -v
You can use this keystore(.jks) file already in Tomcat but browsers give a self-signed certificate warning. Give SubjectAlternativeName extension argument with one or more dns names and optional ip address.
Create CertificateSigningRequest file
keytool -certreq -alias server -file my.server.com.csr \
-keystore my.server.com.jks -storepass mypwd \
-ext "SAN=dns:my.server.com,dns:www.my.server.com,ip:11.22.33.44" \
keytool -printcertreq -file my.server.com.csr
Send .csr file to CertificateAuthority(CA) operator for signing, you should later receive a certificate(cer) file. You must give here SubjectAlternativeName extension argument second time.
Import Certificate file to a keystore
keytool -import -trustcacerts -keystore my.server.com.jks -storepass mypwd \
-alias server -file my.server.com.cer
This command pairs your private key and a public certificate with a trusted valid CA authority. Browsers should not give a certificate warning anymore.
Import intermediate CA certs
keytool.exe -importcert -trustcacerts -file SomeCA.cer -alias someca -keystore my.server.com.jks -storepass mypwd
keytool.exe -importcert -trustcacerts -file SomeCAIssuing.cer -alias somecaissuing -keystore my.server.com.jks -storepass mypwd
This imports CA issuing certificates, you may need to do this before importing your certificate file(.cer).
Your hostname certificate may have an expiration date, so once about to expire soon create a new signing request(.csr) file from the keystore, send new csr file to CA authority, import new certificate(.cer) file.
You most likely are using jks keystore in Tomcat web server so here is tomcat/conf/server.xml https connector examples.
Tomcat 9+
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000" maxThreads="150"
URIEncoding="UTF-8" useBodyEncodingForURI="true" maxHttpHeaderSize="65536"
compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,text/json,application/json"
SSLEnabled="true" scheme="https" secure="true">
<SSLHostConfig protocols="all">
<Certificate certificateKeystoreFile="my.server.com.jks" certificateKeystoreType="JKS"
certificateKeystorePassword="mypwd" certificateKeyAlias="server" />
</SSLHostConfig>
</Connector>
Tomcat8.5, if older than 8.0 you may need to drop ciphers arguments
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
disableUploadTimeout="true" useBodyEncodingForURI="true"
acceptCount="300" acceptorThreadCount="2" maxThreads="400"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,text/json,application/json"
compression="off" compressionMinSize="2048"
keystoreFile="my.server.com.jks" keystorePass="mypwd" keyAlias="server"
SSLEnabled="true" scheme="https" secure="true" clientAuth="false"
sslEnabledProtocols="+TLSv1,+TLSv1.1,+TLSv1.2"
ciphers="
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_RSA_WITH_RC4_128_SHA,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
SSL_RSA_WITH_RC4_128_MD5,
SSL_RSA_WITH_RC4_128_SHA,
TLS_EMPTY_RENEGOTIATION_INFO_SCSVF
"
/>
Related
We currently run a multi region cassandra cluster in AWS. It runs in four regions, 12 nodes per region. It runs without node to node encryption (or client encryption either). We are trying to enable inter datacenter node to node encryption. However, when we flip encryption over we get an exception that nodes are unable to gossip with any peers.
It could possibly be that we didn't build our jks keystore/truststores correctly (more on how we built these files below). But, we additionally do not see intra datacenter communication working (which should be set to unencrypted communication). Additionally, cqlsh cannot connect to the node either; even though we have (by default) client_auth_required set to false.
ERROR [main] 2019-08-15 18:46:32,241 CassandraDaemon.java:749 - Exception encountered during startup
java.lang.RuntimeException: Unable to gossip with any peers
at org.apache.cassandra.gms.Gossiper.doShadowRound(Gossiper.java:1435) ~[apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.StorageService.checkForEndpointCollision(StorageService.java:566) ~[apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.StorageService.prepareToJoin(StorageService.java:823) ~[apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.StorageService.initServer(StorageService.java:683) ~[apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.StorageService.initServer(StorageService.java:632) ~[apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:388) [apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.CassandraDaemon.activate(CassandraDaemon.java:620) [apache-cassandra-3.11.4.jar:3.11.4]
at org.apache.cassandra.service.CassandraDaemon.main(CassandraDaemon.java:732) [apache-cassandra-3.11.4.jar:3.11.4]
INFO [main] 2019-08-15 18:47:07,384 YamlConfigurationLoader.java:89 - Configuration location: file:/etc/cassandra/cassandra.yaml
Something to note is that this error message occurs after a few minutes of the node being up. (i.e. there is a delay between start up before this exception is thrown).
Information about our cassandra setup
cassandra version: 3.11.4
JDK version: openjdk-8.
Linux: Ubuntu 18.04 (bionic).
cassandra.yaml
endpoint_snitch: Ec2MultiRegionSnitch
server_encryption_options:
internode_encryption: dc
keystore: <omitted>
keystore_password: <omitted>
truststore: <omitted>
truststore_password: <omitted>
client_encryption_options:
enabled: false
cassandra-rackdc.properties
prefer_local=true
No obvious errors with SSH output
When starting cassandra with JVM_OPTS="$JVM_OPTS -Djavax.net.debug=ssl" added to cassandra-env.sh we see SSL logs printed to stdout (Note: Subject and Issuer were omitted on purpose).
found key for : cassy-us-west-2
adding as trusted cert:
Subject: ...
Issuer: ...
Algorithm: RSA; Serial number: 0xdad28d843fc73325d4c1a75207d4e74
Valid from Fri May 27 00:00:00 UTC 2016 until Tue May 26 23:59:59 UTC 2026
...
trigger seeding of SecureRandom
done seeding SecureRandom
Looking at Java SE SSL/TLS connection debugging, this looks correct. But to note, we see this series of messages (along with the RSA key signature output) repeated several times in rapid fire. We never observe any messages about the trust store being added; however that might be something that occurs only on client initiation (?)
Additionally, we do see cassandra report that the Encrypted Messaging service has been started.
INFO [main] 2019-08-15 18:45:31,022 MessagingService.java:704 - Starting Encrypted Messaging Service on SSL port 7001
Doesn't appear to be a cassandra.yaml configuration problem
We can bring the node back online by simply configuring internode_encryption: none. This action seems to rule out a broadcast_address or rpc_address configuration problem.
How we built our keystore/truststores
We followed the basic template datastax docs for preparing SSL certificates. One minor difference was that our private key and CSRs were generated using openssl. One per each region (we plan to share key/signed certs across nodes in regions). This was created using a command template as:
openssl req -new -newkey rsa:2048 -out cassy-<region>.csr -keyout cassy-<region>.key -config cassy-<region>.conf -subj "..." -nodes -sha256
The generated CSR was then signed by an internal root CA. Because we generated our files using openssl, we had to build our jks files by importing our certs into them.
Commands to generate truststore
We distribute this one file to all nodes.
keytool -importcert
-keystore generic-server-truststore.jks
-alias rootCa
-file rootCa.crt
-noprompt
-keypass omitted
-storepass omitted
Commands to generate keystore
This was done one per region; but essentially we created a keystore with keytool, then deleted the key entry and then imported our key entry using keytool from a pkcs12 file.
keytool -genkeypair -keyalg RSA -alias cassy-${region} -keystore cassy-${region}.jks -storepass omitted -keypass omitted -validity 365 -keysize 2048 -dname "..."
keytool -delete -alias cassy-${region} -keystore cassy-${region}.jks -storepass omitted
openssl pkcs12 -export -in signed_certs/${region}.pem -inkey keys/cassandra.${region}.key -name cassy-${region} -out ${region}.p12
keytool -importkeystore -deststorepass omitted -destkeystore cassy-${region}.jks -srckeystore ${region}.p12 -srcstoretype PKCS12
keytool -importcert -keystore cassy-${region}.jks -alias rootCa -file ca.crt -noprompt -keypass omitted -storepass omitted
Looking back at this, I don't remember why we used keytool to generate a keypair/keystore, then deleted and imported. I think it was because the keytool importkeystore command refused to run if the keystore didn't already exist.
ca.crt and pem file
The ca.crt file contains the root certificate and the intermediate certificate that was used to sign the CSR. The pem file contains the signed CSR returned to us, the intermediate cert, and the root CA (in that order).
openssl verify ca.crt and pem
openssl verify -CAfile ca.crt us-west-2.pem
signed_certs/us-west-2.pem: OK
Command output after enabling encryption
nodetool status (output truncated)
Datacenter: us-east
===================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
?N 52.44.11.221 ? 256 25.4% null 1c
...
?N 52.204.232.195 ? 256 23.2% null 1d
Datacenter: us-west-2
=====================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
?N 34.209.2.144 ? 256 26.5% null 2c
UN 52.40.32.177 105.99 GiB 256 23.7% null 2c
?N 34.210.109.203 ? 256 24.7% null 2a
...
With the online node being the node with encryption set.
cqlsh to localhost
cassy-node6:~$ cqlsh
Connection error: ('Unable to connect to any servers', {'127.0.0.1': error(111, "Tried connecting to [('127.0.0.1', 9042)]. Last error: Connection refused")})
cqlsh to remote node
Remote node is a node with encryption enabled
cassy-node6:~$ cqlsh 10.0.2.7
Connection error: ('Unable to connect to any servers', {'10.0.2.7': error(111, "Tried connecting to [('10.0.2.7', 9042)]. Last error: Connection refused")})
Behavior we expected
We expected for the node to report that the other regions were all down, as they need to be handled over encryption. So that works as expected; however, cqlsh and intra datacenter peers being reported as unreachable is unexpected.
Specifically, we expected the node to still show peer nodes within the same datacenter as up and normal, regardless if there is a cert issue/error. We also expected cqlsh to continue to work.
Lastly, we are also trying to figure out if we have a jks certificate problem.
I have a simple chain setup and can successfully verify in this case:
$ openssl version
OpenSSL 1.0.2m 2 Nov 2017
$ openssl verify -CAfile chain.pem cert.pem
cert.pem: OK
However I get errors in these cases:
$ openssl verify -CAfile ca-cert.pem cert.pem
cert.pem: C = US...
error 2 at 1 depth lookup:unable to get issuer certificate
Specifically the unable to get issuer certificate.
Also get it here:
$ openssl verify chain.pem
chain.pem: C = US...
error 20 at 0 depth lookup:unable to get local issuer certificate
$ openssl verify cert.pem
cert.pem: C...
error 20 at 0 depth lookup:unable to get local issuer certificate
Finally, I get it in Node.js when I pass the keys to an HTTPS server:
events.js:193
throw er; // Unhandled 'error' event
^
Error: unable to get local issuer certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
at emitNone (events.js:115:13)
at TLSSocket.emit (events.js:218:7)
at TLSSocket._finishInit (_tls_wrap.js:637:8)
I tried passing it with { key, cert, ca }, but still same error.
Wondering how to go about debugging this or what the fix is to get an HTTPS server running.
If I use a pfx file I get the following:
events.js:193
throw er; // Unhandled 'error' event
^
Error: self signed certificate in certificate chain
at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
at emitNone (events.js:115:13)
at TLSSocket.emit (events.js:218:7)
at TLSSocket._finishInit (_tls_wrap.js:637:8)
If I leave only the cert.pem in the cert file, and make the ca attribute be the ca-cert.pem, it gives:
Error: unable to verify the first certificate
at TLSSocket.<anonymous> (_tls_wrap.js:1108:38)
at emitNone (events.js:105:13)
at TLSSocket.emit (events.js:207:7)
at TLSSocket._finishInit (_tls_wrap.js:638:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:468:38)
Not sure what to do.
Here they say:
OpenSSL is unable to find a local certificate for the issuer (or the issuer of the first certificate in the chain received from the web server during the TLS handshake) with which to verify the signature(s).
Not sure what that means.
This error means the certificate path or chain is broken and you are missing certificate files.
-
https://wiki.zimbra.com/wiki/Fix_depth_lookup:unable_to_get_issuer_certificate
Update
Slightly more help:
This problem is usually indicated by log messages saying something like "unable to get local issuer certificate" or "self signed certificate". When a certificate is verified its root CA must be "trusted" by OpenSSL this typically means that the CA certificate must be placed in a directory or file and the relevant program configured to read it. The OpenSSL program 'verify' behaves in a similar way and issues similar error messages: check the verify(1) program manual page for more information.
https://www.openssl.org/docs/faq.html#USER6
But still doesn't help very much.
Looks like Node.js is using a 1.0.2l instead of 1.0.2m but doesn't seem like a big deal.
$ node -pe process.versions | grep openssl
openssl: '1.0.2l'
Update 2
Weird, I get this when I make a request from Node.js:
Uncaught Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
at TLSSocket._finishInit (_tls_wrap.js:637:8)
But when I go to the browser, I don't see the "Proceed with caution" page, and can successfully log a request in Node.js. Maybe that helps somewhat. Please help :D
(This answer extracted from X509_verify_cert at crypto/x509/x509_vfy.c:204, in openssl-1.0.2m)
The OpenSSL verify application verifies a certificate in the following way: It builds the certificate chain starting with the target certificate, and tracing the issuer chain, searching any untrusted certificates supplied along with the target cert first. Upon failing to find an untrusted issuer cert, OpenSSL switches to the trusted certificate store and continues building the chain. This process stops when
an issuer is not found in the trusted store.
a self-signed certificate is encountered.
the max-verify depth is encountered.
At this point we have a chain that may end prematurely (if we failed to find an issuer, or if we exceeded the verify depth).
OpenSSL then scans over each trusted certificate on the chain looking for SSLv3 extensions that specify the purpose of the trusted certificate. If the trusted certificate has the right "trust" attributes for the "purpose" of the verification operation (or has the anyExtendedKeyUsage attribute) the chain is trusted. (Forgive the hand-wave on trust attributes, that part of the code was difficult to read.)
So lets test it out. First, let's repro the OP's error cases:
#
echo "Making Root CA..."
openssl req -newkey rsa:4096 -nodes -keyout ca-key.pem -sha384 -x509 -days 365 -out ca-crt.pem -subj /C=XX/ST=YY/O=RootCA
echo "Making Intermediate CA..."
openssl req -newkey rsa:3072 -nodes -keyout int-key.pem -new -sha384 -out int-csr.pem -subj /C=XX/ST=YY/O=IntermediateCA
openssl x509 -req -days 360 -in int-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out int-crt.pem
echo "Making User Cert..."
openssl req -newkey rsa:2048 -nodes -keyout usr-key.pem -new -sha256 -out usr-csr.pem -subj /C=XX/ST=YY/O=LockCmpXchg8b
openssl x509 -req -days 360 -in usr-csr.pem -CA int-crt.pem -CAkey int-key.pem -CAcreateserial -out usr-crt.pem
echo ""
echo "Making Chain..."
cat ca-crt.pem int-crt.pem > chain.pem
echo ""
echo "Verfying UserCert via RootCA..."
openssl verify -CAfile ca-crt.pem usr-crt.pem
echo ""
echo "Verfying UserCert via IntermediateCA..."
openssl verify -CAfile int-crt.pem usr-crt.pem
echo ""
echo "Verfying UserCert via chain..."
openssl verify -CAfile chain.pem usr-crt.pem
yields
[... Skipping OpenSSL KeyGen / CertGen verbosity ...]
Making Chain...
Verfying UserCert via RootCA...
usr-crt.pem: C = XX, ST = YY, O = LockCmpXchg8b
error 20 at 0 depth lookup:unable to get local issuer certificate
Verfying UserCert via IntermediateCA...
usr-crt.pem: C = XX, ST = YY, O = IntermediateCA
error 2 at 1 depth lookup:unable to get issuer certificate
Verfying UserCert via chain...
usr-crt.pem: OK
Now, lets use the -addtrust option of openssl x509 to make sure we have one of the acceptable trust attributes on the intermediate CA (call this one IntermediateCAWithTrust; we'll use it to sign AnotherUserCert.):
echo ""
echo "Alternate Intermedate CA (using -addtrust anyExtendedKeyUsage)"
echo ""
echo "Making IntermediateCAWithTrust..."
openssl req -newkey rsa:3072 -nodes -keyout int-key2.pem -new -sha384 -out int-csr2.pem -subj /C=XX/ST=YY/O=IntermediateCAWithTrust
openssl x509 -req -days 360 -in int-csr2.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out int-crt2.pem -addtrust anyExtendedKeyUsage
echo "Making AnotherUser Cert..."
openssl req -newkey rsa:2048 -nodes -keyout usr-key2.pem -new -sha256 -out usr-csr2.pem -subj /C=XX/ST=YY/O=LockCmpXchg8b_2
openssl x509 -req -days 360 -in usr-csr2.pem -CA int-crt2.pem -CAkey int-key2.pem -CAcreateserial -out usr-crt2.pem
echo ""
echo "Verfying AnotherUserCert via IntermediateCAWithTrust..."
openssl verify -CAfile int-crt2.pem usr-crt2.pem
This yields
Alternate Intermedate CA (using -addtrust anyExtendedKeyUsage)
Making IntermediateCAWithTrust...
[... Snip more OpenSSL generation output ...]
Making AnotherUser Cert...
[... Snip more OpenSSL generation output ...]
Verfying AnotherUserCert via IntermediateCAWithTrust...
usr-crt2.pem: OK
Hey look! we just successfully verified AnotherUserCert via the IntermediateCAWithTrust, even though we didn't supply the whole chain. The key to this difference is that any one of the trusted certificates in the chain had an appropriate trust attribute for the verify operation.
Looking a little closer (via openssl x509 -in ca-crt.pem -noout -text), our CA certificate has
X509v3 Basic Constraints:
CA:TRUE
which I would imagine OpenSSL treats as a general "may verify for any purpose" extension. The new IntermediateCAWithTrust does not have X509v3 Basic Constraints, but instead has
Trusted Uses:
Any Extended Key Usage
No Rejected Uses.
For more info in the -addtrust option, and the types of trust attributes that can be added, see https://www.openssl.org/docs/manmaster/man1/x509.html#TRUST_SETTINGS
Near the bottom of that page is a concise summary of the preceding discussion:
The basicConstraints extension CA flag is used to determine whether
the certificate can be used as a CA. If the CA flag is true then it is
a CA, if the CA flag is false then it is not a CA. All CAs should have
the CA flag set to true.
If the basicConstraints extension is absent then the certificate is
considered to be a "possible CA" other extensions are checked
according to the intended use of the certificate. A warning is given
in this case because the certificate should really not be regarded as
a CA: however it is allowed to be a CA to work around some broken
software.
So, in short, make sure your intermediate CAs are properly CAs (in their X509v3 Basic Constraints). This seems an excellent tutorial (and it explicitly generates the intermediate CA as a CA): https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
As a backup plan, you can always supply the whole chain, or you can make your intermediate CAs with the -addtrust hack.
https://letsencrypt.org/ is really easy to use and free. Also, run node without SSL on a local HTTP port and use NGINX as a HTTPS proxy.
sudo apt-get install certbot nginx
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl on;
ssl_certificate /etc/letsencrypt/live/host.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/host.com/privkey.pem;
access_log /var/log/nginx/host.access.log;
error_log /var/log/nginx/host.error.log;
server_name _;
gzip on;
gzip_proxied any;
gzip_types text/css text/javascript text/xml text/plain application/javascript application/x-javascript application/json;
location / {
include /etc/nginx/proxy_params;
proxy_pass http://localhost:8080;
proxy_read_timeout 90s;
proxy_redirect http://localhost:8080 https://www.host.com;
}
}
I have figured out a set of SSL commands (using openssl tool) to create the private key, CSR and ultimately, the certificate.
Root key and certificate:
openssl genrsa -aes256 -out private/ca.key.pem 4096
openssl req -config openssl.cnf -key private/ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
Client SSL key and certificate:
openssl genrsa -aes256 -out private/client.key.pem 2048
openssl req -config openssl.cnf -key private/client.key.pem -new -sha256 -out csr/client.csr.pem
openssl ca -config openssl.cnf -extensions usr_cert -days 375 -notext -md sha256 -in csr/client.csr.pem -out certs/client.cert.pem
However, at the third step of client SSL certificate creation, the tool asks couple of further (interactive) questions as follows:
openssl ca -config openssl.cnf -extensions server_cert -days 375 -notext -md sha256 -in csr/www.example.com.csr.pem -out certs/www.example.com.cert.pem
Using configuration from openssl.cnf
Enter pass phrase for /Users/myssl/private/ca.key.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 4096 (0x1000)
Validity
Not Before: Oct 31 18:43:27 2017 GMT
Not After : Nov 10 18:43:27 2018 GMT
Subject:
countryName = IN
stateOrProvinceName = KAR
organizationName = ABC
commonName = www.example.com
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Cert Type:
SSL Server
Netscape Comment:
OpenSSL Generated Server Certificate
X509v3 Subject Key Identifier:
D7:12:DB:DE:D1:92:52:15:E5:AD:83:84:B6:7F:9F:CF:97:06:91:8E
X509v3 Authority Key Identifier:
keyid:1C:5F:64:BB:8B:E5:8E:A7:DE:00:E2:D7:1A:D5:1D:52:53:5E:59:32
DirName:/C=IN/ST=KAR/L=BLR/O=ABC/CN=www.example.com
serial:C3:D6:EE:B0:FE:28:76:14
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
Certificate is to be certified until Nov 10 18:43:27 2018 GMT (375 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
The above two prompts goes off only after adding the '-batch' option to the command line. However, I did not find any mention or help on the 'man openssl' page.
I am using openssl version 1.0.2k. Is this option deprecated or it is hidden? It still works and makes the above command non-interactive.
I have installed openSSl with cygwin and generated self singed certifikate like this
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out csr.pem
openssl x509 -req -in csr.pem -signkey server.key -out cert.pem
i tried to test if i was succesfull so i created a basic tls connection with node
var tls=require("tls");
var fs=require("fs");
var serverOptions={
key:fs.readFileSync('C:/cygwin64/home/Matej/csr.pem'),
cert:fs.readFileSync('C:/cygwin64/home/Matej/cert.pem')
}
var server=tls.createServer(serverOptions);
server.listen(4001)
but it threw error
c.context.setKey(options.key);
^
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
i quite dont understand what does the erron mean. Did i generated the key/certifikate wrong?
I tried looking here but my pem files does not contain ^M .My cert looks like
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
and csr
-----BEGIN CERTIFICATE REQUEST-----
...
-----END CERTIFICATE REQUEST-----
I used IIS 7 on Windows Server Enterprise 2008 to generate a self-signed cert for use with IIS (basically one-click button).
However, even when I export and add this cert to a windows client's curl-ca-bundle.crt, neither it nor openssl.exe will not verify the cert correctly:
openssl s_client -CAfile curl-ca-bundle.crt -showcerts -connect myserver.ad.pri:443
CONNECTED(00000003)
depth=0 /CN=myserver.ad.pri
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 /CN=myserver.ad.pri
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/CN=myserver.ad.pri
i:/CN=myserver.ad.pri
-----BEGIN CERTIFICATE-----
MIIDADCCAeigAwIBAgIQTi9gdBLdo6pJ1h4Zljr/wzANBgkqhkiG9w0BAQUFADAp
....
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=myserver.ad.pri
issuer=/CN=myserver.ad.pri
---
No client certificate CA names sent
---
SSL handshake has read 924 bytes and written 444 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES128-SHA
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
Start Time: 1377728216
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---
read:errno=104
I used IE to export the cert to Base-64 Encoded, which is openssl-readable as PEM:
openssl x509 -inform PEM -in myserver.crt -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
4e:2f:60:74:12:dd:a3:aa:49:d6:1e:19:96:3a:ff:c3
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=myserver.ad.pri
Validity
Not Before: Aug 26 15:38:46 2013 GMT
Not After : Aug 26 00:00:00 2014 GMT
Subject: CN=myserver.ad.pri
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (2048 bit)
Modulus (2048 bit):
....
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage:
Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
Signature Algorithm: sha1WithRSAEncryption
...
-----BEGIN CERTIFICATE-----
....
openssl/curl with the same curl-ca-bundle.crt will verify certs from google.com:443 etc. just fine.
I also ran into this (and I'm very surprised more people haven't.) when I couldn't get a NodeJS HTTP(s) client to connect to an IIS instance with a self-signed-certificate on it (one created through IIS manager) Just got the dreaded' unable to verify the first certificate error!
It seems that this is because the certificates that IISManager creates for this purpose specify some 'Key Usage' extensions; 'Key Encipherment' and 'Data Encipherment'.
It turns out that when openssl encounters a certificate that specifies 'Key Usage' but fails to specify the 'certSign' usage then the openssl code will discount that certificate as a possible CA certificate even if it has been correctly provided to the openssl code (meaning it is unable to verify the certificate against said absent CA!).
(See the logic here https://github.com/openssl/openssl/blob/6f0ac0e2f27d9240516edb9a23b7863e7ad02898/crypto/x509v3/v3_purp.c#L503 )
The solution is as the one already above, which is to create your own certificates with the correct key usages (or no key usage extensions!)
I also thought I should include an alternative way of creating the Self Signed certificate that openssl clients would be happy with if you're in windows land.
First download the powershell script from here
In a powershell console (Administrative) execute the following commands from within a folder that contains the downloaded scripts
New-SelfsignedCertificateEx -StoreLocation "LocalMachine" -KeyUsage "DigitalSignature,KeyEncipherment,KeyCertSign" -Subject "CN=<HOST_NAME_TO_USE>" -FriendlyName "<HOST_NAME_TO_USE>" -SignatureAlgorithm sha256 -SubjectAlternativeName "<HOST_NAME_TO_USE>","anotherhost.org","someotherdomain.com"
Once you've executed the above command your LocalMachine\Personal Certificates store will contain a self-signed certificate that can be used by IIS for its SSL communications. (Please note you may also need to copy this certificate into one of the Trusted Root stores as well to guarantee that the certificate is trusted on that machine)
I solved this by using openssl to create a self-signed CA cert, then created a server cert request (also in OpenSSL, for some reason openssl does not like to sign requests generated by IIS), signed it with the former CA cert, then exported to PKCS12. Then imported into IIS. Once the CA cert is added to curl-ca-bundle.crt, it will verify the chain correctly:
Generate a CA:
openssl req -new -x509 -days 3650 -extensions v3_ca \
-keyout cakey.pem -out cacert.pem -config /etc/ssl/openssl.cnf \
-newkey rsa:2048
Generate a server key and signing request:
openssl req -new -nodes -out server-csr.pem -keyout server-key.pem -newkey rsa:2048
Sign the request with the CA:
openssl ca -config /etc/ssl/openssl.cnf -cert cacert.pem -keyfile cakey.pem \
-out server-cert.pem -in server-csr.pem
Export the server cert to PKCS#12:
openssl pkcs12 -export -out server-key-cert.pfx \
-inkey server-key.pem -in server-cert.pem -certfile cacert.pem
Import server-key-cert.pfx into IIS. (Re)bind the site binding's SSL binding to the cert.
Append cacert.pem to clients' curl-ca-bundle.crt. openssl s_client -showcerts -CAfile curl-ca-bundle.crt -connect server:443 has depth 0 and 1 and will verify return.
Notes: Make sure that keyUsage = nonRepudiation, digitalSignature, keyEncipherment is enabled under section [usr_cert] in openssl.cnf else requests won't contain those keyUsage and IIS will complain on binding.