Path: blob/master/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java
41159 views
/*1* Copyright (c) 1997, 2020, 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.pkcs;2627import java.io.IOException;28import java.io.OutputStream;29import java.security.cert.CertificateException;30import java.util.Date;31import sun.security.x509.CertificateExtensions;32import sun.security.util.*;3334/**35* Class supporting any PKCS9 attributes.36* Supports DER decoding/encoding and access to attribute values.37*38* <a name="classTable"><h3>Type/Class Table</h3></a>39* The following table shows the correspondence between40* PKCS9 attribute types and value component classes.41* For types not listed here, its name is the OID42* in string form, its value is a (single-valued)43* byte array that is the SET's encoding.44*45* <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>46*47* <TR>48* <TH>Object Identifier</TH>49* <TH>Attribute Name</TH>50* <TH>Type</TH>51* <TH>Value Class</TH>52* </TR>53*54* <TR>55* <TD>1.2.840.113549.1.9.1</TD>56* <TD>EmailAddress</TD>57* <TD>Multi-valued</TD>58* <TD><code>String[]</code></TD>59* </TR>60*61* <TR>62* <TD>1.2.840.113549.1.9.2</TD>63* <TD>UnstructuredName</TD>64* <TD>Multi-valued</TD>65* <TD><code>String[]</code></TD>66* </TR>67*68* <TR>69* <TD>1.2.840.113549.1.9.3</TD>70* <TD>ContentType</TD>71* <TD>Single-valued</TD>72* <TD><code>ObjectIdentifier</code></TD>73* </TR>74*75* <TR>76* <TD>1.2.840.113549.1.9.4</TD>77* <TD>MessageDigest</TD>78* <TD>Single-valued</TD>79* <TD><code>byte[]</code></TD>80* </TR>81*82* <TR>83* <TD>1.2.840.113549.1.9.5</TD>84* <TD>SigningTime</TD>85* <TD>Single-valued</TD>86* <TD><code>Date</code></TD>87* </TR>88*89* <TR>90* <TD>1.2.840.113549.1.9.6</TD>91* <TD>Countersignature</TD>92* <TD>Multi-valued</TD>93* <TD><code>SignerInfo[]</code></TD>94* </TR>95*96* <TR>97* <TD>1.2.840.113549.1.9.7</TD>98* <TD>ChallengePassword</TD>99* <TD>Single-valued</TD>100* <TD><code>String</code></TD>101* </TR>102*103* <TR>104* <TD>1.2.840.113549.1.9.8</TD>105* <TD>UnstructuredAddress</TD>106* <TD>Single-valued</TD>107* <TD><code>String</code></TD>108* </TR>109*110* <TR>111* <TD>1.2.840.113549.1.9.9</TD>112* <TD>ExtendedCertificateAttributes</TD>113* <TD>Multi-valued</TD>114* <TD>(not supported)</TD>115* </TR>116*117* <TR>118* <TD>1.2.840.113549.1.9.10</TD>119* <TD>IssuerAndSerialNumber</TD>120* <TD>Single-valued</TD>121* <TD>(not supported)</TD>122* </TR>123*124* <TR>125* <TD>1.2.840.113549.1.9.{11,12}</TD>126* <TD>RSA DSI proprietary</TD>127* <TD>Single-valued</TD>128* <TD>(not supported)</TD>129* </TR>130*131* <TR>132* <TD>1.2.840.113549.1.9.13</TD>133* <TD>S/MIME unused assignment</TD>134* <TD>Single-valued</TD>135* <TD>(not supported)</TD>136* </TR>137*138* <TR>139* <TD>1.2.840.113549.1.9.14</TD>140* <TD>ExtensionRequest</TD>141* <TD>Single-valued</TD>142* <TD>CertificateExtensions</TD>143* </TR>144*145* <TR>146* <TD>1.2.840.113549.1.9.15</TD>147* <TD>SMIMECapability</TD>148* <TD>Single-valued</TD>149* <TD>(not supported)</TD>150* </TR>151*152* <TR>153* <TD>1.2.840.113549.1.9.16.2.12</TD>154* <TD>SigningCertificate</TD>155* <TD>Single-valued</TD>156* <TD>SigningCertificateInfo</TD>157* </TR>158*159* <TR>160* <TD>1.2.840.113549.1.9.16.2.14</TD>161* <TD>SignatureTimestampToken</TD>162* <TD>Single-valued</TD>163* <TD>byte[]</TD>164* </TR>165*166* <TR>167* <TD>1.2.840.113549.1.9.16.2.52</TD>168* <TD>CMSAlgorithmProtection</TD>169* <TD>Single-valued</TD>170* <TD>byte[]</TD>171* </TR>172*173* </TABLE>174*175* @author Douglas Hoover176*/177public class PKCS9Attribute implements DerEncoder {178179/* Are we debugging ? */180private static final Debug debug = Debug.getInstance("jar");181182/**183* Array of attribute OIDs defined in PKCS9, by number.184*/185static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[19];186187private static final Class<?> BYTE_ARRAY_CLASS;188189static {190// set unused PKCS9_OIDS entries to null191// rest are initialized with public constants192PKCS9_OIDS[0] = PKCS9_OIDS[11] = PKCS9_OIDS[12] = PKCS9_OIDS[13] =193PKCS9_OIDS[15] = null;194try {195BYTE_ARRAY_CLASS = Class.forName("[B");196} catch (ClassNotFoundException e) {197throw new ExceptionInInitializerError(e.toString());198}199}200201public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1] =202ObjectIdentifier.of(KnownOIDs.EmailAddress);203public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2] =204ObjectIdentifier.of(KnownOIDs.UnstructuredName);205public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3] =206ObjectIdentifier.of(KnownOIDs.ContentType);207public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4] =208ObjectIdentifier.of(KnownOIDs.MessageDigest);209public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5] =210ObjectIdentifier.of(KnownOIDs.SigningTime);211public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6] =212ObjectIdentifier.of(KnownOIDs.CounterSignature);213public static final ObjectIdentifier CHALLENGE_PASSWORD_OID =214PKCS9_OIDS[7] = ObjectIdentifier.of(KnownOIDs.ChallengePassword);215public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID =216PKCS9_OIDS[8] = ObjectIdentifier.of(KnownOIDs.UnstructuredAddress);217public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID =218PKCS9_OIDS[9] =219ObjectIdentifier.of(KnownOIDs.ExtendedCertificateAttributes);220public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID =221PKCS9_OIDS[10] =222ObjectIdentifier.of(KnownOIDs.IssuerAndSerialNumber);223// [11], [12] are RSA DSI proprietary224// [13] ==> signingDescription, S/MIME, not used anymore225public static final ObjectIdentifier EXTENSION_REQUEST_OID =226PKCS9_OIDS[14] = ObjectIdentifier.of(KnownOIDs.ExtensionRequest);227public static final ObjectIdentifier SIGNING_CERTIFICATE_OID =228PKCS9_OIDS[16] = ObjectIdentifier.of(KnownOIDs.SigningCertificate);229public static final ObjectIdentifier SIGNATURE_TIMESTAMP_TOKEN_OID =230PKCS9_OIDS[17] =231ObjectIdentifier.of(KnownOIDs.SignatureTimestampToken);232public static final ObjectIdentifier CMS_ALGORITHM_PROTECTION_OID =233PKCS9_OIDS[18] =234ObjectIdentifier.of(KnownOIDs.CMSAlgorithmProtection);235236/**237* Acceptable ASN.1 tags for DER encodings of values of PKCS9238* attributes, by index in <code>PKCS9_OIDS</code>.239* Sets of acceptable tags are represented as arrays.240*/241private static final Byte[][] PKCS9_VALUE_TAGS = {242null,243{DerValue.tag_IA5String}, // EMailAddress244{DerValue.tag_IA5String,245DerValue.tag_PrintableString,246DerValue.tag_T61String,247DerValue.tag_BMPString,248DerValue.tag_UniversalString,249DerValue.tag_UTF8String}, // UnstructuredName250{DerValue.tag_ObjectId}, // ContentType251{DerValue.tag_OctetString}, // MessageDigest252{DerValue.tag_UtcTime,253DerValue.tag_GeneralizedTime}, // SigningTime254{DerValue.tag_Sequence}, // Countersignature255{DerValue.tag_PrintableString,256DerValue.tag_T61String,257DerValue.tag_BMPString,258DerValue.tag_UniversalString,259DerValue.tag_UTF8String}, // ChallengePassword260{DerValue.tag_PrintableString,261DerValue.tag_T61String,262DerValue.tag_BMPString,263DerValue.tag_UniversalString,264DerValue.tag_UTF8String}, // UnstructuredAddress265{DerValue.tag_SetOf}, // ExtendedCertificateAttributes266{DerValue.tag_Sequence}, // issuerAndSerialNumber267null,268null,269null,270{DerValue.tag_Sequence}, // extensionRequest271{DerValue.tag_Sequence}, // SMIMECapability272{DerValue.tag_Sequence}, // SigningCertificate273{DerValue.tag_Sequence}, // SignatureTimestampToken274{DerValue.tag_Sequence} // CMSAlgorithmProtection275};276277private static final Class<?>[] VALUE_CLASSES = new Class<?>[19];278279static {280try {281Class<?> str = Class.forName("[Ljava.lang.String;");282283VALUE_CLASSES[0] = null; // not used284VALUE_CLASSES[1] = str; // EMailAddress285VALUE_CLASSES[2] = str; // UnstructuredName286VALUE_CLASSES[3] = // ContentType287Class.forName("sun.security.util.ObjectIdentifier");288VALUE_CLASSES[4] = BYTE_ARRAY_CLASS; // MessageDigest (byte[])289VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime290VALUE_CLASSES[6] = // Countersignature291Class.forName("[Lsun.security.pkcs.SignerInfo;");292VALUE_CLASSES[7] = // ChallengePassword293Class.forName("java.lang.String");294VALUE_CLASSES[8] = str; // UnstructuredAddress295VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes296VALUE_CLASSES[10] = null; // IssuerAndSerialNumber297VALUE_CLASSES[11] = null; // not used298VALUE_CLASSES[12] = null; // not used299VALUE_CLASSES[13] = null; // not used300VALUE_CLASSES[14] = // ExtensionRequest301Class.forName("sun.security.x509.CertificateExtensions");302VALUE_CLASSES[15] = null; // not supported yet303VALUE_CLASSES[16] = null; // not supported yet304VALUE_CLASSES[17] = BYTE_ARRAY_CLASS; // SignatureTimestampToken305VALUE_CLASSES[18] = BYTE_ARRAY_CLASS; // CMSAlgorithmProtection306} catch (ClassNotFoundException e) {307throw new ExceptionInInitializerError(e.toString());308}309}310311/**312* Array indicating which PKCS9 attributes are single-valued,313* by index in <code>PKCS9_OIDS</code>.314*/315private static final boolean[] SINGLE_VALUED = {316false,317false, // EMailAddress318false, // UnstructuredName319true, // ContentType320true, // MessageDigest321true, // SigningTime322false, // Countersignature323true, // ChallengePassword324false, // UnstructuredAddress325false, // ExtendedCertificateAttributes326true, // IssuerAndSerialNumber - not supported yet327false, // not used328false, // not used329false, // not used330true, // ExtensionRequest331true, // SMIMECapability - not supported yet332true, // SigningCertificate333true, // SignatureTimestampToken334true, // CMSAlgorithmProtection335};336337/**338* The OID of this attribute.339*/340private ObjectIdentifier oid;341342/**343* The index of the OID of this attribute in <code>PKCS9_OIDS</code>,344* or -1 if it's unknown.345*/346private int index;347348/**349* Value set of this attribute. Its class is given by350* <code>VALUE_CLASSES[index]</code>. The SET itself351* as byte[] if unknown.352*/353private Object value;354355/**356* Construct an attribute object from the attribute's OID and357* value. If the attribute is single-valued, provide only one358* value. If the attribute is multi-valued, provide an array359* containing all the values.360* Arrays of length zero are accepted, though probably useless.361*362* <P> The363* <a href=#classTable>table</a> gives the class that <code>value</code>364* must have for a given attribute.365*366* @exception IllegalArgumentException367* if the <code>value</code> has the wrong type.368*/369public PKCS9Attribute(ObjectIdentifier oid, Object value)370throws IllegalArgumentException {371init(oid, value);372}373374private void init(ObjectIdentifier oid, Object value)375throws IllegalArgumentException {376377this.oid = oid;378index = indexOf(oid, PKCS9_OIDS, 1);379Class<?> clazz = index == -1 ? BYTE_ARRAY_CLASS: VALUE_CLASSES[index];380if (!clazz.isInstance(value)) {381throw new IllegalArgumentException(382"Wrong value class " +383" for attribute " + oid +384" constructing PKCS9Attribute; was " +385value.getClass().toString() + ", should be " +386clazz.toString());387}388this.value = value;389}390391392/**393* Construct a PKCS9Attribute from its encoding on an input394* stream.395*396* @param derVal the DerValue representing the DER encoding of the attribute.397* @exception IOException on parsing error.398*/399public PKCS9Attribute(DerValue derVal) throws IOException {400401DerInputStream derIn = new DerInputStream(derVal.toByteArray());402DerValue[] val = derIn.getSequence(2);403404if (derIn.available() != 0)405throw new IOException("Excess data parsing PKCS9Attribute");406407if (val.length != 2)408throw new IOException("PKCS9Attribute doesn't have two components");409410// get the oid411oid = val[0].getOID();412byte[] content = val[1].toByteArray();413DerValue[] elems = new DerInputStream(content).getSet(1);414415index = indexOf(oid, PKCS9_OIDS, 1);416if (index == -1) {417if (debug != null) {418debug.println("Unsupported signer attribute: " + oid);419}420value = content;421return;422}423424// check single valued have only one value425if (SINGLE_VALUED[index] && elems.length > 1)426throwSingleValuedException();427428// check for illegal element tags429Byte tag;430for (DerValue elem : elems) {431tag = elem.tag;432if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1)433throwTagException(tag);434}435436switch (index) {437case 1: // email address438case 2: // unstructured name439case 8: // unstructured address440{ // open scope441String[] values = new String[elems.length];442443for (int i=0; i < elems.length; i++)444values[i] = elems[i].getAsString();445value = values;446} // close scope447break;448449case 3: // content type450value = elems[0].getOID();451break;452453case 4: // message digest454value = elems[0].getOctetString();455break;456457case 5: // signing time458byte elemTag = elems[0].getTag();459DerInputStream dis = new DerInputStream(elems[0].toByteArray());460value = (elemTag == DerValue.tag_GeneralizedTime) ?461dis.getGeneralizedTime() : dis.getUTCTime();462break;463464case 6: // countersignature465{ // open scope466SignerInfo[] values = new SignerInfo[elems.length];467for (int i=0; i < elems.length; i++)468values[i] =469new SignerInfo(elems[i].toDerInputStream());470value = values;471} // close scope472break;473474case 7: // challenge password475value = elems[0].getAsString();476break;477478case 9: // extended-certificate attribute -- not supported479throw new IOException("PKCS9 extended-certificate " +480"attribute not supported.");481// break unnecessary482case 10: // issuerAndserialNumber attribute -- not supported483throw new IOException("PKCS9 IssuerAndSerialNumber" +484"attribute not supported.");485// break unnecessary486case 11: // RSA DSI proprietary487case 12: // RSA DSI proprietary488throw new IOException("PKCS9 RSA DSI attributes" +489"11 and 12, not supported.");490// break unnecessary491case 13: // S/MIME unused attribute492throw new IOException("PKCS9 attribute #13 not supported.");493// break unnecessary494495case 14: // ExtensionRequest496value = new CertificateExtensions(497new DerInputStream(elems[0].toByteArray()));498break;499500case 15: // SMIME-capability attribute -- not supported501throw new IOException("PKCS9 SMIMECapability " +502"attribute not supported.");503// break unnecessary504case 16: // SigningCertificate attribute505value = new SigningCertificateInfo(elems[0].toByteArray());506break;507508case 17: // SignatureTimestampToken attribute509value = elems[0].toByteArray();510break;511512case 18: // CMSAlgorithmProtection513value = elems[0].toByteArray();514break;515516default: // can't happen517}518}519520/**521* Write the DER encoding of this attribute to an output stream.522*523* <P> N.B.: This method always encodes values of524* ChallengePassword and UnstructuredAddress attributes as ASN.1525* <code>PrintableString</code>s, without checking whether they526* should be encoded as <code>T61String</code>s.527*/528@Override529public void derEncode(OutputStream out) throws IOException {530DerOutputStream temp = new DerOutputStream();531temp.putOID(oid);532switch (index) {533case -1: // Unknown534temp.write((byte[])value);535break;536case 1: // email address537case 2: // unstructured name538{ // open scope539String[] values = (String[]) value;540DerOutputStream[] temps = new541DerOutputStream[values.length];542543for (int i=0; i < values.length; i++) {544temps[i] = new DerOutputStream();545temps[i].putIA5String( values[i]);546}547temp.putOrderedSetOf(DerValue.tag_Set, temps);548} // close scope549break;550551case 3: // content type552{553DerOutputStream temp2 = new DerOutputStream();554temp2.putOID((ObjectIdentifier) value);555temp.write(DerValue.tag_Set, temp2.toByteArray());556}557break;558559case 4: // message digest560{561DerOutputStream temp2 = new DerOutputStream();562temp2.putOctetString((byte[]) value);563temp.write(DerValue.tag_Set, temp2.toByteArray());564}565break;566567case 5: // signing time568{569DerOutputStream temp2 = new DerOutputStream();570temp2.putUTCTime((Date) value);571temp.write(DerValue.tag_Set, temp2.toByteArray());572}573break;574575case 6: // countersignature576temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value);577break;578579case 7: // challenge password580{581DerOutputStream temp2 = new DerOutputStream();582temp2.putPrintableString((String) value);583temp.write(DerValue.tag_Set, temp2.toByteArray());584}585break;586587case 8: // unstructured address588{ // open scope589String[] values = (String[]) value;590DerOutputStream[] temps = new591DerOutputStream[values.length];592593for (int i=0; i < values.length; i++) {594temps[i] = new DerOutputStream();595temps[i].putPrintableString(values[i]);596}597temp.putOrderedSetOf(DerValue.tag_Set, temps);598} // close scope599break;600601case 9: // extended-certificate attribute -- not supported602throw new IOException("PKCS9 extended-certificate " +603"attribute not supported.");604// break unnecessary605case 10: // issuerAndserialNumber attribute -- not supported606throw new IOException("PKCS9 IssuerAndSerialNumber" +607"attribute not supported.");608// break unnecessary609case 11: // RSA DSI proprietary610case 12: // RSA DSI proprietary611throw new IOException("PKCS9 RSA DSI attributes" +612"11 and 12, not supported.");613// break unnecessary614case 13: // S/MIME unused attribute615throw new IOException("PKCS9 attribute #13 not supported.");616// break unnecessary617618case 14: // ExtensionRequest619{620DerOutputStream temp2 = new DerOutputStream();621CertificateExtensions exts = (CertificateExtensions)value;622try {623exts.encode(temp2, true);624} catch (CertificateException ex) {625throw new IOException(ex.toString());626}627temp.write(DerValue.tag_Set, temp2.toByteArray());628}629break;630case 15: // SMIMECapability631throw new IOException("PKCS9 attribute #15 not supported.");632// break unnecessary633634case 16: // SigningCertificate635throw new IOException(636"PKCS9 SigningCertificate attribute not supported.");637// break unnecessary638639case 17: // SignatureTimestampToken640temp.write(DerValue.tag_Set, (byte[])value);641break;642643case 18: // CMSAlgorithmProtection644temp.write(DerValue.tag_Set, (byte[])value);645break;646647default: // can't happen648}649650DerOutputStream derOut = new DerOutputStream();651derOut.write(DerValue.tag_Sequence, temp.toByteArray());652653out.write(derOut.toByteArray());654655}656657/**658* Returns if the attribute is known. Unknown attributes can be created659* from DER encoding with unknown OIDs.660*/661public boolean isKnown() {662return index != -1;663}664665/**666* Get the value of this attribute. If the attribute is667* single-valued, return just the one value. If the attribute is668* multi-valued, return an array containing all the values.669* It is possible for this array to be of length 0.670*671* <P> The672* <a href=#classTable>table</a> gives the class of the value returned,673* depending on the type of this attribute.674*/675public Object getValue() {676return value;677}678679/**680* Show whether this attribute is single-valued.681*/682public boolean isSingleValued() {683return index == -1 || SINGLE_VALUED[index];684}685686/**687* Return the OID of this attribute.688*/689public ObjectIdentifier getOID() {690return oid;691}692693/**694* Return the name of this attribute.695*/696public String getName() {697String n = oid.toString();698KnownOIDs os = KnownOIDs.findMatch(n);699return (os == null? n : os.stdName());700}701702/**703* Return the OID for a given attribute name or null if we don't recognize704* the name.705*/706public static ObjectIdentifier getOID(String name) {707KnownOIDs o = KnownOIDs.findMatch(name);708if (o != null) {709return ObjectIdentifier.of(o);710} else {711return null;712}713}714715/**716* Return the attribute name for a given OID or null if we don't recognize717* the oid.718*/719public static String getName(ObjectIdentifier oid) {720return KnownOIDs.findMatch(oid.toString()).stdName();721}722723/**724* Returns a string representation of this attribute.725*/726@Override727public String toString() {728StringBuilder sb = new StringBuilder(100);729730sb.append("[");731732if (index == -1) {733sb.append(oid.toString());734} else {735sb.append(getName(oid));736}737sb.append(": ");738739if (index == -1 || SINGLE_VALUED[index]) {740if (value instanceof byte[]) { // special case for octet string741HexDumpEncoder hexDump = new HexDumpEncoder();742sb.append(hexDump.encodeBuffer((byte[]) value));743} else {744sb.append(value.toString());745}746sb.append("]");747return sb.toString();748} else { // multi-valued749boolean first = true;750Object[] values = (Object[]) value;751752for (Object curVal : values) {753if (first)754first = false;755else756sb.append(", ");757sb.append(curVal.toString());758}759return sb.toString();760}761}762763/**764* Beginning the search at <code>start</code>, find the first765* index <code>i</code> such that <code>a[i] = obj</code>.766*767* @return the index, if found, and -1 otherwise.768*/769static int indexOf(Object obj, Object[] a, int start) {770for (int i=start; i < a.length; i++) {771if (obj.equals(a[i])) return i;772}773return -1;774}775776/**777* Throw an exception when there are multiple values for778* a single-valued attribute.779*/780private void throwSingleValuedException() throws IOException {781throw new IOException("Single-value attribute " +782oid + " (" + getName() + ")" +783" has multiple values.");784}785786/**787* Throw an exception when the tag on a value encoding is788* wrong for the attribute whose value it is. This method789* will only be called for known tags.790*/791private void throwTagException(Byte tag)792throws IOException {793Byte[] expectedTags = PKCS9_VALUE_TAGS[index];794StringBuilder msg = new StringBuilder(100);795msg.append("Value of attribute ");796msg.append(oid.toString());797msg.append(" (");798msg.append(getName());799msg.append(") has wrong tag: ");800msg.append(tag.toString());801msg.append(". Expected tags: ");802803msg.append(expectedTags[0].toString());804805for (int i = 1; i < expectedTags.length; i++) {806msg.append(", ");807msg.append(expectedTags[i].toString());808}809msg.append(".");810throw new IOException(msg.toString());811}812}813814815