Example #1
0
 /** Copy all the files from input to output. */
 private void copyFiles(Map<String, ZioEntry> input, ZipOutput output) throws IOException {
   int i = 1;
   for (ZioEntry inEntry : input.values()) {
     if (canceled) break;
     progressHelper.progress(
         ProgressEvent.PRORITY_NORMAL,
         String.format("Copying zip entry %d of %d", i, input.size()));
     i += 1;
     output.write(inEntry);
   }
 }
Example #2
0
 /**
  * Copy all the files in a manifest from input to output. We set the modification times in the
  * output to a fixed time, so as to reduce variation in the output file and make incremental OTAs
  * more efficient.
  */
 private void copyFiles(
     Manifest manifest, Map<String, ZioEntry> input, ZipOutput output, long timestamp)
     throws IOException {
   Map<String, Attributes> entries = manifest.getEntries();
   List<String> names = new ArrayList<String>(entries.keySet());
   Collections.sort(names);
   int i = 1;
   for (String name : names) {
     if (canceled) break;
     progressHelper.progress(
         ProgressEvent.PRORITY_NORMAL,
         String.format("Copying zip entry %d of %d", i, names.size()));
     i += 1;
     ZioEntry inEntry = input.get(name);
     inEntry.setTime(timestamp);
     output.write(inEntry);
   }
 }
Example #3
0
  /**
   * Sign the and signature block template. The signature block template parameter may be null, but
   * if so android-sun-jarsign-support.jar must be in the classpath.
   */
  public void signZip(
      Map<String, ZioEntry> zioEntries, OutputStream outputStream, String outputZipFilename)
      throws IOException, GeneralSecurityException {
    boolean debug = getLogger().isDebugEnabled();

    progressHelper.initProgress();
    if (keySet == null) {
      if (!keymode.startsWith(MODE_AUTO))
        throw new IllegalStateException("No keys configured for signing the file!");

      // Auto-determine which keys to use
      String keyName = this.autoDetectKey(keymode, zioEntries);
      if (keyName == null)
        throw new AutoKeyException(
            "Unable to auto-select key for signing " + new File(outputZipFilename).getName());

      autoKeyObservable.notifyObservers(keyName);

      loadKeys(keyName);
    }

    ZipOutput zipOutput = null;

    try {

      zipOutput = new ZipOutput(outputStream);

      if (KEY_NONE.equals(keySet.getName())) {
        progressHelper.setProgressTotalItems(zioEntries.size());
        progressHelper.setProgressCurrentItem(0);
        copyFiles(zioEntries, zipOutput);
        return;
      }

      // Calculate total steps to complete for accurate progress percentages.
      int progressTotalItems = 0;
      for (ZioEntry entry : zioEntries.values()) {
        String name = entry.getName();
        if (!entry.isDirectory()
            && !name.equals(JarFile.MANIFEST_NAME)
            && !name.equals(CERT_SF_NAME)
            && !name.equals(CERT_RSA_NAME)
            && (stripPattern == null || !stripPattern.matcher(name).matches())) {
          progressTotalItems += 3; // digest for manifest, digest in sig file, copy data
        }
      }
      progressTotalItems += 1; // CERT.RSA generation
      progressHelper.setProgressTotalItems(progressTotalItems);
      progressHelper.setProgressCurrentItem(0);

      // Assume the certificate is valid for at least an hour.
      long timestamp = keySet.getPublicKey().getNotBefore().getTime() + 3600L * 1000;

      // MANIFEST.MF
      // progress(ProgressEvent.PRORITY_NORMAL, JarFile.MANIFEST_NAME);
      Manifest manifest = addDigestsToManifest(zioEntries);
      if (canceled) return;
      ZioEntry ze = new ZioEntry(JarFile.MANIFEST_NAME);
      ze.setTime(timestamp);
      manifest.write(ze.getOutputStream());
      zipOutput.write(ze);

      // CERT.SF
      // progress( ProgressEvent.PRORITY_NORMAL, CERT_SF_NAME);

      // Can't use default Signature on Android.  Although it generates a signature that can be
      // verified by jarsigner,
      // the recovery program appears to require a specific algorithm/mode/padding.  So we use the
      // custom ZipSignature instead.
      // Signature signature = Signature.getInstance("SHA1withRSA");
      ZipSignature signature = new ZipSignature();
      signature.initSign(keySet.getPrivateKey());

      //        	if (getLogger().isDebugEnabled()) {
      //        		getLogger().debug(String.format("Signature provider=%s, alg=%s, class=%s",
      //        				signature.getProvider().getName(),
      //        				signature.getAlgorithm(),
      //        				signature.getClass().getName()));
      //        	}

      ze = new ZioEntry(CERT_SF_NAME);
      ze.setTime(timestamp);

      ByteArrayOutputStream out = new ByteArrayOutputStream();
      generateSignatureFile(manifest, out);
      if (canceled) return;
      byte[] sfBytes = out.toByteArray();
      if (debug) {
        getLogger()
            .debug(
                "Signature File: \n" + new String(sfBytes) + "\n" + HexDumpEncoder.encode(sfBytes));
      }
      ze.getOutputStream().write(sfBytes);
      zipOutput.write(ze);
      signature.update(sfBytes);
      byte[] signatureBytes = signature.sign();

      if (debug) {

        MessageDigest md = MessageDigest.getInstance("SHA1");
        md.update(sfBytes);
        byte[] sfDigest = md.digest();
        getLogger().debug("Sig File SHA1: \n" + HexDumpEncoder.encode(sfDigest));

        getLogger().debug("Signature: \n" + HexDumpEncoder.encode(signatureBytes));

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySet.getPublicKey());

        byte[] tmpData = cipher.doFinal(signatureBytes);
        getLogger().debug("Signature Decrypted: \n" + HexDumpEncoder.encode(tmpData));

        //        		getLogger().debug( "SHA1 ID: \n" +
        // HexDumpEncoder.encode(AlgorithmId.get("SHA1").encode()));

        //        		// Compute signature using low-level APIs.
        //                byte[] beforeAlgorithmIdBytes =  { 0x30, 0x21 };
        //                // byte[] algorithmIdBytes = {0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03,
        // 0x02, 0x1A, 0x05, 0x00 };
        //                byte[] algorithmIdBytes =  AlgorithmId.get("SHA1").encode();
        //                byte[] afterAlgorithmIdBytes = { 0x04, 0x14 };
        //                cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        //                cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        //                getLogger().debug( "Cipher: " + cipher.getAlgorithm() + ", blockSize = " +
        // cipher.getBlockSize());
        //
        //                cipher.update( beforeAlgorithmIdBytes);
        //                cipher.update( algorithmIdBytes);
        //                cipher.update( afterAlgorithmIdBytes);
        //                cipher.update( sfDigest);
        //                byte[] tmpData2 = cipher.doFinal();
        //                getLogger().debug( "Signature : \n" + HexDumpEncoder.encode(tmpData2));

      }

      // CERT.RSA
      progressHelper.progress(ProgressEvent.PRORITY_NORMAL, "Generating signature block file");
      ze = new ZioEntry(CERT_RSA_NAME);
      ze.setTime(timestamp);
      writeSignatureBlock(
          keySet.getSigBlockTemplate(),
          signatureBytes,
          keySet.getPublicKey(),
          ze.getOutputStream());
      zipOutput.write(ze);
      if (canceled) return;

      // Everything else
      copyFiles(manifest, zioEntries, zipOutput, timestamp);
      if (canceled) return;

    } finally {
      zipOutput.close();
      if (canceled) {
        try {
          if (outputZipFilename != null) new File(outputZipFilename).delete();
        } catch (Throwable t) {
          getLogger().warning(t.getClass().getName() + ":" + t.getMessage());
        }
      }
    }
  }