Home | History | Annotate | Download | only in utils
      1 /*
      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
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     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.
     16  */
     17 
     18 /**
     19 * @author Boris Kuznetsov
     20 * @version $Revision$
     21 */
     22 package org.apache.harmony.security.utils;
     23 
     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.LinkedList;
     37 import java.util.List;
     38 import javax.security.auth.x500.X500Principal;
     39 import org.apache.harmony.security.asn1.BerInputStream;
     40 import org.apache.harmony.security.pkcs7.ContentInfo;
     41 import org.apache.harmony.security.pkcs7.SignedData;
     42 import org.apache.harmony.security.pkcs7.SignerInfo;
     43 import org.apache.harmony.security.provider.cert.X509CertImpl;
     44 import org.apache.harmony.security.x501.AttributeTypeAndValue;
     45 import org.apache.harmony.xnet.provider.jsse.OpenSSLProvider;
     46 
     47 public class JarUtils {
     48 
     49     // as defined in PKCS #9: Selected Attribute Types:
     50     // http://www.ietf.org/rfc/rfc2985.txt
     51     private static final int[] MESSAGE_DIGEST_OID =
     52         new int[] {1, 2, 840, 113549, 1, 9, 4};
     53 
     54     /**
     55      * This method handle all the work with  PKCS7, ASN1 encoding, signature verifying,
     56      * and certification path building.
     57      * See also PKCS #7: Cryptographic Message Syntax Standard:
     58      * http://www.ietf.org/rfc/rfc2315.txt
     59      * @param signature - the input stream of signature file to be verified
     60      * @param signatureBlock - the input stream of corresponding signature block file
     61      * @return array of certificates used to verify the signature file
     62      * @throws IOException - if some errors occurs during reading from the stream
     63      * @throws GeneralSecurityException - if signature verification process fails
     64      */
     65     public static Certificate[] verifySignature(InputStream signature, InputStream
     66             signatureBlock) throws IOException, GeneralSecurityException {
     67 
     68         BerInputStream bis = new BerInputStream(signatureBlock);
     69         ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);
     70         SignedData signedData = info.getSignedData();
     71         if (signedData == null) {
     72             throw new IOException("No SignedData found");
     73         }
     74         Collection<org.apache.harmony.security.x509.Certificate> encCerts
     75                 = signedData.getCertificates();
     76         if (encCerts.isEmpty()) {
     77             return null;
     78         }
     79         X509Certificate[] certs = new X509Certificate[encCerts.size()];
     80         int i = 0;
     81         for (org.apache.harmony.security.x509.Certificate encCert : encCerts) {
     82             certs[i++] = new X509CertImpl(encCert);
     83         }
     84 
     85         List<SignerInfo> sigInfos = signedData.getSignerInfos();
     86         SignerInfo sigInfo;
     87         if (!sigInfos.isEmpty()) {
     88             sigInfo = sigInfos.get(0);
     89         } else {
     90             return null;
     91         }
     92 
     93         // Issuer
     94         X500Principal issuer = sigInfo.getIssuer();
     95 
     96         // Certificate serial number
     97         BigInteger snum = sigInfo.getSerialNumber();
     98 
     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())) {
    104                 issuerSertIndex = i;
    105                 break;
    106             }
    107         }
    108         if (i == certs.length) { // No issuer certificate found
    109             return null;
    110         }
    111 
    112         if (certs[issuerSertIndex].hasUnsupportedCriticalExtension()) {
    113             throw new SecurityException("Can not recognize a critical extension");
    114         }
    115 
    116         // Get Signature instance
    117         Signature sig = null;
    118         String da = sigInfo.getDigestAlgorithm();
    119         String dea = sigInfo.getDigestEncryptionAlgorithm();
    120         String alg = null;
    121         if (da != null && dea != null) {
    122             alg = da + "with" +  dea;
    123             try {
    124                 sig = Signature.getInstance(alg, OpenSSLProvider.PROVIDER_NAME);
    125             } catch (NoSuchAlgorithmException e) {}
    126         }
    127         if (sig == null) {
    128             alg = da;
    129             if (alg == null) {
    130                 return null;
    131             }
    132             try {
    133                 sig = Signature.getInstance(alg, OpenSSLProvider.PROVIDER_NAME);
    134             } catch (NoSuchAlgorithmException e) {
    135                 return null;
    136             }
    137         }
    138         sig.initVerify(certs[issuerSertIndex]);
    139 
    140         // If the authenticatedAttributes field of SignerInfo contains more than zero attributes,
    141         // compute the message digest on the ASN.1 DER encoding of the Attributes value.
    142         // Otherwise, compute the message digest on the data.
    143         List<AttributeTypeAndValue> atr = sigInfo.getAuthenticatedAttributes();
    144 
    145         byte[] sfBytes = new byte[signature.available()];
    146         signature.read(sfBytes);
    147 
    148         if (atr == null) {
    149             sig.update(sfBytes);
    150         } else {
    151             sig.update(sigInfo.getEncodedAuthenticatedAttributes());
    152 
    153             // If the authenticatedAttributes field contains the message-digest attribute,
    154             // verify that it equals the computed digest of the signature file
    155             byte[] existingDigest = null;
    156             for (AttributeTypeAndValue a : atr) {
    157                 if (Arrays.equals(a.getType().getOid(), MESSAGE_DIGEST_OID)) {
    158 //TODO value                    existingDigest = a.AttributeValue;
    159                 }
    160             }
    161             if (existingDigest != null) {
    162                 MessageDigest md = MessageDigest.getInstance(sigInfo.getDigestAlgorithm());
    163                 byte[] computedDigest = md.digest(sfBytes);
    164                 if (!Arrays.equals(existingDigest, computedDigest)) {
    165                     throw new SecurityException("Incorrect MD");
    166                 }
    167             }
    168         }
    169 
    170         if (!sig.verify(sigInfo.getEncryptedDigest())) {
    171             throw new SecurityException("Incorrect signature");
    172         }
    173 
    174         return createChain(certs[issuerSertIndex], certs);
    175     }
    176 
    177     private static X509Certificate[] createChain(X509Certificate  signer, X509Certificate[] candidates) {
    178         LinkedList chain = new LinkedList();
    179         chain.add(0, signer);
    180 
    181         // Signer is self-signed
    182         if (signer.getSubjectDN().equals(signer.getIssuerDN())){
    183             return (X509Certificate[])chain.toArray(new X509Certificate[1]);
    184         }
    185 
    186         Principal issuer = signer.getIssuerDN();
    187         X509Certificate issuerCert;
    188         int count = 1;
    189         while (true) {
    190             issuerCert = findCert(issuer, candidates);
    191             if( issuerCert == null) {
    192                 break;
    193             }
    194             chain.add(issuerCert);
    195             count++;
    196             if (issuerCert.getSubjectDN().equals(issuerCert.getIssuerDN())) {
    197                 break;
    198             }
    199             issuer = issuerCert.getIssuerDN();
    200         }
    201         return (X509Certificate[])chain.toArray(new X509Certificate[count]);
    202     }
    203 
    204     private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates) {
    205         for (int i = 0; i < candidates.length; i++) {
    206             if (issuer.equals(candidates[i].getSubjectDN())) {
    207                 return candidates[i];
    208             }
    209         }
    210         return null;
    211     }
    212 
    213 }
    214