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.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;
     47 
     48 public class JarUtils {
     49 
     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};
     54 
     55     /**
     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
     65      */
     66     public static Certificate[] verifySignature(InputStream signature, InputStream
     67             signatureBlock) throws IOException, GeneralSecurityException {
     68 
     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");
     74         }
     75         Collection<org.apache.harmony.security.x509.Certificate> encCerts
     76                 = signedData.getCertificates();
     77         if (encCerts.isEmpty()) {
     78             return null;
     79         }
     80         X509Certificate[] certs = new X509Certificate[encCerts.size()];
     81         int i = 0;
     82         for (org.apache.harmony.security.x509.Certificate encCert : encCerts) {
     83             certs[i++] = new X509CertImpl(encCert);
     84         }
     85 
     86         List<SignerInfo> sigInfos = signedData.getSignerInfos();
     87         SignerInfo sigInfo;
     88         if (!sigInfos.isEmpty()) {
     89             sigInfo = sigInfos.get(0);
     90         } else {
     91             return null;
     92         }
     93 
     94         // Issuer
     95         X500Principal issuer = sigInfo.getIssuer();
     96 
     97         // Certificate serial number
     98         BigInteger snum = sigInfo.getSerialNumber();
     99 
    100         // Locate the certificate
    101         int issuerSertIndex = 0;
    102         for (i = 0; i < certs.length; i++) {
    103             if (issuer.equals(certs[i].getIssuerDN()) &&
    104                     snum.equals(certs[i].getSerialNumber())) {
    105                 issuerSertIndex = i;
    106                 break;
    107             }
    108         }
    109         if (i == certs.length) { // No issuer certificate found
    110             return null;
    111         }
    112 
    113         if (certs[issuerSertIndex].hasUnsupportedCriticalExtension()) {
    114             throw new SecurityException("Can not recognize a critical extension");
    115         }
    116 
    117         // Get Signature instance
    118         Signature sig = null;
    119         String da = sigInfo.getDigestAlgorithm();
    120         String dea = sigInfo.getDigestEncryptionAlgorithm();
    121         String alg = null;
    122         if (da != null && dea != null) {
    123             alg = da + "with" +  dea;
    124             try {
    125                 sig = OpenSSLSignature.getInstance(alg);
    126             } catch (NoSuchAlgorithmException e) {}
    127         }
    128         if (sig == null) {
    129             alg = da;
    130             if (alg == null) {
    131                 return null;
    132             }
    133             try {
    134                 sig = OpenSSLSignature.getInstance(alg);
    135             } catch (NoSuchAlgorithmException e) {
    136                 return null;
    137             }
    138         }
    139         sig.initVerify(certs[issuerSertIndex]);
    140 
    141         // If the authenticatedAttributes field of SignerInfo contains more than zero attributes,
    142         // compute the message digest on the ASN.1 DER encoding of the Attributes value.
    143         // Otherwise, compute the message digest on the data.
    144         List<AttributeTypeAndValue> atr = sigInfo.getAuthenticatedAttributes();
    145 
    146         byte[] sfBytes = new byte[signature.available()];
    147         signature.read(sfBytes);
    148 
    149         if (atr == null) {
    150             sig.update(sfBytes);
    151         } else {
    152             sig.update(sigInfo.getEncodedAuthenticatedAttributes());
    153 
    154             // If the authenticatedAttributes field contains the message-digest attribute,
    155             // verify that it equals the computed digest of the signature file
    156             byte[] existingDigest = null;
    157             for (AttributeTypeAndValue a : atr) {
    158                 if (Arrays.equals(a.getType().getOid(), MESSAGE_DIGEST_OID)) {
    159 //TODO value                    existingDigest = a.AttributeValue;
    160                 }
    161             }
    162             if (existingDigest != null) {
    163                 MessageDigest md = MessageDigest.getInstance(sigInfo.getDigestAlgorithm());
    164                 byte[] computedDigest = md.digest(sfBytes);
    165                 if (!Arrays.equals(existingDigest, computedDigest)) {
    166                     throw new SecurityException("Incorrect MD");
    167                 }
    168             }
    169         }
    170 
    171         if (!sig.verify(sigInfo.getEncryptedDigest())) {
    172             throw new SecurityException("Incorrect signature");
    173         }
    174 
    175         return createChain(certs[issuerSertIndex], certs);
    176     }
    177 
    178     private static X509Certificate[] createChain(X509Certificate  signer, X509Certificate[] candidates) {
    179         LinkedList chain = new LinkedList();
    180         chain.add(0, signer);
    181 
    182         // Signer is self-signed
    183         if (signer.getSubjectDN().equals(signer.getIssuerDN())){
    184             return (X509Certificate[])chain.toArray(new X509Certificate[1]);
    185         }
    186 
    187         Principal issuer = signer.getIssuerDN();
    188         X509Certificate issuerCert;
    189         int count = 1;
    190         while (true) {
    191             issuerCert = findCert(issuer, candidates);
    192             if( issuerCert == null) {
    193                 break;
    194             }
    195             chain.add(issuerCert);
    196             count++;
    197             if (issuerCert.getSubjectDN().equals(issuerCert.getIssuerDN())) {
    198                 break;
    199             }
    200             issuer = issuerCert.getIssuerDN();
    201         }
    202         return (X509Certificate[])chain.toArray(new X509Certificate[count]);
    203     }
    204 
    205     private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates) {
    206         for (int i = 0; i < candidates.length; i++) {
    207             if (issuer.equals(candidates[i].getSubjectDN())) {
    208                 return candidates[i];
    209             }
    210         }
    211         return null;
    212     }
    213 
    214 }
    215