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