Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java
41161 views
/*1* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider.certpath;2627import java.io.ByteArrayInputStream;28import java.io.ByteArrayOutputStream;29import java.io.IOException;30import java.io.InputStream;31import java.security.cert.CertificateEncodingException;32import java.security.cert.Certificate;33import java.security.cert.CertificateException;34import java.security.cert.CertificateFactory;35import java.security.cert.CertPath;36import java.security.cert.X509Certificate;37import java.util.*;3839import sun.security.pkcs.ContentInfo;40import sun.security.pkcs.PKCS7;41import sun.security.pkcs.SignerInfo;42import sun.security.x509.AlgorithmId;43import sun.security.util.DerValue;44import sun.security.util.DerOutputStream;45import sun.security.util.DerInputStream;4647/**48* A {@link java.security.cert.CertPath CertPath} (certification path)49* consisting exclusively of50* {@link java.security.cert.X509Certificate X509Certificate}s.51* <p>52* By convention, X.509 <code>CertPath</code>s are stored from target53* to trust anchor.54* That is, the issuer of one certificate is the subject of the following55* one. However, unvalidated X.509 <code>CertPath</code>s may not follow56* this convention. PKIX <code>CertPathValidator</code>s will detect any57* departure from this convention and throw a58* <code>CertPathValidatorException</code>.59*60* @author Yassir Elley61* @since 1.462*/63public class X509CertPath extends CertPath {6465@java.io.Serial66private static final long serialVersionUID = 4989800333263052980L;6768/**69* List of certificates in this chain70*/71@SuppressWarnings("serial") // Not statically typed as Serializable72private List<X509Certificate> certs;7374/**75* The names of our encodings. PkiPath is the default.76*/77private static final String COUNT_ENCODING = "count";78private static final String PKCS7_ENCODING = "PKCS7";79private static final String PKIPATH_ENCODING = "PkiPath";8081/**82* List of supported encodings83*/84private static final Collection<String> encodingList;8586static {87List<String> list = new ArrayList<>(2);88list.add(PKIPATH_ENCODING);89list.add(PKCS7_ENCODING);90encodingList = Collections.unmodifiableCollection(list);91}9293/**94* Creates an <code>X509CertPath</code> from a <code>List</code> of95* <code>X509Certificate</code>s.96* <p>97* The certificates are copied out of the supplied <code>List</code>98* object.99*100* @param certs a <code>List</code> of <code>X509Certificate</code>s101* @exception CertificateException if <code>certs</code> contains an element102* that is not an <code>X509Certificate</code>103*/104@SuppressWarnings("unchecked")105public X509CertPath(List<? extends Certificate> certs) throws CertificateException {106super("X.509");107108// Ensure that the List contains only X509Certificates109//110// Note; The certs parameter is not necessarily to be of Certificate111// for some old code. For compatibility, to make sure the exception112// is CertificateException, rather than ClassCastException, please113// don't use114// for (Certificate obj : certs)115for (Object obj : certs) {116if (obj instanceof X509Certificate == false) {117throw new CertificateException118("List is not all X509Certificates: "119+ obj.getClass().getName());120}121}122123// Assumes that the resulting List is thread-safe. This is true124// because we ensure that it cannot be modified after construction125// and the methods in the Sun JDK 1.4 implementation of ArrayList that126// allow read-only access are thread-safe.127this.certs = Collections.unmodifiableList(128new ArrayList<X509Certificate>((List<X509Certificate>)certs));129}130131/**132* Creates an <code>X509CertPath</code>, reading the encoded form133* from an <code>InputStream</code>. The data is assumed to be in134* the default encoding.135*136* @param is the <code>InputStream</code> to read the data from137* @exception CertificateException if an exception occurs while decoding138*/139public X509CertPath(InputStream is) throws CertificateException {140this(is, PKIPATH_ENCODING);141}142143/**144* Creates an <code>X509CertPath</code>, reading the encoded form145* from an InputStream. The data is assumed to be in the specified146* encoding.147*148* @param is the <code>InputStream</code> to read the data from149* @param encoding the encoding used150* @exception CertificateException if an exception occurs while decoding or151* the encoding requested is not supported152*/153public X509CertPath(InputStream is, String encoding)154throws CertificateException {155super("X.509");156157switch (encoding) {158case PKIPATH_ENCODING:159certs = parsePKIPATH(is);160break;161case PKCS7_ENCODING:162certs = parsePKCS7(is);163break;164default:165throw new CertificateException("unsupported encoding");166}167}168169/**170* Parse a PKIPATH format CertPath from an InputStream. Return an171* unmodifiable List of the certificates.172*173* @param is the <code>InputStream</code> to read the data from174* @return an unmodifiable List of the certificates175* @exception CertificateException if an exception occurs176*/177private static List<X509Certificate> parsePKIPATH(InputStream is)178throws CertificateException {179List<X509Certificate> certList = null;180CertificateFactory certFac = null;181182if (is == null) {183throw new CertificateException("input stream is null");184}185186try {187DerInputStream dis = new DerInputStream(readAllBytes(is));188DerValue[] seq = dis.getSequence(3);189if (seq.length == 0) {190return Collections.<X509Certificate>emptyList();191}192193certFac = CertificateFactory.getInstance("X.509");194certList = new ArrayList<X509Certificate>(seq.length);195196// append certs in reverse order (target to trust anchor)197for (int i = seq.length-1; i >= 0; i--) {198certList.add((X509Certificate)certFac.generateCertificate199(new ByteArrayInputStream(seq[i].toByteArray())));200}201202return Collections.unmodifiableList(certList);203204} catch (IOException ioe) {205throw new CertificateException("IOException parsing PkiPath data: "206+ ioe, ioe);207}208}209210/**211* Parse a PKCS#7 format CertPath from an InputStream. Return an212* unmodifiable List of the certificates.213*214* @param is the <code>InputStream</code> to read the data from215* @return an unmodifiable List of the certificates216* @exception CertificateException if an exception occurs217*/218private static List<X509Certificate> parsePKCS7(InputStream is)219throws CertificateException {220List<X509Certificate> certList;221222if (is == null) {223throw new CertificateException("input stream is null");224}225226try {227if (is.markSupported() == false) {228// Copy the entire input stream into an InputStream that does229// support mark230is = new ByteArrayInputStream(readAllBytes(is));231}232PKCS7 pkcs7 = new PKCS7(is);233234X509Certificate[] certArray = pkcs7.getCertificates();235// certs are optional in PKCS #7236if (certArray != null) {237certList = Arrays.asList(certArray);238} else {239// no certs provided240certList = new ArrayList<X509Certificate>(0);241}242} catch (IOException ioe) {243throw new CertificateException("IOException parsing PKCS7 data: " +244ioe);245}246// Assumes that the resulting List is thread-safe. This is true247// because we ensure that it cannot be modified after construction248// and the methods in the Sun JDK 1.4 implementation of ArrayList that249// allow read-only access are thread-safe.250return Collections.unmodifiableList(certList);251}252253/*254* Reads the entire contents of an InputStream into a byte array.255*256* @param is the InputStream to read from257* @return the bytes read from the InputStream258*/259private static byte[] readAllBytes(InputStream is) throws IOException {260byte[] buffer = new byte[8192];261ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);262int n;263while ((n = is.read(buffer)) != -1) {264baos.write(buffer, 0, n);265}266return baos.toByteArray();267}268269/**270* Returns the encoded form of this certification path, using the271* default encoding.272*273* @return the encoded bytes274* @exception CertificateEncodingException if an encoding error occurs275*/276@Override277public byte[] getEncoded() throws CertificateEncodingException {278// @@@ Should cache the encoded form279return encodePKIPATH();280}281282/**283* Encode the CertPath using PKIPATH format.284*285* @return a byte array containing the binary encoding of the PkiPath object286* @exception CertificateEncodingException if an exception occurs287*/288private byte[] encodePKIPATH() throws CertificateEncodingException {289290ListIterator<X509Certificate> li = certs.listIterator(certs.size());291try {292DerOutputStream bytes = new DerOutputStream();293// encode certs in reverse order (trust anchor to target)294// according to PkiPath format295while (li.hasPrevious()) {296X509Certificate cert = li.previous();297// check for duplicate cert298if (certs.lastIndexOf(cert) != certs.indexOf(cert)) {299throw new CertificateEncodingException300("Duplicate Certificate");301}302// get encoded certificates303byte[] encoded = cert.getEncoded();304bytes.write(encoded);305}306307// Wrap the data in a SEQUENCE308DerOutputStream derout = new DerOutputStream();309derout.write(DerValue.tag_SequenceOf, bytes);310return derout.toByteArray();311312} catch (IOException ioe) {313throw new CertificateEncodingException("IOException encoding " +314"PkiPath data: " + ioe, ioe);315}316}317318/**319* Encode the CertPath using PKCS#7 format.320*321* @return a byte array containing the binary encoding of the PKCS#7 object322* @exception CertificateEncodingException if an exception occurs323*/324private byte[] encodePKCS7() throws CertificateEncodingException {325PKCS7 p7 = new PKCS7(new AlgorithmId[0],326new ContentInfo(ContentInfo.DATA_OID, null),327certs.toArray(new X509Certificate[certs.size()]),328new SignerInfo[0]);329DerOutputStream derout = new DerOutputStream();330try {331p7.encodeSignedData(derout);332} catch (IOException ioe) {333throw new CertificateEncodingException(ioe.getMessage());334}335return derout.toByteArray();336}337338/**339* Returns the encoded form of this certification path, using the340* specified encoding.341*342* @param encoding the name of the encoding to use343* @return the encoded bytes344* @exception CertificateEncodingException if an encoding error occurs or345* the encoding requested is not supported346*/347@Override348public byte[] getEncoded(String encoding)349throws CertificateEncodingException {350switch (encoding) {351case PKIPATH_ENCODING:352return encodePKIPATH();353case PKCS7_ENCODING:354return encodePKCS7();355default:356throw new CertificateEncodingException("unsupported encoding");357}358}359360/**361* Returns the encodings supported by this certification path, with the362* default encoding first.363*364* @return an <code>Iterator</code> over the names of the supported365* encodings (as Strings)366*/367public static Iterator<String> getEncodingsStatic() {368return encodingList.iterator();369}370371/**372* Returns an iteration of the encodings supported by this certification373* path, with the default encoding first.374* <p>375* Attempts to modify the returned <code>Iterator</code> via its376* <code>remove</code> method result in an377* <code>UnsupportedOperationException</code>.378*379* @return an <code>Iterator</code> over the names of the supported380* encodings (as Strings)381*/382@Override383public Iterator<String> getEncodings() {384return getEncodingsStatic();385}386387/**388* Returns the list of certificates in this certification path.389* The <code>List</code> returned must be immutable and thread-safe.390*391* @return an immutable <code>List</code> of <code>X509Certificate</code>s392* (may be empty, but not null)393*/394@Override395public List<X509Certificate> getCertificates() {396return certs;397}398}399400401