A Utility for Viewing Java Keystore Contents
I end up dealing with a lot of certificates and private keys, and since I still work
primarily in Java, I necessarily end up dealing with quite a few Java Key Store
files. You need them to get Tomcat up and running, you need them to do mutual SSL
authentication, you need them to sign jar
files... yet I always find the
keytool
that comes standard with Java a bit lacking.
In particular, I usually want to know who the subject, the issuer and the end date of each
certificate are. keytool
, unfortunately, doesn't seem to offer any easy way to
output just that data. Of course, I can always do something like this:
... But that's sort of a hassle. For one thing, I have to list the contents to find out what
the aliases are, and for another, I can only see one at a time.
$ keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre/lib/security/cacerts
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 87 entries
digicertassuredidrootca, Apr 16, 2008, trustedCertEntry,
Certificate fingerprint (SHA1): 05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43
...
$ keytool -export -alias digicertassuredidrootca -keystore /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre/lib/security/cacerts \
| openssl x509 -inform der -noout -enddate
Enter keystore password:
notAfter=Nov 10 00:00:00 2031 GMT
The keytool -list
command includes a "verbose" -v
option, but that's a
little bit too verbose for my tastes:
Here, I really just wanted the subject and the end date in a consolidated form.
$ keytool -list -v -keystore /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre/lib/security/cacerts
Enter keystore password:
...
*******************************************
Alias name: affirmtrustpremiumca
Creation date: Apr 14, 2014
Entry type: trustedCertEntry
Owner: CN=AffirmTrust Premium, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Premium, O=AffirmTrust, C=US
Serial number: 6d8c1446b1a60aee
Valid from: Fri Jan 29 08:10:36 CST 2010 until: Mon Dec 31 08:10:36 CST 2040
Certificate fingerprints:
MD5: C4:5D:0E:48:B6:AC:28:30:4E:0A:BC:F9:38:16:87:57
SHA1: D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27
SHA256: 70:A7:3F:7F:37:6B:60:07:42:48:90:45:34:B1:14:82:D5:BF:0E:69:8E:CC:49:8D:F5:25:77:EB:F2:E9:3B:9A
Signature algorithm name: SHA384withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
#2: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
Key_CertSign
Crl_Sign
]
#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 9D C0 67 A6 0C 22 D9 26 F5 45 AB A6 65 52 11 27 ..g..".&.E..eR.'
0010: D8 45 AC 63 .E.c
]
]
*******************************************
...
You can output the keystore contents with the "-rfc" option:
But this isn't much better:
$ keytool -export -alias mykey -rfc -keystore testkeystore.jks
-----BEGIN CERTIFICATE-----
MIID5TCCAs2gAwIBAgIJAJAJCpRdEBfSMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzEO
MAwGA1UECAwFVGV4YXMxEzARBgNVBAoMCk5hbnRoZWFsdGgxFTATBgNVBAsMDEFyY2hpdGVjdHVy
ZTEWMBQGA1UEAwwNSm9zaHVhIERhdmllczElMCMGCSqGSIb3DQEJARYWamRhdmllc0BuYW50aGVh
...
$ keytool -list -rfc -keystore keystore.jks | openssl x509 -noout -subject
seems promising,
but it only outputs one certificate, and also doesn't echo the keystore alias.
I finally got tired of repeating myself and broke down and wrote a utility to output exactly
what I wanted, shown in Listing 1. Since Java exposes the KeyStore
directly, it wasn't difficult;
I wish I could have extended keytool
directly, but I was able to mimic most of the
behavior of the tool without much effort.
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Console;
import java.util.Enumeration;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
public class KeyStoreShow {
public static void main(String[] args) throws IOException,
GeneralSecurityException {
String keyStoreFile = System.getProperty("user.home") +
System.getProperty("file.separator") + ".keystore";
String storeType = KeyStore.getDefaultType(); // JKS
char[] storePassword = null;
boolean showSubject = false;
boolean showIssuer = false;
boolean showStartDate = false;
boolean showEndDate = false;
boolean showPubKey = false;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-keystore")) {
keyStoreFile = args[++i];
} else if (args[i].equals("-storetype")) {
storeType = args[++i];
} else if (args[i].equals("-storepass")) {
storePassword = args[++i].toCharArray();
} else if (args[i].equals("-subject")) {
showSubject = true;
} else if (args[i].equals("-issuer")) {
showIssuer = true;
} else if (args[i].equals("-startdate")) {
showStartDate = true;
} else if (args[i].equals("-enddate")) {
showEndDate = true;
} else if (args[i].equals("-pubKey")) {
showPubKey = true;
} else {
System.out.println("Illegal option " + args[i]);
System.out.println("-keystore <keystore>\tkeystore name");
System.out.println("-storetype <storetype>\tkeystore type");
System.out.println("-storepass <arg>\tkeystore password");
System.out.println("-subject\tprint subject DN");
System.out.println("-issuer\tprint issuer DN");
System.out.println("-startdate\tnotBefore field");
System.out.println("-enddate\tnotAfter field");
System.out.println("-pubkey\toutput the public key");
System.exit(0);
}
}
if (!showSubject &&
!showIssuer &&
!showStartDate &&
!showEndDate &&
!showPubKey ) {
showSubject = true; // make sure something shows up; default is subject
}
if (storePassword == null) {
System.out.print("Password: ");
storePassword = System.console().readPassword();
}
KeyStore ks = KeyStore.getInstance(storeType);
FileInputStream in = new FileInputStream(keyStoreFile);
try {
ks.load(in, storePassword);
} finally {
in.close();
}
Enumeration<String> aliases = ks.aliases(); // [1]
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
System.out.print(alias + ": ");
if (ks.isCertificateEntry(alias)) {
System.out.println("Certificate Entry");
} else {
System.out.println("Private Key Entry");
}
Certificate cert = ks.getCertificate(alias);
if (cert != null) {
if ("X.509".equals(cert.getType())) {
X509Certificate x509 = (X509Certificate) cert;
if (showSubject) {
System.out.println("Subject: " + x509.getSubjectX500Principal().toString());
}
if (showIssuer) {
System.out.println("Issuer: " + x509.getIssuerX500Principal().toString());
}
if (showStartDate) {
System.out.println("Start Date: " + x509.getNotBefore().toString());
}
if (showEndDate) {
System.out.println("End Date: " + x509.getNotAfter().toString());
}
if (showPubKey) {
PublicKey key = x509.getPublicKey();
System.out.println(key.toString());
}
} else {
System.out.println("Unrecognized certificate type '" + cert.getType() + "'");
}
}
}
}
}
Yes, Java's official Keystore implementation still uses the Enumeration class!
Here I merged the syntax of java's keytool
with OpenSSL's x509
utility; if you want to see, for example, the expiration date of each of the certificates in the trusted
roots file, you would do this:
$ java -classpath . KeyStoreShow -keystore \
/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre/lib/security/cacerts -enddate
Password:
digicertassuredidrootca: Certificate Entry
End Date: Sun Nov 09 18:00:00 CST 2031
trustcenterclass2caii: Certificate Entry
End Date: Wed Dec 31 16:59:59 CST 2025
...