1 /*
   2  * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.tools;
  27 
  28 
  29 import java.io.BufferedReader;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.IOException;
  33 import java.io.InputStreamReader;
  34 
  35 import java.io.StreamTokenizer;
  36 import java.io.StringReader;
  37 import java.net.URL;
  38 
  39 import java.security.KeyStore;
  40 
  41 import java.text.Collator;
  42 
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.List;
  46 import java.util.Locale;
  47 import java.util.Properties;
  48 
  49 import sun.security.util.PropertyExpander;
  50 
  51 /**
  52  * <p> This class provides several utilities to <code>KeyStore</code>.
  53  *
  54  * @since 1.6.0
  55  */
  56 public class KeyStoreUtil {
  57 
  58     private KeyStoreUtil() {
  59         // this class is not meant to be instantiated
  60     }
  61 
  62     private static final String JKS = "jks";
  63 
  64     private static final Collator collator = Collator.getInstance();
  65     static {
  66         // this is for case insensitive string comparisons
  67         collator.setStrength(Collator.PRIMARY);
  68     };
  69 
  70     /**
  71      * Returns true if KeyStore has a password. This is true except for
  72      * MSCAPI KeyStores
  73      */
  74     public static boolean isWindowsKeyStore(String storetype) {
  75         return storetype.equalsIgnoreCase("Windows-MY")
  76                 || storetype.equalsIgnoreCase("Windows-ROOT");
  77     }
  78 
  79     /**
  80      * Returns standard-looking names for storetype
  81      */
  82     public static String niceStoreTypeName(String storetype) {
  83         if (storetype.equalsIgnoreCase("Windows-MY")) {
  84             return "Windows-MY";
  85         } else if(storetype.equalsIgnoreCase("Windows-ROOT")) {
  86             return "Windows-ROOT";
  87         } else {
  88             return storetype.toUpperCase(Locale.ENGLISH);
  89         }
  90     }
  91 
  92     /**
  93      * Returns the keystore with the configured CA certificates.
  94      */
  95     public static KeyStore getCacertsKeyStore()
  96         throws Exception
  97     {
  98         String sep = File.separator;
  99         File file = new File(System.getProperty("java.home") + sep
 100                              + "lib" + sep + "security" + sep
 101                              + "cacerts");
 102         if (!file.exists()) {
 103             return null;
 104         }
 105         KeyStore caks = null;
 106         try (FileInputStream fis = new FileInputStream(file)) {
 107             caks = KeyStore.getInstance(JKS);
 108             caks.load(fis, null);
 109         }
 110         return caks;
 111     }
 112 
 113     public static char[] getPassWithModifier(String modifier, String arg,
 114                                              java.util.ResourceBundle rb) {
 115         if (modifier == null) {
 116             return arg.toCharArray();
 117         } else if (collator.compare(modifier, "env") == 0) {
 118             String value = System.getenv(arg);
 119             if (value == null) {
 120                 System.err.println(rb.getString(
 121                         "Cannot.find.environment.variable.") + arg);
 122                 return null;
 123             } else {
 124                 return value.toCharArray();
 125             }
 126         } else if (collator.compare(modifier, "file") == 0) {
 127             try {
 128                 URL url = null;
 129                 try {
 130                     url = new URL(arg);
 131                 } catch (java.net.MalformedURLException mue) {
 132                     File f = new File(arg);
 133                     if (f.exists()) {
 134                         url = f.toURI().toURL();
 135                     } else {
 136                         System.err.println(rb.getString(
 137                                 "Cannot.find.file.") + arg);
 138                         return null;
 139                     }
 140                 }
 141 
 142                 try (BufferedReader br =
 143                      new BufferedReader(new InputStreamReader(
 144                          url.openStream()))) {
 145                     String value = br.readLine();
 146 
 147                     if (value == null) {
 148                         return new char[0];
 149                     }
 150 
 151                     return value.toCharArray();
 152                 }
 153             } catch (IOException ioe) {
 154                 System.err.println(ioe);
 155                 return null;
 156             }
 157         } else {
 158             System.err.println(rb.getString("Unknown.password.type.") +
 159                     modifier);
 160             return null;
 161         }
 162     }
 163 
 164     /**
 165      * Parses a option line likes
 166      *    -genkaypair -dname "CN=Me"
 167      * and add the results into a list
 168      * @param list the list to fill into
 169      * @param s the line
 170      */
 171     private static void parseArgsLine(List<String> list, String s)
 172             throws IOException, PropertyExpander.ExpandException {
 173         StreamTokenizer st = new StreamTokenizer(new StringReader(s));
 174 
 175         st.resetSyntax();
 176         st.whitespaceChars(0x00, 0x20);
 177         st.wordChars(0x21, 0xFF);
 178         // Everything is a word char except for quotation and apostrophe
 179         st.quoteChar('"');
 180         st.quoteChar('\'');
 181 
 182         while (true) {
 183             if (st.nextToken() == StreamTokenizer.TT_EOF) {
 184                 break;
 185             }
 186             list.add(PropertyExpander.expand(st.sval));
 187         }
 188     }
 189 
 190     /**
 191      * Prepends matched options from a pre-configured options file.
 192      * @param tool the name of the tool, can be "keytool" or "jarsigner"
 193      * @param file the pre-configured options file
 194      * @param c1 the name of the command, must not be null
 195      * @param c2 the alternative command name, null if none. For example,
 196      *           "genkey" is alt name for "genkeypair". A command can only
 197      *           have one alt name now.
 198      * @param args existing arguments
 199      * @return arguments combined
 200      * @throws IOException if there is a file I/O or format error
 201      * @throws PropertyExpander.ExpandException
 202      *         if there is a property expansion error
 203      */
 204     public static String[] expandArgs(String tool, String file,
 205                                       String c1, String c2, String[] args)
 206             throws IOException, PropertyExpander.ExpandException {
 207 
 208         List<String> result = new ArrayList<>();
 209         Properties p = new Properties();
 210         p.load(new FileInputStream(file));
 211 
 212         String s = p.getProperty(tool + ".all");
 213         if (s != null) {
 214             parseArgsLine(result, s);
 215         }
 216 
 217         // Cannot provide both -genkey and -genkeypair
 218         String s1 = p.getProperty(tool + "." + c1.substring(1));
 219         String s2 = null;
 220         if (c2 != null) {
 221             s2 = p.getProperty(tool + "." + c2.substring(1));
 222         }
 223         if (s1 != null && s2 != null) {
 224             throw new IOException("Cannot have both " + c1 + " and " + c2);
 225         }
 226         if (s1 == null) {
 227             s1 = s2;
 228         }
 229         if (s1 != null) {
 230             parseArgsLine(result, s1);
 231         }
 232 
 233         if (result.isEmpty()) {
 234             return args;
 235         } else {
 236             result.addAll(Arrays.asList(args));
 237             return result.toArray(new String[result.size()]);
 238         }
 239     }
 240 }