1 19 20 package org.netbeans.modules.autoupdate; 21 22 import java.io.File ; 23 import java.io.FileInputStream ; 24 import java.io.FileOutputStream ; 25 import java.io.IOException ; 26 import java.io.InputStream ; 27 import java.io.OutputStream ; 28 import java.security.KeyStore ; 29 import java.security.KeyStoreException ; 30 import java.security.MessageDigest ; 31 import java.security.NoSuchAlgorithmException ; 32 import java.security.cert.Certificate ; 33 import java.security.cert.CertificateException ; 34 import java.security.cert.X509Certificate ; 35 import java.util.ArrayList ; 36 import java.util.Collection ; 37 import java.util.Enumeration ; 38 import java.util.HashSet ; 39 import java.util.Iterator ; 40 import java.util.LinkedList ; 41 import java.util.List ; 42 import java.util.Set ; 43 import java.util.jar.JarEntry ; 44 import java.util.jar.JarFile ; 45 import java.util.jar.JarInputStream ; 46 import java.util.jar.Manifest ; 47 import java.util.logging.Level ; 48 import java.util.logging.Logger ; 49 import org.netbeans.api.progress.ProgressHandle; 50 import org.netbeans.api.progress.ProgressHandleFactory; 51 import org.openide.util.NbBundle; 52 53 56 class SignVerifier extends Object { 57 58 59 private static final String KS_PSSWD = "open4all"; 61 private static final String ENTRY_SEPARATOR = "/"; private static final String NBM_MAIN = "main"; private static final String NBM_MODULES = "netbeans/modules"; private static final String NBM_AUTOLOAD = NBM_MODULES + ENTRY_SEPARATOR + SafeModule.PROP_AUTOLOAD; private static final String NBM_EAGER = NBM_MODULES + ENTRY_SEPARATOR + SafeModule.PROP_EAGER; private static final String JAR_EXT = ".jar"; 67 68 public static final int NOT_CHECKED = -1; 69 public static final int BAD_DOWNLOAD = 0; 70 public static final int CORRUPTED = 1; 71 public static final int NOT_SIGNED = 2; 72 public static final int SIGNED = 3; 73 public static final int TRUSTED = 4; 74 75 private static final String NEW_LINE = "\n"; private static final String SPACE = " "; private static final String TAB = "\t"; 79 80 DownloadProgressPanel progressDialog; 81 82 private Wizard.Validator validator; 83 84 private int verifySize; 85 86 private int totalVerified; 87 88 private long modulesCount; 89 90 private boolean verifyCanceled = false; 91 private boolean wizardCanceled = false; 92 93 ProgressHandle partialHandle; 95 ProgressHandle overallHandle; 96 long startTime; 97 98 private static final Logger err = Logger.getLogger("org.netbeans.modules.autoupdate"); 100 101 SignVerifier (DownloadProgressPanel progressDialog, Wizard.Validator validator) { 102 this.validator = validator; 103 this.progressDialog = progressDialog; 104 } 105 106 107 108 void doVerify() { 109 verifyCanceled = false; 110 progressDialog.setPartialLabel (getBundle( "CTL_PreparingVerify_Label")); 112 verifySize = getTotalVerifySize(); 113 err.log(Level.FINE, 114 "Runing doVerify, check the total size " + verifySize + 115 ", verifyCanceled? " + verifyCanceled); 116 117 if ( verifyCanceled ) 118 return; 119 120 verifyAll(); 121 122 err.log(Level.FINE, "Verification done."); 123 if ( verifyCanceled ) 124 return; 125 126 validator.setValid( true ); 127 } 128 129 130 131 private int getTotalVerifySize( ) { 132 int result = 0; 133 modulesCount = 0; 134 135 Iterator it = Wizard.getAllModules().iterator(); 136 137 while( it.hasNext() ) { 138 ModuleUpdate mu = (ModuleUpdate)it.next(); 139 140 if ( mu.isSelected() && mu.isDownloadOK() && 141 (mu.getSecurity() == NOT_CHECKED || mu.getSecurity() == BAD_DOWNLOAD )) { 142 File file = Downloader.getNBM( mu ); 143 result += file.length(); 144 modulesCount++; 145 } 146 } 147 return result; 148 } 149 150 151 void verifyAll() { 152 153 overallHandle = getOverallHandle (verifySize); 154 155 progressDialog.setPartialLabel (""); progressDialog.setOverallLabel (""); progressDialog.setExtraLabel (""); 159 int currentModule = 0; 160 totalVerified = 0; 161 162 Iterator it = Wizard.getAllModules().iterator(); 163 164 while( it.hasNext() ) { 165 166 if ( verifyCanceled ) 167 return; 168 169 ModuleUpdate mu = (ModuleUpdate)it.next(); 170 if ( mu.isSelected() && mu.isDownloadOK() && 171 (mu.getSecurity() == NOT_CHECKED || mu.getSecurity() == BAD_DOWNLOAD )) { 172 173 if ( verifyCanceled ) 174 return; 175 176 progressDialog.setPartialLabel (mu.getName() + " [" + (currentModule + 1) + "/" + modulesCount + "]" ); 178 File file = Downloader.getNBM( mu ); 179 try { 180 Collection certificates = verifyJar( file, mu ); 181 182 if ( certificates == null ) { 183 err.log(Level.FINE, 184 mu.getName() + " was verified as NOT_SIGNED"); 185 186 mu.setSecurity( NOT_SIGNED ); 187 mu.setInstallApproved( false ); 188 } 189 else { 190 mu.setCerts( certificates ); 191 192 if ( isTrusted( certificates ) ) { 193 err.log(Level.FINE, 194 mu.getName() + " was verified as TRUSTED"); 195 mu.setSecurity( TRUSTED ); 196 mu.setInstallApproved( true ); 197 } 198 else { 199 err.log(Level.FINE, 200 mu.getName() + " was verified as SIGNED"); 201 mu.setSecurity( SIGNED ); 202 mu.setInstallApproved( false ); 203 } 204 } 205 } 206 catch( SecurityException e ) { 207 err.log(Level.FINE, 208 mu.getName() + " was verified as CORRUPTED"); 209 mu.setSecurity( CORRUPTED ); 210 mu.setInstallApproved( false ); 211 } 212 catch( IOException e ) { 213 err.log(Level.FINE, 214 mu.getName() + " was verified as BAD_DOWNLOAD"); 215 mu.setSecurity( BAD_DOWNLOAD ); 216 mu.setInstallApproved( false ); 217 mu.setDownloadOK( false ); 218 } 219 catch( NullPointerException npe ) { 220 err.log(Level.FINE, 221 mu.getName() + " was verified as BAD_DOWNLOAD"); 222 mu.setSecurity( BAD_DOWNLOAD ); 223 mu.setInstallApproved( false ); 224 mu.setDownloadOK( false ); 225 } 226 227 currentModule++; 228 overallHandle.progress (totalVerified); 229 } 230 } 231 232 overallHandle.finish (); 233 String mssgTotal = NbBundle.getMessage( SignVerifier.class, "FMT_VerifiedTotal", new Object [] { new Integer ( verifySize / 1024 ), 235 new Integer ( verifySize / 1024 ) } ); 236 progressDialog.setOverallLabel (mssgTotal); 237 } 238 239 240 243 252 253 static void processJarEntry (JarEntry entry, ModuleUpdate mu) { 254 if ( entry.getName().startsWith( "netbeans/" + ModuleUpdate.NBM_LIB + "/")) { mu.setDepending( true ); 257 } 258 259 if ( entry.getName().startsWith( NBM_MAIN ) ) 260 mu.setSafeToInstall( false ); 261 262 String jarname = null; 263 if ( entry.getName().startsWith( NBM_AUTOLOAD ) && entry.getName().endsWith( JAR_EXT ) ) { 264 jarname = getJarModuleName( entry.getName(), NBM_AUTOLOAD + ENTRY_SEPARATOR ); 265 if ( jarname != null ) 266 mu.addToJarList( jarname ); 267 } 268 else if ( entry.getName().startsWith( NBM_EAGER ) && entry.getName().endsWith( JAR_EXT ) ) { 269 jarname = getJarModuleName( entry.getName(), NBM_EAGER + ENTRY_SEPARATOR ); 270 if ( jarname != null ) 271 mu.addToJarList( jarname ); 272 } 273 else if( entry.getName().startsWith( NBM_MODULES ) && entry.getName().endsWith( JAR_EXT ) ) { 274 jarname = getJarModuleName( entry.getName(), NBM_MODULES + ENTRY_SEPARATOR ); 275 if ( jarname != null ) 276 mu.addToJarList( jarname ); 277 } 278 } 279 280 283 private Collection verifyJar( File jarName, ModuleUpdate mu ) throws SecurityException , IOException { 284 285 JarInputStream jis = null; 286 boolean anySigned = false; 287 boolean anyUnsigned = false; 288 int moduleVerified = 0; 289 290 int flen = (int) jarName.length(); 291 292 partialHandle = getPartialHandle (flen); 293 294 JarFile jf = new JarFile (jarName); 295 Manifest man = jf.getManifest(); 296 297 Enumeration entries = jf.entries(); 298 LinkedList entriesList = new LinkedList (); 299 byte[] buffer = new byte[8192]; 300 301 try { 302 while (entries.hasMoreElements()) { 303 JarEntry entry = (JarEntry )entries.nextElement(); 304 305 processJarEntry (entry, mu); 306 307 entriesList.add(entry); 308 InputStream is = null; 309 try { 310 is = jf.getInputStream(entry); 311 int n; 312 while ((n = is.read(buffer, 0, buffer.length)) != -1) { 313 moduleVerified ++; 315 if ( moduleVerified % 4096 == 0 ) { 316 partialHandle.progress (moduleVerified < flen ? moduleVerified : flen); 317 } 318 if ( verifyCanceled ) 320 return null; 321 } 322 } finally { 323 if (is != null) is.close(); 324 totalVerified += entry.getCompressedSize(); 325 326 String mssgTotal = NbBundle.getMessage( SignVerifier.class, "FMT_VerifiedTotal", 327 new Object [] { new Integer ( totalVerified / 1024 ), 328 new Integer ( verifySize / 1024 ) } ); 329 progressDialog.setOverallLabel (mssgTotal); 330 } 331 } 332 333 err.log(Level.FINE, 334 "Update " + mu.getCodeNameBase() + 335 " was verified as forced to intall global: " + 336 mu.isForcedGlobal() + " and safeToInstall: " + 337 mu.isSafeToInstall()); 338 339 if ( verifyCanceled ) 340 return null; 341 } finally { 342 jf.close(); 343 if ( wizardCanceled ) 344 Downloader.getNBM( mu ).delete(); 345 } 346 347 partialHandle.finish (); 348 349 Set certificates = new HashSet (); 350 if (man != null) { 351 Iterator e = entriesList.iterator(); 352 while (e.hasNext()) { 353 JarEntry je = (JarEntry ) e.next(); 354 String name = je.getName(); 355 Certificate [] certs = je.getCertificates(); 356 boolean isSigned = ((certs != null) && (certs.length > 0)); 357 anySigned |= isSigned; 358 if (certs != null) { 359 for(int i = 0; i < certs.length; i++) { 360 certificates.add(certs[i]); 361 if ( verifyCanceled ) 362 return null; 363 } 364 } 365 else { if ( !je.isDirectory() && !name.toUpperCase().startsWith( "META-INF/" ) && je.getSize() != 0 ) { 368 anyUnsigned = true; 369 } 370 } 371 } 372 } 373 374 if ( anySigned && anyUnsigned ) { 375 throw new SecurityException ( getBundle( "EXC_NotSignedEntity" ) ); 376 } 377 378 return anySigned ? certificates : null; 379 } 380 381 public static String formatCerts(Collection collection) { 382 StringBuffer sb = new StringBuffer ( collection.size() * 300 ); 383 384 385 Iterator it = collection.iterator(); 386 while(it.hasNext()) { 387 Certificate cert = (Certificate )it.next(); 388 389 if ( cert instanceof X509Certificate ) { 390 try { 391 sb.append( "\n\n" ); sb.append( X509CertToString( (X509Certificate ) cert ) ); 393 } 394 catch ( Exception e ) { 395 sb.append( cert.toString() ); 396 } 397 } 398 else { 399 sb.append( cert.toString() ); 400 } 401 sb.append( "\n\n" ); } 403 404 return sb.toString(); 405 } 406 407 409 boolean isTrusted( Collection certs ) { 410 411 Collection trustedCerts = getTrustedCerts(); 412 413 if ( trustedCerts.size() <= 0 || certs.size() <= 0 ) 414 return false; 415 416 return trustedCerts.containsAll( certs ); 417 } 418 419 421 422 static void addCertificates( Collection certs ) 423 throws CertificateException , KeyStoreException , IOException , NoSuchAlgorithmException { 424 425 KeyStore ks = getKeyStore( Autoupdater.Support.getKSFile (), KS_PSSWD, null); 426 427 Iterator it = certs.iterator(); 428 429 while ( it.hasNext() ) { 430 Certificate c = (Certificate ) it.next(); 431 432 if ( ks.getCertificateAlias( c ) != null ) 434 continue; 435 436 String alias = null; 438 for ( int i = 0; i < 9999; i++ ) { 439 alias = "genAlias" + i; if ( !ks.containsAlias( alias ) ) 441 break; 442 } 443 if ( alias == null ) 444 throw new KeyStoreException ( getBundle( "EXC_TooManyCertificates" ) ); 445 446 ks.setCertificateEntry( alias, c ) ; 447 } 448 449 450 saveKeyStore( ks, Autoupdater.Support.getKSFile (), KS_PSSWD, null); 451 } 452 453 454 static void removeCertificates( Collection certs ) 455 throws CertificateException , KeyStoreException , IOException , NoSuchAlgorithmException { 456 457 KeyStore ks = getKeyStore( Autoupdater.Support.getKSFile (), KS_PSSWD, null); 458 459 Iterator it = certs.iterator(); 460 461 while ( it.hasNext() ) { 462 Certificate c = (Certificate ) it.next(); 463 464 String alias = ks.getCertificateAlias( c ); 465 466 if ( alias != null ) 467 ks.deleteEntry( alias ); 468 469 } 470 471 saveKeyStore( ks, Autoupdater.Support.getKSFile (), KS_PSSWD, null); 472 473 } 474 475 476 478 479 480 Collection getTrustedCerts() { 481 482 Collection trustedCerts = new ArrayList ( 10 ); 483 484 File ksFile = Autoupdater.Support.getKSFile (); 485 486 try { 487 if ( ksFile.canRead() ) { 488 KeyStore ks = getKeyStore (ksFile, KS_PSSWD, null); 489 trustedCerts.addAll ( getCertificates( ks ) ); 490 } 491 } 492 catch ( CertificateException e ) { 494 } 495 catch ( KeyStoreException e ) { 496 } 497 catch ( IOException e ) { 498 } 499 catch ( NoSuchAlgorithmException e ) { 500 } 501 502 return trustedCerts; 503 } 504 505 510 private static KeyStore getKeyStore(File file, String password, String storetype) 511 throws IOException , CertificateException , KeyStoreException , NoSuchAlgorithmException { 512 if (file == null) return null; 513 514 InputStream is = null; 515 516 try { 517 is = new FileInputStream ( file ); 518 } 519 catch ( IOException e ) { 520 } 522 523 KeyStore keyStore = null; 524 525 if ( storetype == null ) { 526 keyStore = KeyStore.getInstance( KeyStore.getDefaultType() ); 527 keyStore.load( is, password.toCharArray() ); 528 if ( is != null ) 529 is.close(); 530 } 531 532 return keyStore; 533 } 534 535 541 public static void saveKeyStore(KeyStore keyStore, File file, String password, String storetype) 542 throws CertificateException , KeyStoreException , IOException , NoSuchAlgorithmException { 543 544 if (file == null) return ; 545 546 OutputStream os = new FileOutputStream ( file ); 547 keyStore.store(os, password.toCharArray( ) ); 548 os.close(); 549 } 550 551 553 public static Collection getCertificates(KeyStore keyStore) throws KeyStoreException { 554 555 List certificates = new ArrayList ( 10 ); 556 557 Enumeration en = keyStore.aliases(); 558 while (en.hasMoreElements()) { 559 String alias = (String )en.nextElement(); 560 Certificate cert = keyStore.getCertificate(alias); 561 certificates.add(cert); 562 } 563 return certificates; 564 } 565 566 void cancelVerify(boolean wizardCanceled) { 567 verifyCanceled = true; 568 this.wizardCanceled = wizardCanceled; 569 } 570 571 572 574 private static String X509CertToString(X509Certificate cert ) 575 throws Exception 576 { 577 return getBundle("MSG_Owner") + SPACE + (cert.getSubjectDN()) + NEW_LINE + 578 getBundle("MSG_Issuer") + SPACE + (cert.getIssuerDN()) + NEW_LINE + 579 getBundle("MSG_SerNumber") + SPACE + cert.getSerialNumber().toString(16) + NEW_LINE + 580 getBundle("MSG_Valid") + SPACE + cert.getNotBefore().toString() + 581 SPACE + getBundle("MSG_Until") + SPACE + cert.getNotAfter().toString() + NEW_LINE + 582 getBundle("MSG_CertFinger") + NEW_LINE + 583 SPACE + TAB + getBundle("MSG_MD5") + SPACE + SPACE + getCertFingerPrint("MD5", cert) + NEW_LINE + 584 SPACE + TAB + getBundle("MSG_SHA1") + SPACE + getCertFingerPrint("SHA1", cert); 585 } 586 587 589 private static String getCertFingerPrint(String mdAlg, Certificate cert) 590 throws Exception 591 { 592 byte[] encCertInfo = cert.getEncoded(); 593 MessageDigest md = MessageDigest.getInstance(mdAlg); 594 byte[] digest = md.digest(encCertInfo); 595 return toHexString(digest); 596 } 597 598 600 private static String toHexString(byte[] block) { 601 StringBuffer buf = new StringBuffer (); 602 int len = block.length; 603 for (int i = 0; i < len; i++) { 604 byte2hex(block[i], buf); 605 if (i < len-1) { 606 buf.append(":"); } 608 } 609 return buf.toString(); 610 } 611 612 613 615 private static void byte2hex(byte b, StringBuffer buf) { 616 char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', 617 '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 618 int high = ((b & 0xf0) >> 4); 619 int low = (b & 0x0f); 620 buf.append(hexChars[high]); 621 buf.append(hexChars[low]); 622 } 623 624 private static String getJarModuleName( String name, String prefix ) { 625 if ( name.substring( prefix.length() ).indexOf( ENTRY_SEPARATOR ) == -1 ) { 626 String common = NBM_MODULES + ENTRY_SEPARATOR; 627 return name.substring( common.length() ); 628 } 629 return null; 630 } 631 632 private static String getBundle( String key ) { 633 return NbBundle.getMessage( SignVerifier.class, key ); 634 } 635 636 private ProgressHandle getPartialHandle (int units) { 637 assert progressDialog != null; 638 ProgressHandle handle = ProgressHandleFactory.createHandle (getBundle ("DownloadProgressPanel_partialHandle_name")); progressDialog.setPartialProgressComponent (handle); 640 handle.start (units); 641 return handle; 642 } 643 644 private ProgressHandle getOverallHandle (int units) { 645 assert progressDialog != null; 646 final ProgressHandle handle = ProgressHandleFactory.createHandle (getBundle ("DownloadProgressPanel_overallHandle_name")); progressDialog.setOverallProgressComponent (handle); 648 handle.start (units); 649 return handle; 650 } 651 652 } 653 | Popular Tags |