| /******************************************************************************* |
| * 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.security.cert.*; |
| import java.util.*; |
| import org.eclipse.osgi.baseadaptor.BaseData; |
| import org.eclipse.osgi.baseadaptor.hooks.StorageHook; |
| import org.eclipse.osgi.framework.util.KeyedElement; |
| import org.eclipse.osgi.internal.provisional.verifier.CertificateChain; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleException; |
| |
| public class SignedStorageHook implements StorageHook { |
| static final String KEY = SignedStorageHook.class.getName(); |
| static final int HASHCODE = KEY.hashCode(); |
| private static final int STORAGE_VERSION = 2; |
| private static ArrayList saveChainCache = new ArrayList(5); |
| private static long firstIDSaved = -1; |
| private static long lastIDSaved = -1; |
| private static ArrayList loadChainCache = new ArrayList(5); |
| private static long lastIDLoaded; |
| |
| private BaseData bundledata; |
| SignedBundleFile signedBundleFile; |
| |
| public int getStorageVersion() { |
| return STORAGE_VERSION; |
| } |
| |
| public StorageHook create(BaseData bundledata) throws BundleException { |
| SignedStorageHook hook = new SignedStorageHook(); |
| hook.bundledata = bundledata; |
| return hook; |
| } |
| |
| public void initialize(Dictionary manifest) throws BundleException { |
| // do nothing |
| } |
| |
| public StorageHook load(BaseData target, DataInputStream is) throws IOException { |
| if (lastIDLoaded > target.getBundleID()) |
| loadChainCache.clear(); |
| lastIDLoaded = target.getBundleID(); |
| SignedStorageHook hook = new SignedStorageHook(); |
| hook.bundledata = target; |
| boolean signed = is.readBoolean(); |
| if (!signed) |
| return hook; |
| int numChains = is.readInt(); |
| CertificateChain[] chains = new CertificateChain[numChains]; |
| for (int i = 0; i < numChains; i++) { |
| int chainIdx = is.readInt(); |
| if (chainIdx >= 0) { |
| chains[i] = (CertificateChain) loadChainCache.get(chainIdx); |
| if (chains[i] == null) |
| throw new IOException("Invalid chain cache."); //$NON-NLS-1$ |
| continue; |
| } |
| String chain = is.readUTF(); |
| |
| boolean trusted = is.readBoolean(); |
| int numCerts = is.readInt(); |
| byte[][] certsBytes = new byte[numCerts][]; |
| for (int j = 0; j < certsBytes.length; j++) { |
| int numBytes = is.readInt(); |
| certsBytes[j] = new byte[numBytes]; |
| is.readFully(certsBytes[j]); |
| } |
| long signingTime = is.readLong(); |
| try { |
| chains[i] = new PKCS7Processor(chain, trusted, certsBytes, signingTime); |
| } catch (CertificateException e) { |
| throw new IOException(e.getMessage()); |
| } |
| loadChainCache.add(chains[i]); |
| } |
| int numEntries = is.readInt(); |
| Hashtable digests = null; |
| Hashtable results = null; |
| if (numEntries >= 0) { |
| digests = new Hashtable(numEntries); |
| results = new Hashtable(numEntries); |
| for (int i = 0; i < numEntries; i++) { |
| String entry = is.readUTF(); |
| String md; |
| byte[] result; |
| if (is.readInt() == 0) |
| md = JarVerifierConstant.MD5_STR; |
| else |
| md = JarVerifierConstant.SHA1_STR; |
| result = new byte[is.readInt()]; |
| is.readFully(result); |
| digests.put(entry, md); |
| results.put(entry, result); |
| } |
| } |
| String md5Result = null; |
| String shaResult = null; |
| if (is.readBoolean()) |
| md5Result = is.readUTF(); |
| if (is.readBoolean()) |
| shaResult = is.readUTF(); |
| hook.signedBundleFile = new SignedBundleFile(chains, digests, results, md5Result, shaResult); |
| return hook; |
| } |
| |
| public void save(DataOutputStream os) throws IOException { |
| getFirstLastID(); |
| if (firstIDSaved == bundledata.getBundleID()) |
| saveChainCache.clear(); |
| if (lastIDSaved == bundledata.getBundleID()) |
| firstIDSaved = lastIDSaved = -1; |
| SignedBundleFile signedFile = signedBundleFile; |
| CertificateChain[] chains = null; |
| String md5Result = null; |
| String shaResult = null; |
| Hashtable digests = null; |
| Hashtable results = null; |
| if (signedFile != null) { |
| chains = signedFile.chains; |
| md5Result = signedFile.manifestMD5Result; |
| shaResult = signedFile.manifestSHAResult; |
| digests = signedFile.digests4entries; |
| results = signedFile.results4entries; |
| } |
| os.writeBoolean(chains != null); |
| if (chains == null) |
| return; |
| os.writeInt(chains.length); |
| for (int i = 0; i < chains.length; i++) { |
| int cacheIdx = saveChainCache.indexOf(chains[i]); |
| os.writeInt(cacheIdx); |
| if (cacheIdx >= 0) |
| continue; |
| saveChainCache.add(chains[i]); |
| os.writeUTF(chains[i].getChain()); |
| os.writeBoolean(chains[i].isTrusted()); |
| Certificate[] certs = chains[i].getCertificates(); |
| os.writeInt(certs == null ? 0 : certs.length); |
| if (certs != null) |
| for (int j = 0; j < certs.length; j++) { |
| byte[] certBytes; |
| try { |
| certBytes = certs[j].getEncoded(); |
| } catch (CertificateEncodingException e) { |
| throw new IOException(e.getMessage()); |
| } |
| os.writeInt(certBytes.length); |
| os.write(certBytes); |
| } |
| os.writeLong(chains[i].getSigningTime() != null ? chains[i].getSigningTime().getTime() : Long.MIN_VALUE); |
| } |
| if (digests == null) |
| os.writeInt(-1); |
| else { |
| os.writeInt(digests.size()); |
| for (Enumeration entries = digests.keys(); entries.hasMoreElements();) { |
| String entry = (String) entries.nextElement(); |
| String md = (String) digests.get(entry); |
| byte[] result = (byte[]) results.get(entry); |
| os.writeUTF(entry); |
| if (md == JarVerifierConstant.MD2_STR) |
| os.writeInt(0); |
| else |
| os.writeInt(1); |
| os.writeInt(result.length); |
| os.write(result); |
| } |
| } |
| os.writeBoolean(md5Result != null); |
| if (md5Result != null) |
| os.writeUTF(md5Result); |
| os.writeBoolean(shaResult != null); |
| if (shaResult != null) |
| os.writeUTF(shaResult); |
| } |
| |
| private void getFirstLastID() { |
| if (firstIDSaved >= 0) |
| return; |
| Bundle[] bundles = bundledata.getAdaptor().getContext().getBundles(); |
| if (bundles.length > 1) { |
| firstIDSaved = bundles[1].getBundleId(); |
| lastIDSaved = bundles[bundles.length - 1].getBundleId(); |
| } |
| } |
| |
| public void copy(StorageHook storageHook) { |
| // do nothing |
| } |
| |
| public void validate() throws IllegalArgumentException { |
| // do nothing |
| } |
| |
| public Dictionary getManifest(boolean firstLoad) throws BundleException { |
| // do nothing |
| return null; |
| } |
| |
| public boolean forgetStatusChange(int status) { |
| // do nothing |
| return false; |
| } |
| |
| public boolean forgetStartLevelChange(int startlevel) { |
| // do nothing |
| return false; |
| } |
| |
| public boolean matchDNChain(String pattern) { |
| return signedBundleFile == null ? false : signedBundleFile.matchDNChain(pattern); |
| } |
| |
| public int getKeyHashCode() { |
| return HASHCODE; |
| } |
| |
| public boolean compare(KeyedElement other) { |
| return other.getKey() == KEY; |
| } |
| |
| public Object getKey() { |
| return KEY; |
| } |
| |
| } |