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