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