diff --git a/jdk/src/share/classes/sun/security/pkcs/PKCS9Attribute.java b/jdk/src/share/classes/sun/security/pkcs/PKCS9Attribute.java index 64ead04d305..0a9e3a5dbea 100644 --- a/jdk/src/share/classes/sun/security/pkcs/PKCS9Attribute.java +++ b/jdk/src/share/classes/sun/security/pkcs/PKCS9Attribute.java @@ -42,12 +42,14 @@ import sun.misc.HexDumpEncoder; /** * Class supporting any PKCS9 attributes. - * Supports DER decoding and access to attribute values, but not - * DER encoding or setting of values. + * Supports DER decoding/encoding and access to attribute values. * *

Type/Class Table

* The following table shows the correspondence between * PKCS9 attribute types and value component classes. + * For types not listed here, its name is the OID + * in string form, its value is a (single-valued) + * byte array that is the SET's encoding. * *

* @@ -185,6 +187,8 @@ public class PKCS9Attribute implements DerEncoder { */ static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[18]; + private final static Class BYTE_ARRAY_CLASS; + static { // static initializer for PKCS9_OIDS for (int i = 1; i < PKCS9_OIDS.length - 2; i++) { PKCS9_OIDS[i] = @@ -196,6 +200,12 @@ public class PKCS9Attribute implements DerEncoder { ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,16,2,12}); PKCS9_OIDS[PKCS9_OIDS.length - 1] = ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,16,2,14}); + + try { + BYTE_ARRAY_CLASS = Class.forName("[B"); + } catch (ClassNotFoundException e) { + throw new ExceptionInInitializerError(e.toString()); + } } // first element [0] not used @@ -331,7 +341,7 @@ public class PKCS9Attribute implements DerEncoder { VALUE_CLASSES[2] = str; // UnstructuredName VALUE_CLASSES[3] = // ContentType Class.forName("sun.security.util.ObjectIdentifier"); - VALUE_CLASSES[4] = Class.forName("[B"); // MessageDigest (byte[]) + VALUE_CLASSES[4] = BYTE_ARRAY_CLASS; // MessageDigest (byte[]) VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime VALUE_CLASSES[6] = // Countersignature Class.forName("[Lsun.security.pkcs.SignerInfo;"); @@ -347,7 +357,7 @@ public class PKCS9Attribute implements DerEncoder { Class.forName("sun.security.x509.CertificateExtensions"); VALUE_CLASSES[15] = null; // not supported yet VALUE_CLASSES[16] = null; // not supported yet - VALUE_CLASSES[17] = Class.forName("[B"); // SignatureTimestampToken + VALUE_CLASSES[17] = BYTE_ARRAY_CLASS; // SignatureTimestampToken } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e.toString()); } @@ -379,13 +389,20 @@ public class PKCS9Attribute implements DerEncoder { }; /** - * The OID of this attribute is PKCS9_OIDS[index]. + * The OID of this attribute. + */ + private ObjectIdentifier oid; + + /** + * The index of the OID of this attribute in PKCS9_OIDS, + * or -1 if it's unknown. */ private int index; /** * Value set of this attribute. Its class is given by - * VALUE_CLASSES[index]. + * VALUE_CLASSES[index]. The SET itself + * as byte[] if unknown. */ private Object value; @@ -400,6 +417,8 @@ public class PKCS9Attribute implements DerEncoder { * table gives the class that value * must have for a given attribute. * + * @exception IllegalArgumentException + * if the value has the wrong type. */ public PKCS9Attribute(ObjectIdentifier oid, Object value) throws IllegalArgumentException { @@ -419,7 +438,7 @@ public class PKCS9Attribute implements DerEncoder { * attributes are accepted; in particular, case does not matter. * * @exception IllegalArgumentException - * if the name is not recognized of the + * if the name is not recognized or the * value has the wrong type. */ public PKCS9Attribute(String name, Object value) @@ -437,21 +456,17 @@ public class PKCS9Attribute implements DerEncoder { private void init(ObjectIdentifier oid, Object value) throws IllegalArgumentException { + this.oid = oid; index = indexOf(oid, PKCS9_OIDS, 1); - - if (index == -1) - throw new IllegalArgumentException( - "Unsupported OID " + oid + - " constructing PKCS9Attribute."); - - if (!VALUE_CLASSES[index].isInstance(value)) + Class clazz = index == -1 ? BYTE_ARRAY_CLASS: VALUE_CLASSES[index]; + if (!clazz.isInstance(value)) { throw new IllegalArgumentException( "Wrong value class " + " for attribute " + oid + " constructing PKCS9Attribute; was " + value.getClass().toString() + ", should be " + - VALUE_CLASSES[index].toString()); - + clazz.toString()); + } this.value = value; } @@ -475,16 +490,19 @@ public class PKCS9Attribute implements DerEncoder { throw new IOException("PKCS9Attribute doesn't have two components"); // get the oid - ObjectIdentifier oid = val[0].getOID(); + oid = val[0].getOID(); + byte[] content = val[1].toByteArray(); + DerValue[] elems = new DerInputStream(content).getSet(1); + index = indexOf(oid, PKCS9_OIDS, 1); if (index == -1) { if (debug != null) { - debug.println("ignoring unsupported signer attribute: " + oid); + debug.println("Unsupported signer attribute: " + oid); } - throw new ParsingException("Unsupported PKCS9 attribute: " + oid); + value = content; + return; } - DerValue[] elems = new DerInputStream(val[1].toByteArray()).getSet(1); // check single valued have only one value if (SINGLE_VALUED[index] && elems.length > 1) throwSingleValuedException(); @@ -584,8 +602,11 @@ public class PKCS9Attribute implements DerEncoder { */ public void derEncode(OutputStream out) throws IOException { DerOutputStream temp = new DerOutputStream(); - temp.putOID(getOID()); + temp.putOID(oid); switch (index) { + case -1: // Unknown + temp.write((byte[])value); + break; case 1: // email address case 2: // unstructured name { // open scope @@ -703,6 +724,14 @@ public class PKCS9Attribute implements DerEncoder { } + /** + * Returns if the attribute is known. Unknown attributes can be created + * from DER encoding with unknown OIDs. + */ + public boolean isKnown() { + return index != -1; + } + /** * Get the value of this attribute. If the attribute is * single-valued, return just the one value. If the attribute is @@ -721,21 +750,23 @@ public class PKCS9Attribute implements DerEncoder { * Show whether this attribute is single-valued. */ public boolean isSingleValued() { - return SINGLE_VALUED[index]; + return index == -1 || SINGLE_VALUED[index]; } /** * Return the OID of this attribute. */ public ObjectIdentifier getOID() { - return PKCS9_OIDS[index]; + return oid; } /** * Return the name of this attribute. */ public String getName() { - return OID_NAME_TABLE.get(PKCS9_OIDS[index]); + return index == -1 ? + oid.toString() : + OID_NAME_TABLE.get(PKCS9_OIDS[index]); } /** @@ -762,10 +793,14 @@ public class PKCS9Attribute implements DerEncoder { buf.append("["); - buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index])); + if (index == -1) { + buf.append(oid.toString()); + } else { + buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index])); + } buf.append(": "); - if (SINGLE_VALUED[index]) { + if (index == -1 || SINGLE_VALUED[index]) { if (value instanceof byte[]) { // special case for octet string HexDumpEncoder hexDump = new HexDumpEncoder(); buf.append(hexDump.encodeBuffer((byte[]) value)); @@ -809,20 +844,21 @@ public class PKCS9Attribute implements DerEncoder { */ private void throwSingleValuedException() throws IOException { throw new IOException("Single-value attribute " + - getOID() + " (" + getName() + ")" + + oid + " (" + getName() + ")" + " has multiple values."); } /** * Throw an exception when the tag on a value encoding is - * wrong for the attribute whose value it is. + * wrong for the attribute whose value it is. This method + * will only be called for known tags. */ private void throwTagException(Byte tag) throws IOException { Byte[] expectedTags = PKCS9_VALUE_TAGS[index]; StringBuffer msg = new StringBuffer(100); msg.append("Value of attribute "); - msg.append(getOID().toString()); + msg.append(oid.toString()); msg.append(" ("); msg.append(getName()); msg.append(") has wrong tag: "); diff --git a/jdk/test/sun/security/pkcs/pkcs9/UnknownAttribute.java b/jdk/test/sun/security/pkcs/pkcs9/UnknownAttribute.java new file mode 100644 index 00000000000..20cc24e627b --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs9/UnknownAttribute.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8011867 + * @summary Accept unknown PKCS #9 attributes + */ + +import java.io.*; +import java.util.Arrays; + +import sun.misc.HexDumpEncoder; +import sun.security.pkcs.PKCS9Attribute; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; + +public class UnknownAttribute { + + public static void main(String[] args) throws Exception { + // Unknown attr + PKCS9Attribute p1 = new PKCS9Attribute( + PKCS9Attribute.CHALLENGE_PASSWORD_STR, "t0p5ecr3t"); + if (!p1.isKnown()) { + throw new Exception(); + } + // Unknown attr from DER + byte[] data = { + 0x30, 0x08, // SEQUENCE OF + 0x06, 0x02, 0x2A, 0x03, // OID 1.2.3 and + 0x31, 0x02, 0x05, 0x00 // an empty SET + }; + PKCS9Attribute p2 = new PKCS9Attribute(new DerValue(data)); + if (p2.isKnown()) { + throw new Exception(); + } + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + p2.derEncode(bout); + new HexDumpEncoder().encodeBuffer(bout.toByteArray(), System.err); + if (!Arrays.equals(data, bout.toByteArray())) { + throw new Exception(); + } + // Unknown attr from value + try { + new PKCS9Attribute(new ObjectIdentifier("1.2.3"), "hello"); + throw new Exception(); + } catch (IllegalArgumentException iae) { + // Good. Unknown attr must have byte[] value type + } + PKCS9Attribute p3 = new PKCS9Attribute( + new ObjectIdentifier("1.2.3"), new byte[]{0x31,0x02,0x05,0x00}); + if (p3.isKnown()) { + throw new Exception(); + } + bout = new ByteArrayOutputStream(); + p3.derEncode(bout); + if (!Arrays.equals(data, bout.toByteArray())) { + throw new Exception(); + } + } +}