Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/x509/NameConstraintsExtension.java
38831 views
/*1* Copyright (c) 1997, 2017, 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.security.cert.CertificateException;30import java.security.cert.X509Certificate;31import java.util.*;3233import javax.security.auth.x500.X500Principal;3435import sun.net.util.IPAddressUtil;36import sun.security.util.*;37import sun.security.pkcs.PKCS9Attribute;3839/**40* This class defines the Name Constraints Extension.41* <p>42* The name constraints extension provides permitted and excluded43* subtrees that place restrictions on names that may be included within44* a certificate issued by a given CA. Restrictions may apply to the45* subject distinguished name or subject alternative names. Any name46* matching a restriction in the excluded subtrees field is invalid47* regardless of information appearing in the permitted subtrees.48* <p>49* The ASN.1 syntax for this is:50* <pre>51* NameConstraints ::= SEQUENCE {52* permittedSubtrees [0] GeneralSubtrees OPTIONAL,53* excludedSubtrees [1] GeneralSubtrees OPTIONAL54* }55* GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree56* </pre>57*58* @author Amit Kapoor59* @author Hemma Prafullchandra60* @see Extension61* @see CertAttrSet62*/63public class NameConstraintsExtension extends Extension64implements CertAttrSet<String>, Cloneable {65/**66* Identifier for this attribute, to be used with the67* get, set, delete methods of Certificate, x509 type.68*/69public static final String IDENT = "x509.info.extensions.NameConstraints";70/**71* Attribute names.72*/73public static final String NAME = "NameConstraints";74public static final String PERMITTED_SUBTREES = "permitted_subtrees";75public static final String EXCLUDED_SUBTREES = "excluded_subtrees";7677// Private data members78private static final byte TAG_PERMITTED = 0;79private static final byte TAG_EXCLUDED = 1;8081private GeneralSubtrees permitted = null;82private GeneralSubtrees excluded = null;8384private boolean hasMin;85private boolean hasMax;86private boolean minMaxValid = false;8788// Recalculate hasMin and hasMax flags.89private void calcMinMax() throws IOException {90hasMin = false;91hasMax = false;92if (excluded != null) {93for (int i = 0; i < excluded.size(); i++) {94GeneralSubtree subtree = excluded.get(i);95if (subtree.getMinimum() != 0)96hasMin = true;97if (subtree.getMaximum() != -1)98hasMax = true;99}100}101102if (permitted != null) {103for (int i = 0; i < permitted.size(); i++) {104GeneralSubtree subtree = permitted.get(i);105if (subtree.getMinimum() != 0)106hasMin = true;107if (subtree.getMaximum() != -1)108hasMax = true;109}110}111minMaxValid = true;112}113114// Encode this extension value.115private void encodeThis() throws IOException {116minMaxValid = false;117if (permitted == null && excluded == null) {118this.extensionValue = null;119return;120}121DerOutputStream seq = new DerOutputStream();122123DerOutputStream tagged = new DerOutputStream();124if (permitted != null) {125DerOutputStream tmp = new DerOutputStream();126permitted.encode(tmp);127tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,128true, TAG_PERMITTED), tmp);129}130if (excluded != null) {131DerOutputStream tmp = new DerOutputStream();132excluded.encode(tmp);133tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,134true, TAG_EXCLUDED), tmp);135}136seq.write(DerValue.tag_Sequence, tagged);137this.extensionValue = seq.toByteArray();138}139140/**141* The default constructor for this class. Both parameters142* are optional and can be set to null. The extension criticality143* is set to true.144*145* @param permitted the permitted GeneralSubtrees (null for optional).146* @param excluded the excluded GeneralSubtrees (null for optional).147*/148public NameConstraintsExtension(GeneralSubtrees permitted,149GeneralSubtrees excluded)150throws IOException {151this.permitted = permitted;152this.excluded = excluded;153154this.extensionId = PKIXExtensions.NameConstraints_Id;155this.critical = true;156encodeThis();157}158159/**160* Create the extension from the passed DER encoded value.161*162* @param critical true if the extension is to be treated as critical.163* @param value an array of DER encoded bytes of the actual value.164* @exception ClassCastException if value is not an array of bytes165* @exception IOException on error.166*/167public NameConstraintsExtension(Boolean critical, Object value)168throws IOException {169this.extensionId = PKIXExtensions.NameConstraints_Id;170this.critical = critical.booleanValue();171172this.extensionValue = (byte[]) value;173DerValue val = new DerValue(this.extensionValue);174if (val.tag != DerValue.tag_Sequence) {175throw new IOException("Invalid encoding for" +176" NameConstraintsExtension.");177}178179// NB. this is always encoded with the IMPLICIT tag180// The checks only make sense if we assume implicit tagging,181// with explicit tagging the form is always constructed.182// Note that all the fields in NameConstraints are defined as183// being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting184// in val.data being null.185if (val.data == null)186return;187while (val.data.available() != 0) {188DerValue opt = val.data.getDerValue();189190if (opt.isContextSpecific(TAG_PERMITTED) && opt.isConstructed()) {191if (permitted != null) {192throw new IOException("Duplicate permitted " +193"GeneralSubtrees in NameConstraintsExtension.");194}195opt.resetTag(DerValue.tag_Sequence);196permitted = new GeneralSubtrees(opt);197198} else if (opt.isContextSpecific(TAG_EXCLUDED) &&199opt.isConstructed()) {200if (excluded != null) {201throw new IOException("Duplicate excluded " +202"GeneralSubtrees in NameConstraintsExtension.");203}204opt.resetTag(DerValue.tag_Sequence);205excluded = new GeneralSubtrees(opt);206} else207throw new IOException("Invalid encoding of " +208"NameConstraintsExtension.");209}210minMaxValid = false;211}212213/**214* Return the printable string.215*/216public String toString() {217return (super.toString() + "NameConstraints: [" +218((permitted == null) ? "" :219("\n Permitted:" + permitted.toString())) +220((excluded == null) ? "" :221("\n Excluded:" + excluded.toString()))222+ " ]\n");223}224225/**226* Write the extension to the OutputStream.227*228* @param out the OutputStream to write the extension to.229* @exception IOException on encoding errors.230*/231public void encode(OutputStream out) throws IOException {232DerOutputStream tmp = new DerOutputStream();233if (this.extensionValue == null) {234this.extensionId = PKIXExtensions.NameConstraints_Id;235this.critical = true;236encodeThis();237}238super.encode(tmp);239out.write(tmp.toByteArray());240}241242/**243* Set the attribute value.244*/245public void set(String name, Object obj) throws IOException {246if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) {247if (!(obj instanceof GeneralSubtrees)) {248throw new IOException("Attribute value should be"249+ " of type GeneralSubtrees.");250}251permitted = (GeneralSubtrees)obj;252} else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) {253if (!(obj instanceof GeneralSubtrees)) {254throw new IOException("Attribute value should be "255+ "of type GeneralSubtrees.");256}257excluded = (GeneralSubtrees)obj;258} else {259throw new IOException("Attribute name not recognized by " +260"CertAttrSet:NameConstraintsExtension.");261}262encodeThis();263}264265/**266* Get the attribute value.267*/268public GeneralSubtrees get(String name) throws IOException {269if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) {270return (permitted);271} else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) {272return (excluded);273} else {274throw new IOException("Attribute name not recognized by " +275"CertAttrSet:NameConstraintsExtension.");276}277}278279/**280* Delete the attribute value.281*/282public void delete(String name) throws IOException {283if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) {284permitted = null;285} else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) {286excluded = null;287} else {288throw new IOException("Attribute name not recognized by " +289"CertAttrSet:NameConstraintsExtension.");290}291encodeThis();292}293294/**295* Return an enumeration of names of attributes existing within this296* attribute.297*/298public Enumeration<String> getElements() {299AttributeNameEnumeration elements = new AttributeNameEnumeration();300elements.addElement(PERMITTED_SUBTREES);301elements.addElement(EXCLUDED_SUBTREES);302303return (elements.elements());304}305306/**307* Return the name of this attribute.308*/309public String getName() {310return (NAME);311}312313/**314* Merge additional name constraints with existing ones.315* This function is used in certification path processing316* to accumulate name constraints from successive certificates317* in the path. Note that NameConstraints can never be318* expanded by a merge, just remain constant or become more319* limiting.320* <p>321* IETF RFC2459 specifies the processing of Name Constraints as322* follows:323* <p>324* (j) If permittedSubtrees is present in the certificate, set the325* constrained subtrees state variable to the intersection of its326* previous value and the value indicated in the extension field.327* <p>328* (k) If excludedSubtrees is present in the certificate, set the329* excluded subtrees state variable to the union of its previous330* value and the value indicated in the extension field.331* <p>332* @param newConstraints additional NameConstraints to be applied333* @throws IOException on error334*/335public void merge(NameConstraintsExtension newConstraints)336throws IOException {337338if (newConstraints == null) {339// absence of any explicit constraints implies unconstrained340return;341}342343/*344* If excludedSubtrees is present in the certificate, set the345* excluded subtrees state variable to the union of its previous346* value and the value indicated in the extension field.347*/348349GeneralSubtrees newExcluded = newConstraints.get(EXCLUDED_SUBTREES);350if (excluded == null) {351excluded = (newExcluded != null) ?352(GeneralSubtrees)newExcluded.clone() : null;353} else {354if (newExcluded != null) {355// Merge new excluded with current excluded (union)356excluded.union(newExcluded);357}358}359360/*361* If permittedSubtrees is present in the certificate, set the362* constrained subtrees state variable to the intersection of its363* previous value and the value indicated in the extension field.364*/365366GeneralSubtrees newPermitted = newConstraints.get(PERMITTED_SUBTREES);367if (permitted == null) {368permitted = (newPermitted != null) ?369(GeneralSubtrees)newPermitted.clone() : null;370} else {371if (newPermitted != null) {372// Merge new permitted with current permitted (intersection)373newExcluded = permitted.intersect(newPermitted);374375// Merge new excluded subtrees to current excluded (union)376if (newExcluded != null) {377if (excluded != null) {378excluded.union(newExcluded);379} else {380excluded = (GeneralSubtrees)newExcluded.clone();381}382}383}384}385386// Optional optimization: remove permitted subtrees that are excluded.387// This is not necessary for algorithm correctness, but it makes388// subsequent operations on the NameConstraints faster and require389// less space.390if (permitted != null) {391permitted.reduce(excluded);392}393394// The NameConstraints have been changed, so re-encode them. Methods in395// this class assume that the encodings have already been done.396encodeThis();397398}399400/**401* check whether a certificate conforms to these NameConstraints.402* This involves verifying that the subject name and subjectAltName403* extension (critical or noncritical) is consistent with the permitted404* subtrees state variables. Also verify that the subject name and405* subjectAltName extension (critical or noncritical) is consistent with406* the excluded subtrees state variables.407*408* @param cert X509Certificate to be verified409* @returns true if certificate verifies successfully410* @throws IOException on error411*/412public boolean verify(X509Certificate cert) throws IOException {413414if (cert == null) {415throw new IOException("Certificate is null");416}417418// Calculate hasMin and hasMax booleans (if necessary)419if (!minMaxValid) {420calcMinMax();421}422423if (hasMin) {424throw new IOException("Non-zero minimum BaseDistance in"425+ " name constraints not supported");426}427428if (hasMax) {429throw new IOException("Maximum BaseDistance in"430+ " name constraints not supported");431}432433X500Principal subjectPrincipal = cert.getSubjectX500Principal();434X500Name subject = X500Name.asX500Name(subjectPrincipal);435436// Check subject as an X500Name437if (subject.isEmpty() == false) {438if (verify(subject) == false) {439return false;440}441}442443GeneralNames altNames = null;444// extract altNames445try {446// extract extensions, if any, from certInfo447// following returns null if certificate contains no extensions448X509CertImpl certImpl = X509CertImpl.toImpl(cert);449SubjectAlternativeNameExtension altNameExt =450certImpl.getSubjectAlternativeNameExtension();451if (altNameExt != null) {452// extract altNames from extension; this call does not453// return an IOException on null altnames454altNames = altNameExt.get(455SubjectAlternativeNameExtension.SUBJECT_NAME);456}457} catch (CertificateException ce) {458throw new IOException("Unable to extract extensions from " +459"certificate: " + ce.getMessage());460}461462if (altNames == null) {463altNames = new GeneralNames();464465// RFC 5280 4.2.1.10:466// When constraints are imposed on the rfc822Name name form,467// but the certificate does not include a subject alternative name,468// the rfc822Name constraint MUST be applied to the attribute of469// type emailAddress in the subject distinguished name.470for (AVA ava : subject.allAvas()) {471ObjectIdentifier attrOID = ava.getObjectIdentifier();472if (attrOID.equals(PKCS9Attribute.EMAIL_ADDRESS_OID)) {473String attrValue = ava.getValueString();474if (attrValue != null) {475try {476altNames.add(new GeneralName(477new RFC822Name(attrValue)));478} catch (IOException ioe) {479continue;480}481}482}483}484}485486// If there is no IPAddressName or DNSName in subjectAlternativeNames,487// see if the last CN inside subjectName can be used instead.488DerValue derValue = subject.findMostSpecificAttribute489(X500Name.commonName_oid);490String cn = derValue == null ? null : derValue.getAsString();491492if (cn != null) {493try {494if (IPAddressUtil.isIPv4LiteralAddress(cn) ||495IPAddressUtil.isIPv6LiteralAddress(cn)) {496if (!hasNameType(altNames, GeneralNameInterface.NAME_IP)) {497altNames.add(new GeneralName(new IPAddressName(cn)));498}499} else {500if (!hasNameType(altNames, GeneralNameInterface.NAME_DNS)) {501altNames.add(new GeneralName(new DNSName(cn)));502}503}504} catch (IOException ioe) {505// OK, cn is neither IP nor DNS506}507}508509// verify each subjectAltName510for (int i = 0; i < altNames.size(); i++) {511GeneralNameInterface altGNI = altNames.get(i).getName();512if (!verify(altGNI)) {513return false;514}515}516517// All tests passed.518return true;519}520521private static boolean hasNameType(GeneralNames names, int type) {522for (GeneralName name : names.names()) {523if (name.getType() == type) {524return true;525}526}527return false;528}529530/**531* check whether a name conforms to these NameConstraints.532* This involves verifying that the name is consistent with the533* permitted and excluded subtrees variables.534*535* @param name GeneralNameInterface name to be verified536* @returns true if certificate verifies successfully537* @throws IOException on error538*/539public boolean verify(GeneralNameInterface name) throws IOException {540if (name == null) {541throw new IOException("name is null");542}543544// Verify that the name is consistent with the excluded subtrees545if (excluded != null && excluded.size() > 0) {546547for (int i = 0; i < excluded.size(); i++) {548GeneralSubtree gs = excluded.get(i);549if (gs == null)550continue;551GeneralName gn = gs.getName();552if (gn == null)553continue;554GeneralNameInterface exName = gn.getName();555if (exName == null)556continue;557558// if name matches or narrows any excluded subtree,559// return false560switch (exName.constrains(name)) {561case GeneralNameInterface.NAME_DIFF_TYPE:562case GeneralNameInterface.NAME_WIDENS: // name widens excluded563case GeneralNameInterface.NAME_SAME_TYPE:564break;565case GeneralNameInterface.NAME_MATCH:566case GeneralNameInterface.NAME_NARROWS: // subject name excluded567return false;568}569}570}571572// Verify that the name is consistent with the permitted subtrees573if (permitted != null && permitted.size() > 0) {574575boolean sameType = false;576577for (int i = 0; i < permitted.size(); i++) {578GeneralSubtree gs = permitted.get(i);579if (gs == null)580continue;581GeneralName gn = gs.getName();582if (gn == null)583continue;584GeneralNameInterface perName = gn.getName();585if (perName == null)586continue;587588// if Name matches any type in permitted,589// and Name does not match or narrow some permitted subtree,590// return false591switch (perName.constrains(name)) {592case GeneralNameInterface.NAME_DIFF_TYPE:593continue; // continue checking other permitted names594case GeneralNameInterface.NAME_WIDENS: // name widens permitted595case GeneralNameInterface.NAME_SAME_TYPE:596sameType = true;597continue; // continue to look for a match or narrow598case GeneralNameInterface.NAME_MATCH:599case GeneralNameInterface.NAME_NARROWS:600// name narrows permitted601return true; // name is definitely OK, so break out of loop602}603}604if (sameType) {605return false;606}607}608return true;609}610611/**612* Clone all objects that may be modified during certificate validation.613*/614public Object clone() {615try {616NameConstraintsExtension newNCE =617(NameConstraintsExtension) super.clone();618619if (permitted != null) {620newNCE.permitted = (GeneralSubtrees) permitted.clone();621}622if (excluded != null) {623newNCE.excluded = (GeneralSubtrees) excluded.clone();624}625return newNCE;626} catch (CloneNotSupportedException cnsee) {627throw new RuntimeException("CloneNotSupportedException while " +628"cloning NameConstraintsException. This should never happen.");629}630}631}632633634