2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 * @author Boris Kuznetsov
22 package org.apache.harmony.security.utils;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.math.BigInteger;
27 import java.security.GeneralSecurityException;
28 import java.security.MessageDigest;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.Principal;
31 import java.security.Signature;
32 import java.security.cert.Certificate;
33 import java.security.cert.X509Certificate;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Iterator;
37 import java.util.LinkedList;
38 import java.util.List;
39 import javax.security.auth.x500.X500Principal;
40 import org.apache.harmony.security.asn1.BerInputStream;
41 import org.apache.harmony.security.pkcs7.ContentInfo;
42 import org.apache.harmony.security.pkcs7.SignedData;
43 import org.apache.harmony.security.pkcs7.SignerInfo;
44 import org.apache.harmony.security.provider.cert.X509CertImpl;
45 import org.apache.harmony.security.x501.AttributeTypeAndValue;
46 import org.apache.harmony.xnet.provider.jsse.OpenSSLSignature;
48 public class JarUtils {
50 // as defined in PKCS #9: Selected Attribute Types:
51 // http://www.ietf.org/rfc/rfc2985.txt
52 private static final int[] MESSAGE_DIGEST_OID =
53 new int[] {1, 2, 840, 113549, 1, 9, 4};
56 * This method handle all the work with PKCS7, ASN1 encoding, signature verifying,
57 * and certification path building.
58 * See also PKCS #7: Cryptographic Message Syntax Standard:
59 * http://www.ietf.org/rfc/rfc2315.txt
60 * @param signature - the input stream of signature file to be verified
61 * @param signatureBlock - the input stream of corresponding signature block file
62 * @return array of certificates used to verify the signature file
63 * @throws IOException - if some errors occurs during reading from the stream
64 * @throws GeneralSecurityException - if signature verification process fails
66 public static Certificate[] verifySignature(InputStream signature, InputStream
67 signatureBlock) throws IOException, GeneralSecurityException {
69 BerInputStream bis = new BerInputStream(signatureBlock);
70 ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);
71 SignedData signedData = info.getSignedData();
72 if (signedData == null) {
73 throw new IOException("No SignedData found");
75 Collection encCerts = signedData.getCertificates();
76 if (encCerts.isEmpty()) {
79 X509Certificate[] certs = new X509Certificate[encCerts.size()];
81 for (Iterator it = encCerts.iterator(); it.hasNext();) {
82 certs[i++]= new X509CertImpl((org.apache.harmony.security.x509.Certificate)it.next());
85 List sigInfos = signedData.getSignerInfos();
87 if (!sigInfos.isEmpty()) {
88 sigInfo = (SignerInfo)sigInfos.get(0);
94 X500Principal issuer = sigInfo.getIssuer();
96 // Certificate serial number
97 BigInteger snum = sigInfo.getSerialNumber();
99 // Locate the certificate
100 int issuerSertIndex = 0;
101 for (i = 0; i < certs.length; i++) {
102 if (issuer.equals(certs[i].getIssuerDN()) &&
103 snum.equals(certs[i].getSerialNumber())) {
108 if (i == certs.length) { // No issuer certificate found
112 if (certs[issuerSertIndex].hasUnsupportedCriticalExtension()) {
113 throw new SecurityException("Can not recognize a critical extension");
116 // Get Signature instance
117 Signature sig = null;
118 String da = sigInfo.getdigestAlgorithm();
119 String dea = sigInfo.getDigestEncryptionAlgorithm();
121 if (da != null && dea != null) {
122 alg = da + "with" + dea;
124 // BEGIN android-removed
125 // sig = OpenSSLSignature.getInstance(alg);
126 // END android-removed
127 // BEGIN android-added
128 sig = OpenSSLSignature.getInstance(alg);
129 // END android-removed
130 } catch (NoSuchAlgorithmException e) {}
138 // BEGIN android-removed
139 // sig = OpenSSLSignature.getInstance(alg);
140 // END android-removed
141 // BEGIN android-added
142 sig = OpenSSLSignature.getInstance(alg);
143 // END android-removed
144 } catch (NoSuchAlgorithmException e) {
148 sig.initVerify(certs[issuerSertIndex]);
150 // If the authenticatedAttributes field of SignerInfo contains more than zero attributes,
151 // compute the message digest on the ASN.1 DER encoding of the Attributes value.
152 // Otherwise, compute the message digest on the data.
153 List atr = sigInfo.getAuthenticatedAttributes();
155 byte[] sfBytes = new byte[signature.available()];
156 signature.read(sfBytes);
161 sig.update(sigInfo.getEncodedAuthenticatedAttributes());
163 // If the authenticatedAttributes field contains the message-digest attribute,
164 // verify that it equals the computed digest of the signature file
165 byte[] existingDigest = null;
166 for (Iterator it = atr.iterator(); it.hasNext();) {
167 AttributeTypeAndValue a = (AttributeTypeAndValue)it.next();
168 if (Arrays.equals(a.getType().getOid(), MESSAGE_DIGEST_OID) ){
169 //TODO value existingDigest = a.AttributeValue;
172 if (existingDigest != null) {
173 MessageDigest md = MessageDigest.getInstance(sigInfo.getDigestAlgorithm());
174 byte[] computedDigest = md.digest(sfBytes);
175 if (!Arrays.equals(existingDigest, computedDigest)) {
176 throw new SecurityException("Incorrect MD");
181 if (!sig.verify(sigInfo.getEncryptedDigest())) {
182 throw new SecurityException("Incorrect signature");
185 return createChain(certs[issuerSertIndex], certs);
188 private static X509Certificate[] createChain(X509Certificate signer, X509Certificate[] candidates) {
189 LinkedList chain = new LinkedList();
190 chain.add(0, signer);
192 // Signer is self-signed
193 if (signer.getSubjectDN().equals(signer.getIssuerDN())){
194 return (X509Certificate[])chain.toArray(new X509Certificate[1]);
197 Principal issuer = signer.getIssuerDN();
198 X509Certificate issuerCert;
201 issuerCert = findCert(issuer, candidates);
202 if( issuerCert == null) {
205 chain.add(issuerCert);
207 if (issuerCert.getSubjectDN().equals(issuerCert.getIssuerDN())) {
210 issuer = issuerCert.getIssuerDN();
212 return (X509Certificate[])chain.toArray(new X509Certificate[count]);
215 private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates) {
216 for (int i = 0; i < candidates.length; i++) {
217 if (issuer.equals(candidates[i].getSubjectDN())) {
218 return candidates[i];