| /******************************************************************************* |
| * Copyright (c) 2007, 2008 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.signedcontent; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.cert.*; |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry; |
| import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile; |
| import org.eclipse.osgi.signedcontent.*; |
| import org.eclipse.osgi.util.NLS; |
| |
| public class SignedContentImpl implements SignedContent { |
| final static SignerInfo[] EMPTY_SIGNERINFO = new SignerInfo[0]; |
| // the content which is signed |
| volatile SignedBundleFile content; // TODO can this be more general? |
| // the content entry md results used for entry content verification |
| // keyed by entry path -> {SignerInfo[] infos, byte[][] results)} |
| private final HashMap contentMDResults; |
| private final SignerInfo[] signerInfos; |
| // map of tsa singers keyed by SignerInfo -> {tsa_SignerInfo, signingTime} |
| private HashMap tsaSignerInfos; |
| volatile private boolean checkedValid = false; |
| |
| public SignedContentImpl(SignerInfo[] signerInfos, HashMap contentMDResults) { |
| this.signerInfos = signerInfos == null ? EMPTY_SIGNERINFO : signerInfos; |
| this.contentMDResults = contentMDResults; |
| } |
| |
| public SignedContentEntry[] getSignedEntries() { |
| if (contentMDResults == null) |
| return new SignedContentEntry[0]; |
| ArrayList results = new ArrayList(contentMDResults.size()); |
| for (Iterator iMDResults = contentMDResults.entrySet().iterator(); iMDResults.hasNext();) { |
| Entry entry = (Entry) iMDResults.next(); |
| String entryName = (String) entry.getKey(); |
| Object[] mdResult = (Object[]) entry.getValue(); |
| results.add(new SignedContentEntryImpl(entryName, (SignerInfo[]) mdResult[0])); |
| } |
| return (SignedContentEntry[]) results.toArray(new SignedContentEntry[results.size()]); |
| } |
| |
| public SignedContentEntry getSignedEntry(String name) { |
| if (contentMDResults == null) |
| return null; |
| Object[] mdResult = (Object[]) contentMDResults.get(name); |
| return mdResult == null ? null : new SignedContentEntryImpl(name, (SignerInfo[]) mdResult[0]); |
| } |
| |
| public SignerInfo[] getSignerInfos() { |
| return signerInfos; |
| } |
| |
| public Date getSigningTime(SignerInfo signerInfo) { |
| if (tsaSignerInfos == null) |
| return null; |
| Object[] tsaInfo = (Object[]) tsaSignerInfos.get(signerInfo); |
| return tsaInfo == null ? null : (Date) tsaInfo[1]; |
| } |
| |
| public SignerInfo getTSASignerInfo(SignerInfo signerInfo) { |
| if (tsaSignerInfos == null) |
| return null; |
| Object[] tsaInfo = (Object[]) tsaSignerInfos.get(signerInfo); |
| return tsaInfo == null ? null : (SignerInfo) tsaInfo[0]; |
| } |
| |
| public boolean isSigned() { |
| return signerInfos.length > 0; |
| } |
| |
| public void checkValidity(SignerInfo signer) throws CertificateExpiredException, CertificateNotYetValidException { |
| Date signingTime = getSigningTime(signer); |
| if (checkedValid) |
| return; |
| Certificate[] certs = signer.getCertificateChain(); |
| for (int i = 0; i < certs.length; i++) { |
| if (!(certs[i] instanceof X509Certificate)) |
| continue; |
| if (signingTime == null) |
| ((X509Certificate) certs[i]).checkValidity(); |
| else |
| ((X509Certificate) certs[i]).checkValidity(signingTime); |
| } |
| checkedValid = true; |
| } |
| |
| void setContent(SignedBundleFile content) { |
| this.content = content; |
| } |
| |
| void setTSASignerInfos(HashMap tsaSignerInfos) { |
| this.tsaSignerInfos = tsaSignerInfos; |
| } |
| |
| void addTSASignerInfo(SignerInfo baseInfo, SignerInfo tsaSignerInfo, Date signingTime) { |
| // sanity check to make sure the baseInfo is here |
| if (!containsInfo(baseInfo)) |
| throw new IllegalArgumentException("The baseInfo is not found"); //$NON-NLS-1$ |
| if (tsaSignerInfos == null) |
| tsaSignerInfos = new HashMap(signerInfos.length); |
| tsaSignerInfos.put(baseInfo, new Object[] {tsaSignerInfo, signingTime}); |
| } |
| |
| HashMap getContentMDResults() { |
| return contentMDResults; |
| } |
| |
| private boolean containsInfo(SignerInfo signerInfo) { |
| for (int i = 0; i < signerInfos.length; i++) |
| if (signerInfo == signerInfos[i]) |
| return true; |
| return false; |
| } |
| |
| InputStream getDigestInputStream(BundleEntry nestedEntry) throws IOException { |
| if (contentMDResults == null) |
| return nestedEntry.getInputStream(); |
| Object[] mdResult = (Object[]) contentMDResults.get(nestedEntry.getName()); |
| if (mdResult == null) |
| return null; |
| return new DigestedInputStream(nestedEntry, content, (SignerInfo[]) mdResult[0], (byte[][]) mdResult[1], nestedEntry.getSize()); |
| } |
| |
| public class SignedContentEntryImpl implements SignedContentEntry { |
| private final String entryName; |
| private final SignerInfo[] entrySigners; |
| |
| public SignedContentEntryImpl(String entryName, SignerInfo[] entrySigners) { |
| this.entryName = entryName; |
| this.entrySigners = entrySigners == null ? EMPTY_SIGNERINFO : entrySigners; |
| } |
| |
| public String getName() { |
| return entryName; |
| } |
| |
| public SignerInfo[] getSignerInfos() { |
| return entrySigners; |
| } |
| |
| public boolean isSigned() { |
| return entrySigners.length > 0; |
| } |
| |
| public void verify() throws IOException, InvalidContentException { |
| BundleFile currentContent = content; |
| if (currentContent == null) |
| throw new InvalidContentException("The content was not set", null); //$NON-NLS-1$ |
| BundleEntry entry = null; |
| SecurityException exception = null; |
| try { |
| entry = currentContent.getEntry(entryName); |
| } catch (SecurityException e) { |
| exception = e; |
| } |
| if (entry == null) |
| throw new InvalidContentException(NLS.bind(SignedContentMessages.file_is_removed_from_jar, entryName, currentContent.getBaseFile().toString()), exception); |
| entry.getBytes(); |
| } |
| } |
| } |