Package org.eclipse.osgi.internal.verifier

Source Code of org.eclipse.osgi.internal.verifier.SignedBundleFile$SignedBundleEntry

/*******************************************************************************
* Copyright (c) 2006, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.osgi.internal.verifier;

import java.io.*;
import java.net.URL;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.internal.provisional.verifier.*;
import org.eclipse.osgi.util.NLS;

/**
* This class wraps a Repository of classes and resources to check and enforce
* signatures. It requires full signing of the manifest by all signers. If no
* signatures are found, the classes and resources are retrieved without checks.
*/
public class SignedBundleFile extends BundleFile implements CertificateVerifier, JarVerifierConstant {
  private static DefaultTrustAuthority trustAllAuthority = new DefaultTrustAuthority(0);
  private BundleFile bundleFile;
  CertificateChain[] chains;

  /**
   * The key of the hashtable will be the name of the entry (type String). The
   * value will be MessageDigest algorithm to use.
   */
  Hashtable digests4entries;
  /**
   * The key of the hashtable will be the name of the entry (type String). The
   * value will be byte[] which is an array of one MessageDigest result.
   */
  Hashtable results4entries;
  String manifestSHAResult = null;
  String manifestMD5Result = null;
  boolean certsInitialized = false;

  SignedBundleFile() {
    // default constructor
  }

  SignedBundleFile(CertificateChain[] chains, Hashtable digests4entries, Hashtable results4entries, String manifestMD5Result, String manifestSHAResult) {
    this.chains = chains;
    this.digests4entries = digests4entries;
    this.results4entries = results4entries;
    this.manifestMD5Result = manifestMD5Result;
    this.manifestSHAResult = manifestSHAResult;
    certsInitialized = true;
    //    isSigned = true;
  }

  /**
   * Verify the digest listed in each entry in the .SF file with corresponding section in the manifest
   */
  private void verifyManifestAndSingatureFile(byte[] manifestBytes, byte[] sfBytes) {

    String sf = new String(sfBytes);
    sf = stripContinuations(sf);

    // check if there -Digest-Manfiest: header in the file
    int off = sf.indexOf(digestManifestSearch);
    if (off != -1) {
      int start = sf.lastIndexOf('\n', off);
      String manfiestDigest = null;
      if (start != -1) {
        // Signature-Version has to start the file, so there
        // should always be a newline at the start of
        // Digest-Manifest
        String digestName = sf.substring(start + 1, off);
        if (digestName.equalsIgnoreCase(MD5_STR)) {
          if (manifestMD5Result == null)
            manifestMD5Result = calculateDigest(getMessageDigest(MD5_STR), manifestBytes);
          manfiestDigest = manifestMD5Result;
        } else if (digestName.equalsIgnoreCase(SHA1_STR)) {
          if (manifestSHAResult == null)
            manifestSHAResult = calculateDigest(getMessageDigest(SHA1_STR), manifestBytes);
          manfiestDigest = manifestSHAResult;
        }
        off += digestManifestSearchLen;

        // find out the index of first '\n' after the -Digest-Manifest:
        int nIndex = sf.indexOf('\n', off);
        String digestValue = sf.substring(off, nIndex - 1);

        // check if the the computed digest value of manifest file equals to the digest value in the .sf file
        if (!manfiestDigest.equals(digestValue)) {
          Exception e = new SecurityException(NLS.bind(JarVerifierMessages.Security_File_Is_Tampered, new String[] {bundleFile.getBaseFile().toString()}));
          SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
          throw (SecurityException) e;
        }
      }
    }

  }

  /**
   * Read the .SF file abd assuming that same digest algorithm will be used through out the whole
   * .SF file.  That digest algorithm name in the last entry will be returned.
   *
   * @param SFBuf      a .SF file in bytes
   * @return        the digest algorithm name used in the .SF file
   */
  private String getDigAlgFromSF(byte SFBuf[]) {
    String rtvValue = null;

    // need to make a string from the MF file data bytes
    String mfStr = new String(SFBuf);
    String entryStr = null;

    // start parsing each entry in the MF String
    int entryStartOffset = mfStr.indexOf(MF_ENTRY_NEWLN_NAME);
    int length = mfStr.length();

    while ((entryStartOffset != -1) && (entryStartOffset < length)) {

      // get the start of the next 'entry', i.e. the end of this entry
      int entryEndOffset = mfStr.indexOf(MF_ENTRY_NEWLN_NAME, entryStartOffset + 1);
      if (entryEndOffset == -1) {
        // if there is no next entry, then the end of the string
        // is the end of this entry
        entryEndOffset = mfStr.length();
      }

      // get the string for this entry only, since the entryStartOffset
      // points to the '\n' befor the 'Name: ' we increase it by 1
      // this is guaranteed to not go past end-of-string and be less
      // then entryEndOffset.
      entryStr = mfStr.substring(entryStartOffset + 1, entryEndOffset);
      entryStr = stripContinuations(entryStr);
      break;
    }

    if (entryStr != null) {
      // process the entry to retrieve the digest algorith name
      String digestLine = getDigestLine(entryStr, null);

      // throw parsing
      rtvValue = getMessageDigestName(digestLine);
    }

    return rtvValue;
  }

  /**
   * @param mfBuf the data from an MF file of a JAR archive
   *
   * This method will populate the "digest type & result" hashtables
   * with whatever entries it can correctly parse from the MF file and with the same digest algorithm.
   * it will 'skip' incorrect entries (TODO: should the correct behavior
   * be to throw an exception, or return an error code?)...
   *
   *
   */
  private void populateManifest(byte mfBuf[], String digAlg) {
    // need to make a string from the MF file data bytes
    String mfStr = new String(mfBuf);

    // start parsing each entry in the MF String
    int entryStartOffset = mfStr.indexOf(MF_ENTRY_NEWLN_NAME);
    int length = mfStr.length();

    while ((entryStartOffset != -1) && (entryStartOffset < length)) {

      // get the start of the next 'entry', i.e. the end of this entry
      int entryEndOffset = mfStr.indexOf(MF_ENTRY_NEWLN_NAME, entryStartOffset + 1);
      if (entryEndOffset == -1) {
        // if there is no next entry, then the end of the string
        // is the end of this entry
        entryEndOffset = mfStr.length();
      }

      // get the string for this entry only, since the entryStartOffset
      // points to the '\n' befor the 'Name: ' we increase it by 1
      // this is guaranteed to not go past end-of-string and be less
      // then entryEndOffset.
      String entryStr = mfStr.substring(entryStartOffset + 1, entryEndOffset);
      entryStr = stripContinuations(entryStr);

      // entry points to the start of the next 'entry'
      String entryName = getEntryFileName(entryStr);

      // if we could retrieve an entry name, then we will extract
      // digest type list, and the digest value list
      if (entryName != null) {

        String aDigestLine = getDigestLine(entryStr, digAlg);

        if (aDigestLine != null) {
          String msgDigestAlgorithm = getDigestAlgorithmFromString(aDigestLine);
          byte digestResultsList[] = getDigestResultsList(aDigestLine);

          //
          // only insert this entry into the table if its
          // "well-formed",
          // i.e. only if we could extract its name, digest types, and
          // digest-results
          //
          // sanity check, if the 2 lists are non-null, then their
          // counts must match
          //
          //          if ((msgDigestObj != null) && (digestResultsList != null)
          //              && (1 != digestResultsList.length)) {
          //            throw new RuntimeException(
          //                "Errors occurs when parsing the manifest file stream!"); //$NON-NLS-1$
          //          }

          if (digests4entries == null) {
            digests4entries = new Hashtable(10);
            results4entries = new Hashtable(10);
          }
          // TODO throw exception if duplicate entry??
          // no need, in theory, there is impossible to have two
          // duplicate entries unless the manifest file
          // is tampered
          if (!digests4entries.contains(entryName)) {
            digests4entries.put(entryName, msgDigestAlgorithm);
            results4entries.put(entryName, digestResultsList);
          }

        } // could get lines of digest entries in this MF file entry

      } // could retrieve entry name

      // increment the offset to the ending entry...
      entryStartOffset = entryEndOffset;
    }
  }

  private String stripContinuations(String entry) {
    if (entry.indexOf("\n ") < 0) //$NON-NLS-1$
      return entry;
    StringBuffer buffer = new StringBuffer(entry.length());
    int cont = entry.indexOf("\n "); //$NON-NLS-1$
    int start = 0;
    while (cont >= 0) {
      buffer.append(entry.substring(start, cont - 1));
      start = cont + 2;
      cont = cont + 2 < entry.length() ? entry.indexOf("\n ", cont + 2) : -1; //$NON-NLS-1$
    }
    // get the last one continuation
    if (start < entry.length())
      buffer.append(entry.substring(start));
    return buffer.toString();
  }

  private String getEntryFileName(String manifestEntry) {
    // get the beginning of the name
    int nameStart = manifestEntry.indexOf(MF_ENTRY_NAME);
    if (nameStart == -1) {
      return null;
    }
    // check where the name ends
    int nameEnd = manifestEntry.indexOf('\n', nameStart);
    if (nameEnd == -1) {
      return null;
    }
    // if there is a '\r' before the '\n', then we'll strip it
    if (manifestEntry.charAt(nameEnd - 1) == '\r') {
      nameEnd--;
    }
    // get to the beginning of the actual name...
    nameStart += MF_ENTRY_NAME.length();
    if (nameStart >= nameEnd) {
      return null;
    }
    return manifestEntry.substring(nameStart, nameEnd);
  }

  /**
   *
   * @param manifestEntry contains a single MF file entry of the format
   *            "Name: foo"
   *            "MD5-Digest: [base64 encoded MD5 digest data]"
   *            "SHA1-Digest: [base64 encoded SHA1 digest dat]"
   *
   * @param  manifestEntry  a entry contains either one or multiple digest lines
   * @param  desireDigestAlg  a string representing the desire digest value to be returned if there are
   *               multiple digest lines.
   *               If this value is null, return whatever digest value is in the entry.
   *
   * @return this function returns a digest line based on the desire digest algorithm value
   *        (since only MD5 and SHA1 are recognized here),
   *        or a 'null' will be returned if none of the digest algorithms
   *        were recognized.
   */
  private String getDigestLine(String manifestEntry, String desireDigestAlg) {
    String rtvValue = null;

    // find the first digest line
    int indexDigest = manifestEntry.indexOf(MF_DIGEST_PART);
    // if we didn't find any digests at all, then we are done
    if (indexDigest == -1)
      return null;

    // while we continue to find digest entries
    // note: in the following loop we bail if any of the lines
    //     look malformed...
    while (indexDigest != -1) {
      // see where this digest line begins (look to left)
      int indexStart = manifestEntry.lastIndexOf('\n', indexDigest);
      if (indexStart == -1)
        return null;
      // see where it ends (look to right)
      int indexEnd = manifestEntry.indexOf('\n', indexDigest);
      if (indexEnd == -1)
        return null;
      // strip off ending '\r', if any
      int indexEndToUse = indexEnd;
      if (manifestEntry.charAt(indexEndToUse - 1) == '\r')
        indexEndToUse--;
      // indexStart points to the '\n' before this digest line
      int indexStartToUse = indexStart + 1;
      if (indexStartToUse >= indexEndToUse)
        return null;

      // now this may be a valid digest line, parse it a bit more
      // to see if this is a preferred digest algorithm
      String digestLine = manifestEntry.substring(indexStartToUse, indexEndToUse);
      String digAlg = getMessageDigestName(digestLine);
      if (desireDigestAlg != null && desireDigestAlg.equalsIgnoreCase(digAlg)) {
        rtvValue = digestLine;
        break;
      }

      // desireDigestAlg is null, always return the
      rtvValue = digestLine;

      // iterate to next digest line in this entry
      indexDigest = manifestEntry.indexOf(MF_DIGEST_PART, indexEnd);
    }

    // if we couldn't find any digest lines, then we are done
    return rtvValue;
  }

  private String getDigestAlgorithmFromString(String digestLines) {
    if (digestLines != null) {
      // String sDigestLine = digestLines[i];
      int indexDigest = digestLines.indexOf(MF_DIGEST_PART);
      String sDigestAlgType = digestLines.substring(0, indexDigest);
      if (sDigestAlgType.equalsIgnoreCase(MD5_STR)) {
        // remember the "algorithm type"
        return MD5_STR;
      } else if (sDigestAlgType.equalsIgnoreCase(SHA1_STR)) {
        // remember the "algorithm type" object
        return SHA1_STR;
      } else {
        // unknown algorithm type, we will stop processing this entry
        // break;
        throw new SecurityException(NLS.bind(JarVerifierMessages.Algorithm_Not_Supported, sDigestAlgType));
      }
    }
    return null;
  }

  /**
   * Return the Message Digest name
   *
   * @param digLine    the message digest line is in the following format.  That is in the
   *             following format:
   *                 DIGEST_NAME-digest: digest value
   * @return        a string representing a message digest.
   */
  private String getMessageDigestName(String digLine) {
    String rtvValue = null;
    if (digLine != null) {
      int indexDigest = digLine.indexOf(MF_DIGEST_PART);
      if (indexDigest != -1) {
        rtvValue = digLine.substring(0, indexDigest);
      }
    }
    return rtvValue;
  }

  private byte[] getDigestResultsList(String digestLines) {
    byte resultsList[] = null;
    if (digestLines != null) {
      // for each digest-line retrieve the digest result
      // for (int i = 0; i < digestLines.length; i++) {
      String sDigestLine = digestLines;
      int indexDigest = sDigestLine.indexOf(MF_DIGEST_PART);
      indexDigest += MF_DIGEST_PART.length();
      // if there is no data to extract for this digest value
      // then we will fail...
      if (indexDigest >= sDigestLine.length()) {
        resultsList = null;
        // break;
      }
      // now attempt to base64 decode the result
      String sResult = sDigestLine.substring(indexDigest);
      try {
        resultsList = Base64.decode(sResult.getBytes());
      } catch (Throwable t) {
        // malformed digest result, no longer processing this entry
        resultsList = null;
      }
    }
    return resultsList;
  }

  static private int readFully(InputStream is, byte b[]) throws IOException {
    int count = b.length;
    int offset = 0;
    int rc;
    while ((rc = is.read(b, offset, count)) > 0) {
      count -= rc;
      offset += rc;
    }
    return offset;
  }

  byte[] readIntoArray(BundleEntry be) throws IOException {
    int size = (int) be.getSize();
    InputStream is = be.getInputStream();
    byte b[] = new byte[size];
    int rc = readFully(is, b);
    if (rc != size) {
      throw new IOException("Couldn't read all of " + be.getName() + ": " + rc + " != " + size); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }
    return b;
  }

  /**
   * Sets the BundleFile for this singed bundle. It will extract
   * signatures and digests from the bundle file and validate input streams
   * before using them from the bundle file.
   *
   * @param bundleFile the BundleFile to extract elements from.
   * @param the support flags for this signed bundle
   * @throws IOException
   */
  void setBundleFile(BundleFile bundleFile, int supportFlags) throws IOException {
    this.bundleFile = bundleFile;
    if (certsInitialized)
      return;
    BundleEntry be = bundleFile.getEntry(META_INF_MANIFEST_MF);
    if (be == null)
      return;

    // read all the signature block file names into a list
    Enumeration en = bundleFile.getEntryPaths(META_INF);
    List signers = new ArrayList(2);
    while (en.hasMoreElements()) {
      String name = (String) en.nextElement();
      if ((name.endsWith(DOT_DSA) || name.endsWith(DOT_RSA)) && name.indexOf('/') == name.lastIndexOf('/'))
        signers.add(name);
    }

    // this means the jar is not signed
    if (signers.size() == 0)
      return;

    byte manifestBytes[] = readIntoArray(be);
    // determine the signer to be used for later
    String latestSigner = findLatestSigner(bundleFile, signers);
    // process the signers
    try {
      ArrayList processors = new ArrayList(signers.size());
      Iterator iSigners = signers.iterator();
      for (int i = 0; iSigners.hasNext(); i++) {
        String signer = (String) iSigners.next();
        PKCS7Processor processor = processSigner(bundleFile, manifestBytes, signer, latestSigner, supportFlags);
        boolean error = false;
        try {
          processor.validateCerts();
          determineCertsTrust(processor, supportFlags);
        } catch (CertificateExpiredException e) {
          // ignore
        } catch (CertificateNotYetValidException e) {
          // ignore
        } catch (InvalidKeyException e) {
          error = true;
        }
        if (!error) {
          // make sure the latestSigner is the first in the list
          if (signer == latestSigner)
            processors.add(0, processor);
          else
            processors.add(processor);
        }
      }
      chains = processors.size() == 0 ? null : (CertificateChain[]) processors.toArray(new CertificateChain[processors.size()]);
    } catch (SignatureException e) {
      throw new SecurityException(NLS.bind(JarVerifierMessages.Signature_Not_Verify_1, new String[] {latestSigner, bundleFile.toString()}));
    }
  }

  private void determineCertsTrust(PKCS7Processor signerPKCS7, int supportFlags) {
    CertificateTrustAuthority trustAuthority;
    if ((supportFlags & SignedBundleHook.VERIFY_TRUST) != 0)
      trustAuthority = SignedBundleHook.getTrustAuthority();
    else
      trustAuthority = trustAllAuthority;
    if (trustAuthority != null)
      signerPKCS7.determineTrust(trustAuthority);
  }

  private PKCS7Processor processSigner(BundleFile bf, byte[] manifestBytes, String signer, String latestSigner, int supportFlags) throws IOException, SignatureException {
    BundleEntry be = bf.getEntry(signer);
    byte pkcs7Bytes[] = readIntoArray(be);
    int dotIndex = signer.lastIndexOf('.');
    be = bf.getEntry(signer.substring(0, dotIndex) + DOT_SF);
    byte sfBytes[] = readIntoArray(be);

    // Step 1, verify the .SF file is signed by the private key that corresponds to the public key
    // in the .RSA/.DSA file
    PKCS7Processor chain = null;
    try {

      chain = new PKCS7Processor(pkcs7Bytes, 0, pkcs7Bytes.length);

      // call the Step 1 in the Jar File Verification algorithm
      chain.verifySFSignature(sfBytes, 0, sfBytes.length);

      // algorithm used
      String digAlg = getDigAlgFromSF(sfBytes);
      if (digAlg == null) {
        throw new SecurityException(NLS.bind(JarVerifierMessages.SF_File_Parsing_Error, new String[] {bf.toString()}));
      }
      // populate the two digest hashtable instance variable based on the used Message Digest algorithm
      if (latestSigner == signer) { // only do this if it is the latest signer

        // Process the Step 2 in the Jar File Verification algorithm
        // Get the manifest out of the signature file and make sure
        // it matches MANIFEST.MF
        verifyManifestAndSingatureFile(manifestBytes, sfBytes);

        // only populate the manifests if we are verifying content at runtime
        if ((supportFlags & SignedBundleHook.VERIFY_RUNTIME) != 0)
          populateManifest(manifestBytes, digAlg);
      }

      // process the Step 3
      // read each file in the JAR file that has an entry in the .SF file. 
      // also determine the
      //        verifySFEntriesDigest(bf, sfBytes, manifestBytes);
    } catch (InvalidKeyException e) {
      SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
      throw new SecurityException(NLS.bind(JarVerifierMessages.Invalid_Key_Exception, new String[] {bf.getBaseFile().toString(), e.getMessage()}));
    } catch (CertificateException e) {
      SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
      throw new SecurityException(NLS.bind(JarVerifierMessages.PKCS7_Cert_Excep, new String[] {bf.getBaseFile().toString(), e.getMessage()}));
    } catch (NoSuchAlgorithmException e) {
      SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
      throw new SecurityException(NLS.bind(JarVerifierMessages.PKCS7_No_Such_Algorithm, new String[] {bf.getBaseFile().toString(), e.getMessage()}));
    }

    return chain;
  }

  private String findLatestSigner(BundleFile bf, List names) {
    String result = null;
    long latestTime = Long.MIN_VALUE;
    for (Iterator iNames = names.iterator(); iNames.hasNext();) {
      String name = (String) iNames.next();
      BundleEntry entry = bf.getEntry(name);
      if (entry.getTime() > latestTime) {
        result = name;
        latestTime = entry.getTime();
      }
    }
    return result;
  }

  /**
   * Returns the Base64 encoded digest of the passed set of bytes.
   */
  private String calculateDigest(MessageDigest digest, byte[] bytes) {
    return new String(Base64.encode(digest.digest(bytes)));
  }

  public File getFile(String path, boolean nativeCode) {
    return bundleFile.getFile(path, nativeCode);
  }

  public BundleEntry getEntry(String path) {
    // strip off leading slashes so we can ensure the path matches the one provided in the manifest.
    if (path.length() > 0 && path.charAt(0) == '/')
      path = path.substring(1);
    BundleEntry be = bundleFile.getEntry(path);
    if (digests4entries == null)
      return be;
    if (be == null) {
      if (digests4entries.get(path) == null)
        return null;
      throw new SecurityException(NLS.bind(JarVerifierMessages.file_is_removed_from_jar, getBaseFile().toString(), path));
    }
    if (be.getName().startsWith(META_INF))
      return be;
    if (!isSigned())
      // If there is no signatures, we just return the regular bundle entry
      return be;
    return new SignedBundleEntry(be);
  }

  public Enumeration getEntryPaths(String path) {
    return bundleFile.getEntryPaths(path);
  }

  public void close() throws IOException {
    bundleFile.close();
  }

  public void open() throws IOException {
    bundleFile.open();
  }

  public boolean containsDir(String dir) {
    return bundleFile.containsDir(dir);
  }

  boolean matchDNChain(String pattern) {
    CertificateChain[] matchChains = getChains();
    for (int i = 0; i < matchChains.length; i++)
      if (matchChains[i].isTrusted() && DNChainMatching.match(matchChains[i].getChain(), pattern))
        return true;
    return false;
  }

  public File getBaseFile() {
    return bundleFile.getBaseFile();
  }

  class SignedBundleEntry extends BundleEntry {
    BundleEntry nestedEntry;

    SignedBundleEntry(BundleEntry nestedEntry) {
      this.nestedEntry = nestedEntry;
    }

    public InputStream getInputStream() throws IOException {
      String name = getName();
      String digest = digests4entries == null ? null : (String) digests4entries.get(name);
      if (digest == null)
         // the digest does not exist; this must be a corrupted jar
        throw new IOException("Corrupted file: the digest does not exist for the file " + name); //$NON-NLS-1$
       byte results[] = (byte[]) results4entries.get(name);
      // TODO ELI: it is probbaly best to decode the value of digest into Base64 base.  This will only optimize the bad jar case.
      return new DigestedInputStream(nestedEntry.getInputStream(), digest, results, nestedEntry.getSize());
    }

    public long getSize() {
      return nestedEntry.getSize();
    }

    public String getName() {
      return nestedEntry.getName();
    }

    public long getTime() {
      return nestedEntry.getTime();
    }

    public URL getLocalURL() {
      return nestedEntry.getLocalURL();
    }

    public URL getFileURL() {
      return nestedEntry.getFileURL();
    }

  }

  public void checkContent() throws CertificateException, CertificateExpiredException, SignatureException {
    if (!isSigned() || digests4entries == null)
      return;

    for (Enumeration entries = digests4entries.keys(); entries.hasMoreElements();) {
      String name = (String) entries.nextElement();
      BundleEntry entry = getEntry(name);
      if (entry == null) {
        throw new SecurityException(NLS.bind(JarVerifierMessages.Jar_Is_Tampered, bundleFile.getBaseFile().getName()));
      }
      try {
        entry.getBytes();
      } catch (IOException e) {
        SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
        throw new SecurityException(NLS.bind(JarVerifierMessages.File_In_Jar_Is_Tampered, new String[] {name, bundleFile.getBaseFile().toString()}));
      }
    }

    // validate the certs chain and determine the trust
    for (int i = 0; i < chains.length; i++) {
      PKCS7Processor signerPKCS7 = (PKCS7Processor) chains[i];
      try {
        signerPKCS7.validateCerts();
      } catch (InvalidKeyException e) {
        throw new CertificateException(e.getMessage());
      }
      // determine the trust of certificates
      determineCertsTrust(signerPKCS7, SignedBundleHook.VERIFY_ALL);
    }
  }

  public String[] verifyContent() {
    if (!isSigned() || digests4entries == null)
      return EMPTY_STRING;
    ArrayList corrupted = new ArrayList(0); // be optimistic
    for (Enumeration entries = digests4entries.keys(); entries.hasMoreElements();) {
      String name = (String) entries.nextElement();
      BundleEntry entry = getEntry(name);
      if (entry == null)
        corrupted.add(name); // we expected the entry to be here
      else
        try {
          entry.getBytes();
        } catch (IOException e) {
          // must be invalid
          corrupted.add(name);
        }
    }
    return corrupted.size() == 0 ? EMPTY_STRING : (String[]) corrupted.toArray(new String[corrupted.size()]);
  }

  public CertificateChain[] getChains() {
    if (!isSigned())
      return new CertificateChain[0];
    return chains;
  }

  public boolean isSigned() {
    return chains != null;
  }

  static synchronized MessageDigest getMessageDigest(String algorithm) {
    try {
      return MessageDigest.getInstance(algorithm);
    } catch (NoSuchAlgorithmException e) {
      SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
    }
    return null;
  }

  //  static public void main(String args[]) throws IOException {
  //
  //    ZipBundleFile jf = new ZipBundleFile(new File(args[0]), null);
  //    SignedBundleFile sr = new SignedBundleFile();
  //
  //    sr.setBundleFile(jf);
  //
  //    // read the first level directory entries
  //    Enumeration en = sr.getEntryPaths("/"); //$NON-NLS-1$
  //    while (en.hasMoreElements()) {
  //      String filePath = (String) en.nextElement();
  //      System.out.println("main(): " + filePath); //$NON-NLS-1$
  //
  //      // if this file is not a directory file
  //      // then we'll get its input stream for testing
  //      if (filePath.indexOf('/') == -1) {
  //        BundleEntry be = sr.getEntry(filePath);
  //        InputStream is = be.getInputStream();
  //        is.skip(be.getSize());
  //        is.read();
  //        is.close();
  //      }
  //    }
  //
  //    if (!sr.isSigned()) {
  //      System.out.println("No signers present"); //$NON-NLS-1$
  //    } else {
  //      CertificateChain[] chains = sr.getChains();
  //      for (int i = 0; i < chains.length; i++) {
  //        System.out.println(chains[i].getChain());
  //      }
  //    }
  //
  //    System.out.println("Done"); //$NON-NLS-1$   
  //  }
}
TOP

Related Classes of org.eclipse.osgi.internal.verifier.SignedBundleFile$SignedBundleEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.