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 18package java.util.jar; 19 20import java.io.IOException; 21import java.security.CodeSigner; 22import java.security.cert.CertPath; 23import java.security.cert.Certificate; 24import java.security.cert.CertificateException; 25import java.security.cert.CertificateFactory; 26import java.security.cert.X509Certificate; 27import java.util.ArrayList; 28import java.util.List; 29import java.util.zip.ZipEntry; 30 31import javax.security.auth.x500.X500Principal; 32 33/** 34 * Represents a single file in a JAR archive together with the manifest 35 * attributes and digital signatures associated with it. 36 * 37 * @see JarFile 38 * @see JarInputStream 39 */ 40public class JarEntry extends ZipEntry { 41 private Attributes attributes; 42 43 JarFile parentJar; 44 45 CodeSigner signers[]; 46 47 // Cached factory used to build CertPath-s in <code>getCodeSigners()</code>. 48 private CertificateFactory factory; 49 50 private boolean isFactoryChecked = false; 51 52 /** 53 * Creates a new {@code JarEntry} named name. 54 * 55 * @param name 56 * The name of the new {@code JarEntry}. 57 */ 58 public JarEntry(String name) { 59 super(name); 60 } 61 62 /** 63 * Creates a new {@code JarEntry} using the values obtained from entry. 64 * 65 * @param entry 66 * The ZipEntry to obtain values from. 67 */ 68 public JarEntry(ZipEntry entry) { 69 super(entry); 70 } 71 72 /** 73 * Returns the {@code Attributes} object associated with this entry or 74 * {@code null} if none exists. 75 * 76 * @return the {@code Attributes} for this entry. 77 * @exception IOException 78 * If an error occurs obtaining the {@code Attributes}. 79 * @see Attributes 80 */ 81 public Attributes getAttributes() throws IOException { 82 if (attributes != null || parentJar == null) { 83 return attributes; 84 } 85 Manifest manifest = parentJar.getManifest(); 86 if (manifest == null) { 87 return null; 88 } 89 return attributes = manifest.getAttributes(getName()); 90 } 91 92 /** 93 * Returns an array of {@code Certificate} Objects associated with this 94 * entry or {@code null} if none exists. Make sure that the everything is 95 * read from the input stream before calling this method, or else the method 96 * returns {@code null}. 97 * 98 * @return the certificate for this entry. 99 * @see java.security.cert.Certificate 100 */ 101 public Certificate[] getCertificates() { 102 if (null == parentJar) { 103 return null; 104 } 105 JarVerifier jarVerifier = parentJar.verifier; 106 if (null == jarVerifier) { 107 return null; 108 } 109 return jarVerifier.getCertificates(getName()); 110 } 111 112 void setAttributes(Attributes attrib) { 113 attributes = attrib; 114 } 115 116 /** 117 * Create a new {@code JarEntry} using the values obtained from the 118 * argument. 119 * 120 * @param je 121 * The {@code JarEntry} to obtain values from. 122 */ 123 public JarEntry(JarEntry je) { 124 super(je); 125 parentJar = je.parentJar; 126 attributes = je.attributes; 127 signers = je.signers; 128 } 129 130 /** 131 * Returns the code signers for the digital signatures associated with the 132 * JAR file. If there is no such code signer, it returns {@code null}. Make 133 * sure that the everything is read from the input stream before calling 134 * this method, or else the method returns {@code null}. 135 * 136 * @return the code signers for the JAR entry. 137 * @see CodeSigner 138 */ 139 public CodeSigner[] getCodeSigners() { 140 if (null == signers) { 141 signers = getCodeSigners(getCertificates()); 142 } 143 if (null == signers) { 144 return null; 145 } 146 147 CodeSigner[] tmp = new CodeSigner[signers.length]; 148 System.arraycopy(signers, 0, tmp, 0, tmp.length); 149 return tmp; 150 } 151 152 private CodeSigner[] getCodeSigners(Certificate[] certs) { 153 if (null == certs) { 154 return null; 155 } 156 157 X500Principal prevIssuer = null; 158 ArrayList<Certificate> list = new ArrayList<Certificate>(certs.length); 159 ArrayList<CodeSigner> asigners = new ArrayList<CodeSigner>(); 160 161 for (Certificate element : certs) { 162 if (!(element instanceof X509Certificate)) { 163 // Only X509Certificate-s are taken into account - see API spec. 164 continue; 165 } 166 X509Certificate x509 = (X509Certificate) element; 167 if (null != prevIssuer) { 168 X500Principal subj = x509.getSubjectX500Principal(); 169 if (!prevIssuer.equals(subj)) { 170 // Ok, this ends the previous chain, 171 // so transform this one into CertPath ... 172 addCodeSigner(asigners, list); 173 // ... and start a new one 174 list.clear(); 175 }// else { it's still the same chain } 176 177 } 178 prevIssuer = x509.getIssuerX500Principal(); 179 list.add(x509); 180 } 181 if (!list.isEmpty()) { 182 addCodeSigner(asigners, list); 183 } 184 if (asigners.isEmpty()) { 185 // 'signers' is 'null' already 186 return null; 187 } 188 189 CodeSigner[] tmp = new CodeSigner[asigners.size()]; 190 asigners.toArray(tmp); 191 return tmp; 192 193 } 194 195 private void addCodeSigner(ArrayList<CodeSigner> asigners, 196 List<Certificate> list) { 197 CertPath certPath = null; 198 if (!isFactoryChecked) { 199 try { 200 factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ 201 } catch (CertificateException ex) { 202 // do nothing 203 } finally { 204 isFactoryChecked = true; 205 } 206 } 207 if (null == factory) { 208 return; 209 } 210 try { 211 certPath = factory.generateCertPath(list); 212 } catch (CertificateException ex) { 213 // do nothing 214 } 215 if (null != certPath) { 216 asigners.add(new CodeSigner(certPath, null)); 217 } 218 } 219} 220