Monday Feb 23, 2009
Andreas has written a blog entry on retrieving certificates from an SSL server. Whenever I see someone asking this question on the Java forum I point the user to this entry. Now it's time for this function to be included in keytool.
Call keytool -printcert -sslserver sun.com to see what's shown.
During the implementation of this feature, there are some discussions on how the function should be called. Two topics are most interesting:
What's the function name? At first, the plan is to add a new function to import the certificate into a keystore. The command will look like "-importcert -sslserver". However, there came several problems:
- For a normal certificate file, you can first call -printcert on it, read carefully, and then decide if it can be imported. For a certificate from an SSL server, you can still call something like "-printcert -sslserver" on it, but do you dare call "-importcert -sslserver" after examining it carefully? No, because the SSL server is not controlled by you, and it might send out a different certificate in the second call. That's scary, isn't it?
- An SSL server sends you a certificate chain. If you want to import one that's not always the end-entity cert, you need to specify a position number. This means another option, more interactions, and, more error messages or IndexOutOfBoundException. That's not good.
So the command ends up with a simple "-printcert -sslserver". It's left to the user to read/check/cut/paste the info wanted.
What protocols to support? This is a simple question, and the answer is ALL. Every application protocol that's based on SSL is included. However, the implementation chooses only HTTPS, for several reasons:
- HTTPS is the most popular SSL-based protocol out there, and programming it is the easiest, I simply call
new URL("https://" + sslserver).openConnection().connect();
- HTTPS supports proxy, so you can add -Dhttps.proxyHost and -Dhttps.proxyPort if the SSL server is behind a proxy.
- Last and the best. It also works for any SSL-based application protocol, because the handshake part of any such protocol is identical. Please notice that I only call the connect() method, where handshake is done but no application specific data communication is performed yet.
BTW, the feature was added into keytool
long time ago.
Monday Feb 23, 2009
There're two enhancements made to keytool today (the doc has not been updated, it's still for JDK 6):
We have 2 new commands: -gencert, -printcertreq and 1 new option -ext. Read the
RFE descriptions.
-printcertreq is simply for printing the content of a certificate request. It behaves like the -printcert command, reading a PKCS #10 format cert req from a file or stdin, and does not need a keystore to run with.
-gencert is a big enhancement, which means you can setup a tiny CA now with keytool. The command reads a certificate request from a file (specified by -infile) or stdin, creates a certificate, signs it with the private key in the PrivateKeyEntry specified by -alias, and print the output to another file (specified by -outfile) or stdout. That's it. Just like -genkeypair for self-signed certificate, you can specify -sigalg, -startdate, and -validity options to the command.
-ext is used to add X.509v3 certificate extensions to a certificate (for both -genkeypair and -gencert) or a certificate request (for -certreq). The option can be specified multiple times to add multiple extensions. The value of this option takes the form of
name[:critical][=
value]. Here
name is the extension name, and
value the value (omit if empty). The :critical modifier, if provided, means the extension's isCritical attribute is true; otherwise, false.
Currently we support these named extensions (case-insensitive):
| name | value |
| BC or BasicConstraints | The full form: "ca:{true|false}[,pathlen:len]";
or, "len", a shorthand for "ca:true,pathlen:len";
or omitted, means "ca:true" |
| KU or KeyUsage | usage(,usage)*, usage can be one of digitalSignature,
nonRepudiation (contentCommitment), keyEncipherment,
dataEncipherment, keyAgreement, keyCertSign, cRLSign,
encipherOnly, decipherOnly. Usage can be abbreviated
with the first few letters (say, dig for
digitalSignature) or in camel-case style (say,
dS for digitalSignature, cRLS for cRLSign), as long as
no ambiguity is found. Usage is case-insensitive. |
| EKU or ExtendedkeyUsage | usage(,usage)*, usage can be one of anyExtendedKeyUsage,
serverAuth, clientAuth, codeSigning, emailProtection,
timeStamping, OCSPSigning, or any OID string.
Named usage can be abbreviated with the first
few letters or in camel-case style, as long as
no ambiguity is found. Usage is case-insensitive. |
| SAN or SubjectAlternativeName | type:value(,type:value)*, type can be EMAIL, URI,
DNS, IP, or OID, value is the string format value
for the type. |
| IAN or IssuerAlternativeName | same as SubjectAlternativeName |
| SIA or SubjectInfoAccess | method:location-type:location-value
(,method:location-type:location-value)*,
method can be "timeStamping", "caRepository" or
any OID. location-type and location-value can be any
type:value supported by the SubjectAlternativeName
extension. |
| AIA or AuthorityInfoAccess | same as SubjectInfoAccess. method can be "ocsp",
"caIssuers" or any OID. |
When name is an arbitrary OID, value is the HEX dumped DER encoding of the extnValue for the extension excluding the OCTET STRING type and length bytes. Any extra character other than standard HEX numbers (0-9, a-f, A-F) are ignored in the HEX string. Therefore, both "01:02:03:04" and "01020304" are accepted as identical values. If there's no value, the extension has an empty value field then.
A special name "honored", used in -gencert only, denotes how the extensions included in the certificate request should be honored. The value for this name is a comma-seperated list of "all" (all requested extensions are honored), "name[:{critical|non-critical}]" (the named extension is honored, but using a different isCritical attribute) and "-name" (used with all, denotes an exception). Requested extensions are not honored by default.
If, besides the -ext honored option, another named or OID -ext option is provided, this extension will be added to those already honored. However, if this name (or OID) also appears in the honored value, its value and criticality overrides the one in the request.
The subjectKeyIdentifier extension is always created. For non self-signed certificates, the authorityKeyIdentifier is always created.
Try this command on your system if you already have 2 self-signed certs me and ca created in your default keystore:
keytool -storepass changeit -certreq -alias me -ext bc -ext eku=sa,ca | \
keytool -storepass changeit -gencert -alias ca -ext honored=all,-bc \
-ext aia=ocsp:uri:http://ocsp.ca.com,cai:uri:http://ca.com/ca.crt |
keytool -printcert
Here, the user me requests for an SSL server certificate from the CA. It asks for an EKU extension named ServerAuth and ClientAuth, which is useful for an SSL server. However, it also secretly asks for a BC extension, so that it can start its own CA. The CA, with sharp eyes, notices this problem. It grants all extensions requested
except BC. It also adds another extension AIA which includes issuer info into the cert generated.
When you run
openssl x509 -text with an X.509 certificate, the output includes a bunch of human-readable texts before the BASE64-encoded certificate itself. Java did not accept these texts and threw an exception something like "unknown tag or bad length", since it tried to interpret the file as DER encoded. Now we enhance the X.509
CertificateFactory class to accept this kind of certificate.
By the way, have I mentioned -startdate before? This option allows you to change the issuing time of a certificate from current system time to something else. Some people may like this option to create certs for special test cases, and some other people would like the certificate to have an earlier time because they want to use it right now but their clients and servers are not precisely time-synchronized. The grammar for this option takes one of the 2 following formats:
- ([+-]nnn[ymdHMS])+
- [yyyy/mm/dd] [HH:MM:SS]
So -startdate -5M means 5 minutes ago, -startdate "2001/01/01 11:11:11" means that exact time, -startdate 11:11:11 means that time today. Read more in the
RFE decriptions.
I recently came across your blog and have been rea...