src/java.base/share/classes/sun/security/x509/NameConstraintsExtension.java
changeset 47216 71c04702a3d5
parent 31426 9cd672654f97
child 47413 17b77ca4d419
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.x509;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.io.OutputStream;
       
    30 import java.security.cert.CertificateException;
       
    31 import java.security.cert.X509Certificate;
       
    32 import java.util.*;
       
    33 
       
    34 import javax.security.auth.x500.X500Principal;
       
    35 
       
    36 import sun.security.util.*;
       
    37 import sun.security.pkcs.PKCS9Attribute;
       
    38 
       
    39 /**
       
    40  * This class defines the Name Constraints Extension.
       
    41  * <p>
       
    42  * The name constraints extension provides permitted and excluded
       
    43  * subtrees that place restrictions on names that may be included within
       
    44  * a certificate issued by a given CA.  Restrictions may apply to the
       
    45  * subject distinguished name or subject alternative names.  Any name
       
    46  * matching a restriction in the excluded subtrees field is invalid
       
    47  * 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 OPTIONAL
       
    54  * }
       
    55  * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
       
    56  * </pre>
       
    57  *
       
    58  * @author Amit Kapoor
       
    59  * @author Hemma Prafullchandra
       
    60  * @see Extension
       
    61  * @see CertAttrSet
       
    62  */
       
    63 public class NameConstraintsExtension extends Extension
       
    64 implements CertAttrSet<String>, Cloneable {
       
    65     /**
       
    66      * Identifier for this attribute, to be used with the
       
    67      * get, set, delete methods of Certificate, x509 type.
       
    68      */
       
    69     public static final String IDENT = "x509.info.extensions.NameConstraints";
       
    70     /**
       
    71      * Attribute names.
       
    72      */
       
    73     public static final String NAME = "NameConstraints";
       
    74     public static final String PERMITTED_SUBTREES = "permitted_subtrees";
       
    75     public static final String EXCLUDED_SUBTREES = "excluded_subtrees";
       
    76 
       
    77     // Private data members
       
    78     private static final byte TAG_PERMITTED = 0;
       
    79     private static final byte TAG_EXCLUDED = 1;
       
    80 
       
    81     private GeneralSubtrees     permitted = null;
       
    82     private GeneralSubtrees     excluded = null;
       
    83 
       
    84     private boolean hasMin;
       
    85     private boolean hasMax;
       
    86     private boolean minMaxValid = false;
       
    87 
       
    88     // Recalculate hasMin and hasMax flags.
       
    89     private void calcMinMax() throws IOException {
       
    90         hasMin = false;
       
    91         hasMax = false;
       
    92         if (excluded != null) {
       
    93             for (int i = 0; i < excluded.size(); i++) {
       
    94                 GeneralSubtree subtree = excluded.get(i);
       
    95                 if (subtree.getMinimum() != 0)
       
    96                     hasMin = true;
       
    97                 if (subtree.getMaximum() != -1)
       
    98                     hasMax = true;
       
    99             }
       
   100         }
       
   101 
       
   102         if (permitted != null) {
       
   103             for (int i = 0; i < permitted.size(); i++) {
       
   104                 GeneralSubtree subtree = permitted.get(i);
       
   105                 if (subtree.getMinimum() != 0)
       
   106                     hasMin = true;
       
   107                 if (subtree.getMaximum() != -1)
       
   108                     hasMax = true;
       
   109             }
       
   110         }
       
   111         minMaxValid = true;
       
   112     }
       
   113 
       
   114     // Encode this extension value.
       
   115     private void encodeThis() throws IOException {
       
   116         minMaxValid = false;
       
   117         if (permitted == null && excluded == null) {
       
   118             this.extensionValue = null;
       
   119             return;
       
   120         }
       
   121         DerOutputStream seq = new DerOutputStream();
       
   122 
       
   123         DerOutputStream tagged = new DerOutputStream();
       
   124         if (permitted != null) {
       
   125             DerOutputStream tmp = new DerOutputStream();
       
   126             permitted.encode(tmp);
       
   127             tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
       
   128                                  true, TAG_PERMITTED), tmp);
       
   129         }
       
   130         if (excluded != null) {
       
   131             DerOutputStream tmp = new DerOutputStream();
       
   132             excluded.encode(tmp);
       
   133             tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
       
   134                                  true, TAG_EXCLUDED), tmp);
       
   135         }
       
   136         seq.write(DerValue.tag_Sequence, tagged);
       
   137         this.extensionValue = seq.toByteArray();
       
   138     }
       
   139 
       
   140     /**
       
   141      * The default constructor for this class. Both parameters
       
   142      * are optional and can be set to null.  The extension criticality
       
   143      * is set to true.
       
   144      *
       
   145      * @param permitted the permitted GeneralSubtrees (null for optional).
       
   146      * @param excluded the excluded GeneralSubtrees (null for optional).
       
   147      */
       
   148     public NameConstraintsExtension(GeneralSubtrees permitted,
       
   149                                     GeneralSubtrees excluded)
       
   150     throws IOException {
       
   151         this.permitted = permitted;
       
   152         this.excluded = excluded;
       
   153 
       
   154         this.extensionId = PKIXExtensions.NameConstraints_Id;
       
   155         this.critical = true;
       
   156         encodeThis();
       
   157     }
       
   158 
       
   159     /**
       
   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 bytes
       
   165      * @exception IOException on error.
       
   166      */
       
   167     public NameConstraintsExtension(Boolean critical, Object value)
       
   168     throws IOException {
       
   169         this.extensionId = PKIXExtensions.NameConstraints_Id;
       
   170         this.critical = critical.booleanValue();
       
   171 
       
   172         this.extensionValue = (byte[]) value;
       
   173         DerValue val = new DerValue(this.extensionValue);
       
   174         if (val.tag != DerValue.tag_Sequence) {
       
   175             throw new IOException("Invalid encoding for" +
       
   176                                   " NameConstraintsExtension.");
       
   177         }
       
   178 
       
   179         // NB. this is always encoded with the IMPLICIT tag
       
   180         // 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 as
       
   183         // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting
       
   184         // in val.data being null.
       
   185         if (val.data == null)
       
   186             return;
       
   187         while (val.data.available() != 0) {
       
   188             DerValue opt = val.data.getDerValue();
       
   189 
       
   190             if (opt.isContextSpecific(TAG_PERMITTED) && opt.isConstructed()) {
       
   191                 if (permitted != null) {
       
   192                     throw new IOException("Duplicate permitted " +
       
   193                          "GeneralSubtrees in NameConstraintsExtension.");
       
   194                 }
       
   195                 opt.resetTag(DerValue.tag_Sequence);
       
   196                 permitted = new GeneralSubtrees(opt);
       
   197 
       
   198             } else if (opt.isContextSpecific(TAG_EXCLUDED) &&
       
   199                        opt.isConstructed()) {
       
   200                 if (excluded != null) {
       
   201                     throw new IOException("Duplicate excluded " +
       
   202                              "GeneralSubtrees in NameConstraintsExtension.");
       
   203                 }
       
   204                 opt.resetTag(DerValue.tag_Sequence);
       
   205                 excluded = new GeneralSubtrees(opt);
       
   206             } else
       
   207                 throw new IOException("Invalid encoding of " +
       
   208                                       "NameConstraintsExtension.");
       
   209         }
       
   210         minMaxValid = false;
       
   211     }
       
   212 
       
   213     /**
       
   214      * Return the printable string.
       
   215      */
       
   216     public String toString() {
       
   217         StringBuilder sb = new StringBuilder();
       
   218         sb.append(super.toString())
       
   219             .append("NameConstraints: [");
       
   220         if (permitted != null) {
       
   221             sb.append("\n    Permitted:")
       
   222                 .append(permitted);
       
   223         }
       
   224         if (excluded != null) {
       
   225             sb.append("\n    Excluded:")
       
   226                 .append(excluded);
       
   227         }
       
   228         sb.append("   ]\n");
       
   229         return sb.toString();
       
   230     }
       
   231 
       
   232     /**
       
   233      * Write the extension to the OutputStream.
       
   234      *
       
   235      * @param out the OutputStream to write the extension to.
       
   236      * @exception IOException on encoding errors.
       
   237      */
       
   238     public void encode(OutputStream out) throws IOException {
       
   239         DerOutputStream tmp = new DerOutputStream();
       
   240         if (this.extensionValue == null) {
       
   241             this.extensionId = PKIXExtensions.NameConstraints_Id;
       
   242             this.critical = true;
       
   243             encodeThis();
       
   244         }
       
   245         super.encode(tmp);
       
   246         out.write(tmp.toByteArray());
       
   247     }
       
   248 
       
   249     /**
       
   250      * Set the attribute value.
       
   251      */
       
   252     public void set(String name, Object obj) throws IOException {
       
   253         if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) {
       
   254             if (!(obj instanceof GeneralSubtrees)) {
       
   255                 throw new IOException("Attribute value should be"
       
   256                                     + " of type GeneralSubtrees.");
       
   257             }
       
   258             permitted = (GeneralSubtrees)obj;
       
   259         } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) {
       
   260             if (!(obj instanceof GeneralSubtrees)) {
       
   261                 throw new IOException("Attribute value should be "
       
   262                                     + "of type GeneralSubtrees.");
       
   263             }
       
   264             excluded = (GeneralSubtrees)obj;
       
   265         } else {
       
   266           throw new IOException("Attribute name not recognized by " +
       
   267                         "CertAttrSet:NameConstraintsExtension.");
       
   268         }
       
   269         encodeThis();
       
   270     }
       
   271 
       
   272     /**
       
   273      * Get the attribute value.
       
   274      */
       
   275     public GeneralSubtrees get(String name) throws IOException {
       
   276         if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) {
       
   277             return (permitted);
       
   278         } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) {
       
   279             return (excluded);
       
   280         } else {
       
   281           throw new IOException("Attribute name not recognized by " +
       
   282                         "CertAttrSet:NameConstraintsExtension.");
       
   283         }
       
   284     }
       
   285 
       
   286     /**
       
   287      * Delete the attribute value.
       
   288      */
       
   289     public void delete(String name) throws IOException {
       
   290         if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) {
       
   291             permitted = null;
       
   292         } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) {
       
   293             excluded = null;
       
   294         } else {
       
   295           throw new IOException("Attribute name not recognized by " +
       
   296                         "CertAttrSet:NameConstraintsExtension.");
       
   297         }
       
   298         encodeThis();
       
   299     }
       
   300 
       
   301     /**
       
   302      * Return an enumeration of names of attributes existing within this
       
   303      * attribute.
       
   304      */
       
   305     public Enumeration<String> getElements() {
       
   306         AttributeNameEnumeration elements = new AttributeNameEnumeration();
       
   307         elements.addElement(PERMITTED_SUBTREES);
       
   308         elements.addElement(EXCLUDED_SUBTREES);
       
   309 
       
   310         return (elements.elements());
       
   311     }
       
   312 
       
   313     /**
       
   314      * Return the name of this attribute.
       
   315      */
       
   316     public String getName() {
       
   317         return (NAME);
       
   318     }
       
   319 
       
   320     /**
       
   321      * Merge additional name constraints with existing ones.
       
   322      * This function is used in certification path processing
       
   323      * to accumulate name constraints from successive certificates
       
   324      * in the path.  Note that NameConstraints can never be
       
   325      * expanded by a merge, just remain constant or become more
       
   326      * limiting.
       
   327      * <p>
       
   328      * IETF RFC2459 specifies the processing of Name Constraints as
       
   329      * follows:
       
   330      * <p>
       
   331      * (j)  If permittedSubtrees is present in the certificate, set the
       
   332      * constrained subtrees state variable to the intersection of its
       
   333      * previous value and the value indicated in the extension field.
       
   334      * <p>
       
   335      * (k)  If excludedSubtrees is present in the certificate, set the
       
   336      * excluded subtrees state variable to the union of its previous
       
   337      * value and the value indicated in the extension field.
       
   338      *
       
   339      * @param newConstraints additional NameConstraints to be applied
       
   340      * @throws IOException on error
       
   341      */
       
   342     public void merge(NameConstraintsExtension newConstraints)
       
   343             throws IOException {
       
   344 
       
   345         if (newConstraints == null) {
       
   346             // absence of any explicit constraints implies unconstrained
       
   347             return;
       
   348         }
       
   349 
       
   350         /*
       
   351          * If excludedSubtrees is present in the certificate, set the
       
   352          * excluded subtrees state variable to the union of its previous
       
   353          * value and the value indicated in the extension field.
       
   354          */
       
   355 
       
   356         GeneralSubtrees newExcluded = newConstraints.get(EXCLUDED_SUBTREES);
       
   357         if (excluded == null) {
       
   358             excluded = (newExcluded != null) ?
       
   359                         (GeneralSubtrees)newExcluded.clone() : null;
       
   360         } else {
       
   361             if (newExcluded != null) {
       
   362                 // Merge new excluded with current excluded (union)
       
   363                 excluded.union(newExcluded);
       
   364             }
       
   365         }
       
   366 
       
   367         /*
       
   368          * If permittedSubtrees is present in the certificate, set the
       
   369          * constrained subtrees state variable to the intersection of its
       
   370          * previous value and the value indicated in the extension field.
       
   371          */
       
   372 
       
   373         GeneralSubtrees newPermitted = newConstraints.get(PERMITTED_SUBTREES);
       
   374         if (permitted == null) {
       
   375             permitted = (newPermitted != null) ?
       
   376                         (GeneralSubtrees)newPermitted.clone() : null;
       
   377         } else {
       
   378             if (newPermitted != null) {
       
   379                 // Merge new permitted with current permitted (intersection)
       
   380                 newExcluded = permitted.intersect(newPermitted);
       
   381 
       
   382                 // Merge new excluded subtrees to current excluded (union)
       
   383                 if (newExcluded != null) {
       
   384                     if (excluded != null) {
       
   385                         excluded.union(newExcluded);
       
   386                     } else {
       
   387                         excluded = (GeneralSubtrees)newExcluded.clone();
       
   388                     }
       
   389                 }
       
   390             }
       
   391         }
       
   392 
       
   393         // Optional optimization: remove permitted subtrees that are excluded.
       
   394         // This is not necessary for algorithm correctness, but it makes
       
   395         // subsequent operations on the NameConstraints faster and require
       
   396         // less space.
       
   397         if (permitted != null) {
       
   398             permitted.reduce(excluded);
       
   399         }
       
   400 
       
   401         // The NameConstraints have been changed, so re-encode them.  Methods in
       
   402         // this class assume that the encodings have already been done.
       
   403         encodeThis();
       
   404 
       
   405     }
       
   406 
       
   407     /**
       
   408      * check whether a certificate conforms to these NameConstraints.
       
   409      * This involves verifying that the subject name and subjectAltName
       
   410      * extension (critical or noncritical) is consistent with the permitted
       
   411      * subtrees state variables.  Also verify that the subject name and
       
   412      * subjectAltName extension (critical or noncritical) is consistent with
       
   413      * the excluded subtrees state variables.
       
   414      *
       
   415      * @param cert X509Certificate to be verified
       
   416      * @return true if certificate verifies successfully
       
   417      * @throws IOException on error
       
   418      */
       
   419     public boolean verify(X509Certificate cert) throws IOException {
       
   420 
       
   421         if (cert == null) {
       
   422             throw new IOException("Certificate is null");
       
   423         }
       
   424 
       
   425         // Calculate hasMin and hasMax booleans (if necessary)
       
   426         if (!minMaxValid) {
       
   427             calcMinMax();
       
   428         }
       
   429 
       
   430         if (hasMin) {
       
   431             throw new IOException("Non-zero minimum BaseDistance in"
       
   432                                 + " name constraints not supported");
       
   433         }
       
   434 
       
   435         if (hasMax) {
       
   436             throw new IOException("Maximum BaseDistance in"
       
   437                                 + " name constraints not supported");
       
   438         }
       
   439 
       
   440         X500Principal subjectPrincipal = cert.getSubjectX500Principal();
       
   441         X500Name subject = X500Name.asX500Name(subjectPrincipal);
       
   442 
       
   443         if (subject.isEmpty() == false) {
       
   444             if (verify(subject) == false) {
       
   445                 return false;
       
   446             }
       
   447         }
       
   448 
       
   449         GeneralNames altNames = null;
       
   450         // extract altNames
       
   451         try {
       
   452             // extract extensions, if any, from certInfo
       
   453             // following returns null if certificate contains no extensions
       
   454             X509CertImpl certImpl = X509CertImpl.toImpl(cert);
       
   455             SubjectAlternativeNameExtension altNameExt =
       
   456                 certImpl.getSubjectAlternativeNameExtension();
       
   457             if (altNameExt != null) {
       
   458                 // extract altNames from extension; this call does not
       
   459                 // return an IOException on null altnames
       
   460                 altNames = altNameExt.get(
       
   461                         SubjectAlternativeNameExtension.SUBJECT_NAME);
       
   462             }
       
   463         } catch (CertificateException ce) {
       
   464             throw new IOException("Unable to extract extensions from " +
       
   465                         "certificate: " + ce.getMessage());
       
   466         }
       
   467 
       
   468         // If there are no subjectAlternativeNames, perform the special-case
       
   469         // check where if the subjectName contains any EMAILADDRESS
       
   470         // attributes, they must be checked against RFC822 constraints.
       
   471         // If that passes, we're fine.
       
   472         if (altNames == null) {
       
   473             return verifyRFC822SpecialCase(subject);
       
   474         }
       
   475 
       
   476         // verify each subjectAltName
       
   477         for (int i = 0; i < altNames.size(); i++) {
       
   478             GeneralNameInterface altGNI = altNames.get(i).getName();
       
   479             if (!verify(altGNI)) {
       
   480                 return false;
       
   481             }
       
   482         }
       
   483 
       
   484         // All tests passed.
       
   485         return true;
       
   486     }
       
   487 
       
   488     /**
       
   489      * check whether a name conforms to these NameConstraints.
       
   490      * This involves verifying that the name is consistent with the
       
   491      * permitted and excluded subtrees variables.
       
   492      *
       
   493      * @param name GeneralNameInterface name to be verified
       
   494      * @return true if certificate verifies successfully
       
   495      * @throws IOException on error
       
   496      */
       
   497     public boolean verify(GeneralNameInterface name) throws IOException {
       
   498         if (name == null) {
       
   499             throw new IOException("name is null");
       
   500         }
       
   501 
       
   502         // Verify that the name is consistent with the excluded subtrees
       
   503         if (excluded != null && excluded.size() > 0) {
       
   504 
       
   505             for (int i = 0; i < excluded.size(); i++) {
       
   506                 GeneralSubtree gs = excluded.get(i);
       
   507                 if (gs == null)
       
   508                     continue;
       
   509                 GeneralName gn = gs.getName();
       
   510                 if (gn == null)
       
   511                     continue;
       
   512                 GeneralNameInterface exName = gn.getName();
       
   513                 if (exName == null)
       
   514                     continue;
       
   515 
       
   516                 // if name matches or narrows any excluded subtree,
       
   517                 // return false
       
   518                 switch (exName.constrains(name)) {
       
   519                 case GeneralNameInterface.NAME_DIFF_TYPE:
       
   520                 case GeneralNameInterface.NAME_WIDENS: // name widens excluded
       
   521                 case GeneralNameInterface.NAME_SAME_TYPE:
       
   522                     break;
       
   523                 case GeneralNameInterface.NAME_MATCH:
       
   524                 case GeneralNameInterface.NAME_NARROWS: // subject name excluded
       
   525                     return false;
       
   526                 }
       
   527             }
       
   528         }
       
   529 
       
   530         // Verify that the name is consistent with the permitted subtrees
       
   531         if (permitted != null && permitted.size() > 0) {
       
   532 
       
   533             boolean sameType = false;
       
   534 
       
   535             for (int i = 0; i < permitted.size(); i++) {
       
   536                 GeneralSubtree gs = permitted.get(i);
       
   537                 if (gs == null)
       
   538                     continue;
       
   539                 GeneralName gn = gs.getName();
       
   540                 if (gn == null)
       
   541                     continue;
       
   542                 GeneralNameInterface perName = gn.getName();
       
   543                 if (perName == null)
       
   544                     continue;
       
   545 
       
   546                 // if Name matches any type in permitted,
       
   547                 // and Name does not match or narrow some permitted subtree,
       
   548                 // return false
       
   549                 switch (perName.constrains(name)) {
       
   550                 case GeneralNameInterface.NAME_DIFF_TYPE:
       
   551                     continue; // continue checking other permitted names
       
   552                 case GeneralNameInterface.NAME_WIDENS: // name widens permitted
       
   553                 case GeneralNameInterface.NAME_SAME_TYPE:
       
   554                     sameType = true;
       
   555                     continue; // continue to look for a match or narrow
       
   556                 case GeneralNameInterface.NAME_MATCH:
       
   557                 case GeneralNameInterface.NAME_NARROWS:
       
   558                     // name narrows permitted
       
   559                     return true; // name is definitely OK, so break out of loop
       
   560                 }
       
   561             }
       
   562             if (sameType) {
       
   563                 return false;
       
   564             }
       
   565         }
       
   566         return true;
       
   567     }
       
   568 
       
   569     /**
       
   570      * Perform the RFC 822 special case check. We have a certificate
       
   571      * that does not contain any subject alternative names. Check that
       
   572      * any EMAILADDRESS attributes in its subject name conform to these
       
   573      * NameConstraints.
       
   574      *
       
   575      * @param subject the certificate's subject name
       
   576      * @return true if certificate verifies successfully
       
   577      * @throws IOException on error
       
   578      */
       
   579     public boolean verifyRFC822SpecialCase(X500Name subject) throws IOException {
       
   580         for (AVA ava : subject.allAvas()) {
       
   581             ObjectIdentifier attrOID = ava.getObjectIdentifier();
       
   582             if (attrOID.equals(PKCS9Attribute.EMAIL_ADDRESS_OID)) {
       
   583                 String attrValue = ava.getValueString();
       
   584                 if (attrValue != null) {
       
   585                     RFC822Name emailName;
       
   586                     try {
       
   587                         emailName = new RFC822Name(attrValue);
       
   588                     } catch (IOException ioe) {
       
   589                         continue;
       
   590                     }
       
   591                     if (!verify(emailName)) {
       
   592                         return(false);
       
   593                     }
       
   594                 }
       
   595              }
       
   596         }
       
   597         return true;
       
   598     }
       
   599 
       
   600     /**
       
   601      * Clone all objects that may be modified during certificate validation.
       
   602      */
       
   603     public Object clone() {
       
   604         try {
       
   605             NameConstraintsExtension newNCE =
       
   606                 (NameConstraintsExtension) super.clone();
       
   607 
       
   608             if (permitted != null) {
       
   609                 newNCE.permitted = (GeneralSubtrees) permitted.clone();
       
   610             }
       
   611             if (excluded != null) {
       
   612                 newNCE.excluded = (GeneralSubtrees) excluded.clone();
       
   613             }
       
   614             return newNCE;
       
   615         } catch (CloneNotSupportedException cnsee) {
       
   616             throw new RuntimeException("CloneNotSupportedException while " +
       
   617                 "cloning NameConstraintsException. This should never happen.");
       
   618         }
       
   619     }
       
   620 }