1 7 8 package javax.crypto; 9 10 import java.io.*; 11 import java.security.AccessController; 12 import java.security.PublicKey; 13 import java.security.NoSuchProviderException; 14 import java.security.PrivilegedExceptionAction; 15 import java.security.cert.*; 16 import java.util.*; 17 import java.net.URL; 18 import java.net.JarURLConnection; 19 import java.net.MalformedURLException; 20 import java.util.jar.*; 21 import java.util.zip.ZipEntry; 22 23 import sun.security.validator.Validator; 24 25 32 final class JarVerifier { 33 34 private static final boolean debug = false; 35 36 private Vector verifiedSignerCache = null; 38 39 private URL jarURL; 41 42 private JarFile jarFile = null; 44 45 private Validator validator; 46 47 52 JarVerifier(URL jarURL, Validator validator) { 53 this.jarURL = jarURL; 54 this.validator = validator; 55 verifiedSignerCache = new Vector(2); 56 } 57 58 64 void verify() throws JarException, IOException { 65 if (jarURL == null) { 66 throw new JarException("Class is on the bootclasspath"); 67 } 68 try { 69 verifyJars(jarURL, null); 70 } catch (NoSuchProviderException nspe) { 71 throw new JarException("Cannot verify " + jarURL.toString()); 72 } catch (CertificateException ce) { 73 throw new JarException("Cannot verify " + jarURL.toString()); 74 } finally { 75 verifiedSignerCache = null; 76 } 77 } 78 79 88 JarFile getJarFile() { 89 return jarFile; 90 } 91 92 97 private void verifyJars(URL jarURL, Vector verifiedJarsCache) 98 throws NoSuchProviderException, CertificateException, IOException 99 { 100 String jarURLString = jarURL.toString(); 101 102 if ((verifiedJarsCache == null) || 104 !verifiedJarsCache.contains(jarURLString)) { 105 106 String supportingJars = verifySingleJar(jarURL); 109 110 if (verifiedJarsCache != null) 112 verifiedJarsCache.addElement(jarURLString); 113 114 if (supportingJars != null) { 116 if (verifiedJarsCache == null) { 117 verifiedJarsCache = new Vector(); 118 verifiedJarsCache.addElement(jarURLString); 119 } 120 verifyManifestClassPathJars(jarURL, 121 supportingJars, 122 verifiedJarsCache); 123 } 124 } 125 126 } 127 128 private void verifyManifestClassPathJars(URL baseURL, 129 String supportingJars, 130 Vector verifiedJarsCache) 131 throws NoSuchProviderException, CertificateException, IOException 132 { 133 String[] jarFileNames = parseAttrClasspath(supportingJars); 135 136 try { 137 for (int i = 0; i < jarFileNames.length; i++) { 139 URL url = new URL(baseURL, jarFileNames[i]); 140 verifyJars(url, verifiedJarsCache); 141 } 142 } catch (MalformedURLException mue) { 143 MalformedURLException ex = new MalformedURLException( 144 "The JAR file " + baseURL.toString() + 145 " contains invalid URLs in its Class-Path attribute"); 146 ex.initCause(mue); 147 throw ex; 148 } 149 } 150 151 157 private String verifySingleJar(URL jarURL) 158 throws NoSuchProviderException, CertificateException, IOException 159 { 160 final URL url = jarURL.getProtocol().equalsIgnoreCase("jar")? 164 jarURL : new URL("jar:" + jarURL.toString() + "!/"); 165 166 JarFile jf = null; 167 boolean isCached = true; 168 169 try { 170 try { 171 jf = (JarFile) AccessController.doPrivileged( 172 new PrivilegedExceptionAction() { 173 public Object run() throws Exception { 174 JarURLConnection conn = (JarURLConnection) 175 url.openConnection(); 176 return conn.getJarFile(); 177 } 178 }); 179 } catch (java.security.PrivilegedActionException pae) { 180 SecurityException se = new SecurityException( 181 "Cannot verify " + url.toString()); 182 se.initCause(pae); 183 throw se; 184 } 185 186 byte[] buffer = new byte[8192]; 193 Vector entriesVec = new Vector(); 194 195 Enumeration entries = jf.entries(); 196 while (entries.hasMoreElements()) { 197 JarEntry je = (JarEntry)entries.nextElement(); 198 entriesVec.addElement(je); 199 BufferedInputStream is = 200 new BufferedInputStream(jf.getInputStream(je)); 201 int n; 202 try { 203 while ((n = is.read(buffer, 0, buffer.length)) != -1) { 204 } 207 } finally { 208 is.close(); 209 } 210 } 211 212 if (this.jarURL.equals(jarURL)) { 213 this.jarFile = jf; 214 } else { 215 isCached = false; 217 } 218 219 Manifest man = jf.getManifest(); 222 if (man == null) 223 throw new JarException(jarURL.toString() + " is not signed."); 224 225 Enumeration e = jf.entries(); 232 while (e.hasMoreElements()) { 233 JarEntry je = (JarEntry) e.nextElement(); 234 235 if (je.isDirectory()) 236 continue; 237 238 Certificate[] certs = je.getCertificates(); 240 if ((certs == null) || (certs.length == 0)) { 241 if (!je.getName().startsWith("META-INF")) 242 throw new JarException(jarURL.toString() + 243 " has unsigned entries - " 244 + je.getName() ); 245 } else { 246 258 int startIndex = 0; 259 X509Certificate[] certChain; 260 boolean signedAsExpected = false; 261 262 while ((certChain = getAChain(certs, startIndex)) != null) { 263 if (verifiedSignerCache.contains( 269 (X509Certificate)certChain[0])) { 270 signedAsExpected = true; 271 break; 272 } else if (isTrusted(certChain)) { 273 signedAsExpected = true; 274 verifiedSignerCache.addElement(certChain[0]); 275 break; 276 } 277 startIndex += certChain.length; 279 } 280 281 if (!signedAsExpected) { 282 throw new JarException(jarURL.toString() + 283 " is not signed by a" + 284 " trusted signer."); 285 } 286 } 287 } 288 289 return man.getMainAttributes().getValue( 291 Attributes.Name.CLASS_PATH); 292 } finally { 293 if ((jf != null) && (isCached == false)) { 294 jf = null; 295 } 296 } 297 } 298 299 302 private static String[] parseAttrClasspath(String supportingJars) 303 throws JarException 304 { 305 supportingJars = supportingJars.trim(); 306 307 int endIndex = supportingJars.indexOf(' '); 308 String name = null; 309 Vector values = new Vector(); 310 boolean done = false; 311 312 do { 313 if (endIndex > 0) { 314 name = supportingJars.substring(0, endIndex); 315 supportingJars = supportingJars.substring(endIndex + 1).trim(); 319 endIndex = supportingJars.indexOf(' '); 320 } else { 321 name = supportingJars; 322 done = true; 323 } 324 if (name.endsWith(".jar")) { 325 values.addElement(name); 326 } else { 327 throw new JarException("The provider contains " + 329 "un-verifiable components"); 330 } 331 } while (!done); 332 333 return (String[]) values.toArray(new String[0]); 334 } 335 336 private boolean isTrusted(X509Certificate chain[]) { 337 try { 338 validator.validate(chain); 339 return true; 340 } catch (CertificateException e) { 341 return false; 342 } 343 } 344 345 private static X509Certificate[] getAChain(Certificate[] certs, 349 int startIndex) { 350 int i; 351 352 if (startIndex > certs.length - 1) 353 return null; 354 355 for (i = startIndex; i < certs.length - 1; i++) { 356 if (!((X509Certificate)certs[i + 1]).getSubjectDN().equals( 357 ((X509Certificate)certs[i]).getIssuerDN())) { 358 break; 359 } 360 } 361 int certChainSize = (i-startIndex) + 1; 362 X509Certificate[] ret = new X509Certificate[certChainSize]; 363 for (int j = 0; j < certChainSize; j++ ) { 364 ret[j] = (X509Certificate) certs[startIndex + j]; 365 } 366 return ret; 367 } 368 369 static List convertCertsToChains(Certificate[] certs) 373 throws CertificateException { 374 if (certs == null) { 375 return Collections.EMPTY_LIST; 376 } 377 List list = new ArrayList(); 378 X509Certificate[] oneChain = null; 379 int index = 0; 380 while ((oneChain = getAChain(certs, index)) != null) { 381 list.add(oneChain); 382 index += oneChain.length; 384 } 385 386 return list; 387 } 388 389 static List getSignersOfJarEntry(URL jarEntryURL) throws Exception { 392 JarURLConnection conn = (JarURLConnection) jarEntryURL.openConnection(); 393 byte[] buffer = new byte[8192]; 394 BufferedInputStream is = new BufferedInputStream(conn.getInputStream()); 396 try { 397 while (is.read(buffer, 0, buffer.length) != -1) { 398 } 401 } finally { 402 is.close(); 403 } 404 405 return convertCertsToChains(conn.getCertificates()); 406 } 407 408 protected void finalize() throws Throwable { 409 jarFile = null; 410 super.finalize(); 411 } 412 } 413 | Popular Tags |