/* * Import system SSL certificates to java keystore * Copyright (C) 2010 SUSE LINUX Products GmbH * * Author: Ludwig Nussel * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ import java.security.KeyStore; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.BufferedInputStream; import java.io.FilenameFilter; import java.util.HashSet; import java.util.Enumeration; import java.util.Iterator; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; public class keystore { static HashSet blacklist; public static void usage() { System.err.println("Usage: java keystore -keystore -cadir [-storepass |-f|-v]"); System.err.println(""); System.err.println(" -keystore \tname of final keystore (required)"); System.err.println(" -cadir \t\tdirectory contains certificates (required)"); System.err.println(" -storepass \tthe password"); System.err.println(" -f\t\t\t\tfresh existing keystore"); System.err.println(" -v\t\t\t\tbe verbose"); System.err.println(" -h/--help\t\t\tshow this help"); } public static void main(String[] args) throws java.security.KeyStoreException, java.security.NoSuchAlgorithmException, java.security.cert.CertificateException, java.io.IOException { char[] password = null; String ksfilename = null; String cadirname = null; boolean verbose = false; boolean fresh = false; if (args.length == 0) { usage(); System.exit(1); } if (!System.getProperty("java.vendor").equals("Free Software Foundation, Inc.")) { password = "changeit".toCharArray(); } for (int i = 0; i < args.length; ++i) { if (args[i].equals("-keystore")) { ksfilename = args[++i]; } else if (args[i].equals("-cadir")) { cadirname = args[++i]; } else if (args[i].equals("-storepass")) { password = args[++i].toCharArray(); } else if (args[i].equals("-v")) { verbose = true; } else if (args[i].equals("-f")) { fresh = true; } else if (args[i].equals("-h") || args[i].equals("--help")) { usage(); System.exit(1); } else { System.err.println("invalid argument: " + args[i]); System.err.println("type -h/--help for help"); System.exit(1); } } if (ksfilename == null) { System.err.println("must specify -keystore"); return; } if (cadirname == null) { System.err.println("must specify -cadir"); return; } File cadir = new File(cadirname); if (!cadir.isDirectory()) { System.err.println("cadir is not a directory"); return; } blacklist = new HashSet(); // XXX: make a file // blacklist.add("foo"); String certs[] = cadir.list(new FilenameFilter(){ public boolean accept(File dir, String name) { if (!name.endsWith(".pem")) { return false; } if (blacklist.contains(name)) { return false; } return true; } }); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream storein = null; try { File f = new File(ksfilename); if (!fresh && f.exists()) { storein = new FileInputStream(ksfilename); } ks.load(storein, password); } finally { if (storein != null) { storein.close(); } } HashSet known = new HashSet(); for (Enumeration a = ks.aliases(); a.hasMoreElements();) { known.add(a.nextElement()); } CertificateFactory cf = CertificateFactory.getInstance("X509"); int added = 0; int removed = 0; for (int i = 0; i < certs.length; ++i) { BufferedInputStream f; try { f = new BufferedInputStream(new FileInputStream(cadirname+"/"+certs[i])); } catch (java.io.FileNotFoundException ex) { System.err.println("skipping " + certs[i] + ": file not found"); continue; } String marker = "-----BEGIN CERTIFICATE-----"; boolean found = false; f.mark(80); String line; String alias = null; // we need to parse and skip the "header" while((line = readline(f)) != null) { if (line.equals(marker)) { f.reset(); found = true; break; } else if (line.startsWith("# alias=")) { // FIXME: somehow UTF-8 encoding must be enforced here alias = line.substring(8); } f.mark(80); } if (found) { if (alias == null) { alias = certs[i].substring(0, certs[i].length()-4); // without .pem } alias = alias.toLowerCase(); try { X509Certificate cert = (X509Certificate)cf.generateCertificate(f); if (known.contains(alias)) { if (verbose) System.out.println("already known: " + alias); known.remove(alias); } else { if (verbose) System.out.println("adding " + alias); ks.setCertificateEntry(alias, cert); ++added; } } catch (java.security.cert.CertificateException ex) { System.err.println("imporing " + certs[i] + " failed: " + ex.getCause()); } } else { System.out.println("skipping file with unrecognized format: " + certs[i]); } } if (!known.isEmpty()) { for (Iterator it = known.iterator(); it.hasNext();) { String alias = it.next(); if (verbose) System.out.println("removing " + alias); ks.deleteEntry(alias); ++removed; } } if (added != 0 || removed != 0) { FileOutputStream storeout = new FileOutputStream(ksfilename); ks.store(storeout, password); storeout.close(); } System.out.println(added + " added, " + removed + " removed."); } public static String readline(BufferedInputStream in) throws java.io.IOException { StringBuffer buf = new StringBuffer(80); int c = in.read(); while(c != -1 && c != '\n' && c != '\r') { buf.append((char)c); c = in.read(); } if (c == '\r') { in.mark(1); c = in.read(); if (c != '\n') in.reset(); } if (buf.length() == 0) return null; return buf.toString(); } }