1 /* 2 * Copyright (c) 2005, 2014, 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.security.Provider; 42 import java.security.Security; 43 import java.security.cert.X509Certificate; 44 import java.text.Collator; 45 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.List; 49 import java.util.Locale; 50 import java.util.Properties; 51 import java.util.ServiceLoader; 52 53 import sun.security.util.PropertyExpander; 54 55 /** 56 * <p> This class provides several utilities to <code>KeyStore</code>. 57 * 58 * @since 1.6.0 59 */ 60 public class KeyStoreUtil { 61 62 private KeyStoreUtil() { 63 // this class is not meant to be instantiated 64 } 65 66 private static final String JKS = "jks"; 67 68 private static final Collator collator = Collator.getInstance(); 69 static { 70 // this is for case insensitive string comparisons 71 collator.setStrength(Collator.PRIMARY); 72 }; 73 74 /** 75 * Returns true if the certificate is self-signed, false otherwise. 76 */ 77 public static boolean isSelfSigned(X509Certificate cert) { 78 return signedBy(cert, cert); 79 } 80 81 public static boolean signedBy(X509Certificate end, X509Certificate ca) { 82 if (!ca.getSubjectX500Principal().equals(end.getIssuerX500Principal())) { 83 return false; 84 } 85 try { 86 end.verify(ca.getPublicKey()); 87 return true; 88 } catch (Exception e) { 89 return false; 90 } 91 } 92 93 /** 94 * Returns true if KeyStore has a password. This is true except for 95 * MSCAPI KeyStores 96 */ 97 public static boolean isWindowsKeyStore(String storetype) { 98 return storetype.equalsIgnoreCase("Windows-MY") 99 || storetype.equalsIgnoreCase("Windows-ROOT"); 100 } 101 102 /** 103 * Returns standard-looking names for storetype 104 */ 105 public static String niceStoreTypeName(String storetype) { 106 if (storetype.equalsIgnoreCase("Windows-MY")) { 107 return "Windows-MY"; 108 } else if(storetype.equalsIgnoreCase("Windows-ROOT")) { 109 return "Windows-ROOT"; 110 } else { 111 return storetype.toUpperCase(Locale.ENGLISH); 112 } 113 } 114 115 /** 116 * Returns the keystore with the configured CA certificates. 117 */ 118 public static KeyStore getCacertsKeyStore() 119 throws Exception 120 { 121 String sep = File.separator; 122 File file = new File(System.getProperty("java.home") + sep 123 + "lib" + sep + "security" + sep 124 + "cacerts"); 125 if (!file.exists()) { 126 return null; 127 } 128 KeyStore caks = null; 129 try (FileInputStream fis = new FileInputStream(file)) { 130 caks = KeyStore.getInstance(JKS); 131 caks.load(fis, null); 132 } 133 return caks; 134 } 135 136 public static char[] getPassWithModifier(String modifier, String arg, 137 java.util.ResourceBundle rb) { 138 if (modifier == null) { 139 return arg.toCharArray(); 140 } else if (collator.compare(modifier, "env") == 0) { 141 String value = System.getenv(arg); 142 if (value == null) { 143 System.err.println(rb.getString( 144 "Cannot.find.environment.variable.") + arg); 145 return null; 146 } else { 147 return value.toCharArray(); 148 } 149 } else if (collator.compare(modifier, "file") == 0) { 150 try { 151 URL url = null; 152 try { 153 url = new URL(arg); 154 } catch (java.net.MalformedURLException mue) { 155 File f = new File(arg); 156 if (f.exists()) { 157 url = f.toURI().toURL(); 158 } else { 159 System.err.println(rb.getString( 160 "Cannot.find.file.") + arg); 161 return null; 162 } 163 } 164 165 try (BufferedReader br = 166 new BufferedReader(new InputStreamReader( 167 url.openStream()))) { 168 String value = br.readLine(); 169 170 if (value == null) { 171 return new char[0]; 172 } 173 174 return value.toCharArray(); 175 } 176 } catch (IOException ioe) { 177 System.err.println(ioe); 178 return null; 179 } 180 } else { 181 System.err.println(rb.getString("Unknown.password.type.") + 182 modifier); 183 return null; 184 } 185 } 186 187 /** 188 * Parses a option line likes 189 * -genkaypair -dname "CN=Me" 190 * and add the results into a list 191 * @param list the list to fill into 192 * @param s the line 193 */ 194 private static void parseArgsLine(List<String> list, String s) 195 throws IOException, PropertyExpander.ExpandException { 196 StreamTokenizer st = new StreamTokenizer(new StringReader(s)); 197 198 st.resetSyntax(); 199 st.whitespaceChars(0x00, 0x20); 200 st.wordChars(0x21, 0xFF); 201 // Everything is a word char except for quotation and apostrophe 202 st.quoteChar('"'); 203 st.quoteChar('\''); 204 205 while (true) { 206 if (st.nextToken() == StreamTokenizer.TT_EOF) { 207 break; 208 } 209 list.add(PropertyExpander.expand(st.sval)); 210 } 211 } 212 213 /** 214 * Prepends matched options from a pre-configured options file. 215 * @param tool the name of the tool, can be "keytool" or "jarsigner" 216 * @param file the pre-configured options file 217 * @param c1 the name of the command, with the "-" prefix, 218 * must not be null 219 * @param c2 the alternative command name, with the "-" prefix, 220 * null if none. For example, "genkey" is alt name for 221 * "genkeypair". A command can only have one alt name now. 222 * @param args existing arguments 223 * @return arguments combined 224 * @throws IOException if there is a file I/O or format error 225 * @throws PropertyExpander.ExpandException 226 * if there is a property expansion error 227 */ 228 public static String[] expandArgs(String tool, String file, 229 String c1, String c2, String[] args) 230 throws IOException, PropertyExpander.ExpandException { 231 232 List<String> result = new ArrayList<>(); 233 Properties p = new Properties(); 234 p.load(new FileInputStream(file)); 235 236 String s = p.getProperty(tool + ".all"); 237 if (s != null) { 238 parseArgsLine(result, s); 239 } 240 241 // Cannot provide both -genkey and -genkeypair 242 String s1 = p.getProperty(tool + "." + c1.substring(1)); 243 String s2 = null; 244 if (c2 != null) { 245 s2 = p.getProperty(tool + "." + c2.substring(1)); 246 } 247 if (s1 != null && s2 != null) { 248 throw new IOException("Cannot have both " + c1 + " and " 249 + c2 + " as pre-configured options"); 250 } 251 if (s1 == null) { 252 s1 = s2; 253 } 254 if (s1 != null) { 255 parseArgsLine(result, s1); 256 } 257 258 if (result.isEmpty()) { 259 return args; 260 } else { 261 result.addAll(Arrays.asList(args)); 262 return result.toArray(new String[result.size()]); 263 } 264 } 265 266 /** 267 * Loads a security provider in a module with its name. 268 * 269 * @param provName the name 270 * @param arg optional arg 271 * @param debug if true, print some log 272 * @throws IllegalAccessException if a provider cannot be added 273 */ 274 public static void loadProviderByName(String provName, String arg, boolean debug) { 275 for (Provider p: ServiceLoader.load(Provider.class)) { 276 if (p.getName().equals(provName)) { 277 if (arg != null) { 278 p = p.configure(arg); 279 } 280 Security.addProvider(p); 281 if (debug) { 282 System.out.println("loadProviderByName: " + p); 283 } 284 return; 285 } 286 } 287 throw new IllegalArgumentException(provName); 288 } 289 290 /** 291 * Loads a security provider with its full-qualified name. 292 * 293 * @param provClass the class name 294 * @param arg optional arg 295 * @param cl optional class loader 296 * @param debug if true, print some log 297 * @throws IllegalAccessException if a provider cannot be added 298 */ 299 public static void loadProviderByClass(String provClass, String arg, 300 ClassLoader cl, boolean debug) { 301 Provider prov = null; 302 for (Provider p: ServiceLoader.load(Provider.class)) { 303 // A provider in module can also be use class name 304 if (p.getClass().getName().equals(provClass)) { 305 prov = p; 306 break; 307 } 308 } 309 if (prov == null) { 310 // legacy provider on classpath 311 try { 312 Class<?> clazz; 313 if (cl != null) { 314 clazz = cl.loadClass(provClass); 315 } else { 316 clazz = Class.forName(provClass); 317 } 318 KeyStoreUtil.class.getModule().addReads(clazz.getModule()); 319 Object obj = clazz.newInstance(); 320 if (!(obj instanceof Provider)) { 321 throw new IllegalArgumentException(provClass); 322 } 323 prov = (Provider) obj; 324 } catch (Exception e) { 325 throw new IllegalArgumentException(provClass, e); 326 } 327 } 328 if (arg != null) { 329 prov = prov.configure(arg); 330 } 331 Security.addProvider(prov); 332 if (debug) { 333 System.out.println("loadProviderByClass: " + prov); 334 } 335 } 336 }