Path: blob/master/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
41159 views
/*1* Copyright (c) 1997, 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.x509;2627import java.io.IOException;28import java.io.OutputStream;29import java.lang.reflect.Constructor;30import java.lang.reflect.Field;31import java.lang.reflect.InvocationTargetException;32import java.security.cert.CertificateException;33import java.util.*;3435import sun.security.util.HexDumpEncoder;3637import sun.security.util.*;3839/**40* This class defines the Extensions attribute for the Certificate.41*42* @author Amit Kapoor43* @author Hemma Prafullchandra44* @see CertAttrSet45*/46public class CertificateExtensions implements CertAttrSet<Extension> {47/**48* Identifier for this attribute, to be used with the49* get, set, delete methods of Certificate, x509 type.50*/51public static final String IDENT = "x509.info.extensions";52/**53* name54*/55public static final String NAME = "extensions";5657private static final Debug debug = Debug.getInstance("x509");5859private Map<String,Extension> map = Collections.synchronizedMap(60new TreeMap<String,Extension>());61private boolean unsupportedCritExt = false;6263private Map<String,Extension> unparseableExtensions;6465/**66* Default constructor.67*/68public CertificateExtensions() { }6970/**71* Create the object, decoding the values from the passed DER stream.72*73* @param in the DerInputStream to read the Extension from.74* @exception IOException on decoding errors.75*/76public CertificateExtensions(DerInputStream in) throws IOException {77init(in);78}7980// helper routine81private void init(DerInputStream in) throws IOException {8283DerValue[] exts = in.getSequence(5);8485for (int i = 0; i < exts.length; i++) {86Extension ext = new Extension(exts[i]);87parseExtension(ext);88}89}9091private static Class<?>[] PARAMS = {Boolean.class, Object.class};9293// Parse the encoded extension94private void parseExtension(Extension ext) throws IOException {95try {96Class<?> extClass = OIDMap.getClass(ext.getExtensionId());97if (extClass == null) { // Unsupported extension98if (ext.isCritical()) {99unsupportedCritExt = true;100}101if (map.put(ext.getExtensionId().toString(), ext) == null) {102return;103} else {104throw new IOException("Duplicate extensions not allowed");105}106}107Constructor<?> cons = extClass.getConstructor(PARAMS);108109Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()),110ext.getExtensionValue()};111CertAttrSet<?> certExt = (CertAttrSet<?>)112cons.newInstance(passed);113if (map.put(certExt.getName(), (Extension)certExt) != null) {114throw new IOException("Duplicate extensions not allowed");115}116} catch (InvocationTargetException invk) {117Throwable e = invk.getCause();118if (ext.isCritical() == false) {119// ignore errors parsing non-critical extensions120if (unparseableExtensions == null) {121unparseableExtensions = new TreeMap<String,Extension>();122}123unparseableExtensions.put(ext.getExtensionId().toString(),124new UnparseableExtension(ext, e));125if (debug != null) {126debug.println("Debug info only." +127" Error parsing extension: " + ext);128e.printStackTrace();129HexDumpEncoder h = new HexDumpEncoder();130System.err.println(h.encodeBuffer(ext.getExtensionValue()));131}132return;133}134if (e instanceof IOException) {135throw (IOException)e;136} else {137throw new IOException(e);138}139} catch (IOException e) {140throw e;141} catch (Exception e) {142throw new IOException(e);143}144}145146/**147* Encode the extensions in DER form to the stream, setting148* the context specific tag as needed in the X.509 v3 certificate.149*150* @param out the DerOutputStream to marshal the contents to.151* @exception CertificateException on encoding errors.152* @exception IOException on errors.153*/154public void encode(OutputStream out)155throws CertificateException, IOException {156encode(out, false);157}158159/**160* Encode the extensions in DER form to the stream.161*162* @param out the DerOutputStream to marshal the contents to.163* @param isCertReq if true then no context specific tag is added.164* @exception CertificateException on encoding errors.165* @exception IOException on errors.166*/167public void encode(OutputStream out, boolean isCertReq)168throws CertificateException, IOException {169DerOutputStream extOut = new DerOutputStream();170Collection<Extension> allExts = map.values();171Object[] objs = allExts.toArray();172173for (int i = 0; i < objs.length; i++) {174if (objs[i] instanceof CertAttrSet)175((CertAttrSet)objs[i]).encode(extOut);176else if (objs[i] instanceof Extension)177((Extension)objs[i]).encode(extOut);178else179throw new CertificateException("Illegal extension object");180}181182DerOutputStream seq = new DerOutputStream();183seq.write(DerValue.tag_Sequence, extOut);184185DerOutputStream tmp;186if (!isCertReq) { // certificate187tmp = new DerOutputStream();188tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3),189seq);190} else191tmp = seq; // pkcs#10 certificateRequest192193out.write(tmp.toByteArray());194}195196/**197* Set the attribute value.198* @param name the extension name used in the cache.199* @param obj the object to set.200* @exception IOException if the object could not be cached.201*/202public void set(String name, Object obj) throws IOException {203if (obj instanceof Extension) {204map.put(name, (Extension)obj);205} else {206throw new IOException("Unknown extension type.");207}208}209210/**211* Get the attribute value.212* @param name the extension name used in the lookup.213* @exception IOException if named extension is not found.214*/215public Extension get(String name) throws IOException {216Extension obj = map.get(name);217if (obj == null) {218throw new IOException("No extension found with name " + name);219}220return (obj);221}222223// Similar to get(String), but throw no exception, might return null.224// Used in X509CertImpl::getExtension(OID).225Extension getExtension(String name) {226return map.get(name);227}228229/**230* Delete the attribute value.231* @param name the extension name used in the lookup.232* @exception IOException if named extension is not found.233*/234public void delete(String name) throws IOException {235Object obj = map.get(name);236if (obj == null) {237throw new IOException("No extension found with name " + name);238}239map.remove(name);240}241242public String getNameByOid(ObjectIdentifier oid) throws IOException {243for (String name: map.keySet()) {244if (map.get(name).getExtensionId().equals(oid)) {245return name;246}247}248return null;249}250251/**252* Return an enumeration of names of attributes existing within this253* attribute.254*/255public Enumeration<Extension> getElements() {256return Collections.enumeration(map.values());257}258259/**260* Return a collection view of the extensions.261* @return a collection view of the extensions in this Certificate.262*/263public Collection<Extension> getAllExtensions() {264return map.values();265}266267public Map<String,Extension> getUnparseableExtensions() {268if (unparseableExtensions == null) {269return Collections.emptyMap();270} else {271return unparseableExtensions;272}273}274275/**276* Return the name of this attribute.277*/278public String getName() {279return NAME;280}281282/**283* Return true if a critical extension is found that is284* not supported, otherwise return false.285*/286public boolean hasUnsupportedCriticalExtension() {287return unsupportedCritExt;288}289290/**291* Compares this CertificateExtensions for equality with the specified292* object. If the {@code other} object is an293* {@code instanceof} {@code CertificateExtensions}, then294* all the entries are compared with the entries from this.295*296* @param other the object to test for equality with this297* CertificateExtensions.298* @return true iff all the entries match that of the Other,299* false otherwise.300*/301public boolean equals(Object other) {302if (this == other)303return true;304if (!(other instanceof CertificateExtensions))305return false;306Collection<Extension> otherC =307((CertificateExtensions)other).getAllExtensions();308Object[] objs = otherC.toArray();309310int len = objs.length;311if (len != map.size())312return false;313314Extension otherExt, thisExt;315String key = null;316for (int i = 0; i < len; i++) {317if (objs[i] instanceof CertAttrSet)318key = ((CertAttrSet)objs[i]).getName();319otherExt = (Extension)objs[i];320if (key == null)321key = otherExt.getExtensionId().toString();322thisExt = map.get(key);323if (thisExt == null)324return false;325if (! thisExt.equals(otherExt))326return false;327}328return this.getUnparseableExtensions().equals(329((CertificateExtensions)other).getUnparseableExtensions());330}331332/**333* Returns a hashcode value for this CertificateExtensions.334*335* @return the hashcode value.336*/337public int hashCode() {338return map.hashCode() + getUnparseableExtensions().hashCode();339}340341/**342* Returns a string representation of this {@code CertificateExtensions}343* object in the form of a set of entries, enclosed in braces and separated344* by the ASCII characters "<code>, </code>" (comma and space).345* <p>Overrides to {@code toString} method of {@code Object}.346*347* @return a string representation of this CertificateExtensions.348*/349public String toString() {350return map.toString();351}352353}354355class UnparseableExtension extends Extension {356private String name;357private String exceptionDescription;358359public UnparseableExtension(Extension ext, Throwable why) {360super(ext);361362name = "";363try {364Class<?> extClass = OIDMap.getClass(ext.getExtensionId());365if (extClass != null) {366Field field = extClass.getDeclaredField("NAME");367name = (String)(field.get(null)) + " ";368}369} catch (Exception e) {370// If we cannot find the name, just ignore it371}372373this.exceptionDescription = why.toString();374}375376@Override public String toString() {377return super.toString() +378"Unparseable " + name + "extension due to\n" +379exceptionDescription + "\n\n" +380new HexDumpEncoder().encodeBuffer(getExtensionValue());381}382}383384385