8265372: Simplify PKCS9Attribute

Reviewed-by: weijun
This commit is contained in:
Ben Perez 2024-02-16 14:51:31 +00:00 committed by Weijun Wang
parent 80b63b6729
commit 244573e719
4 changed files with 339 additions and 531 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,9 @@
package sun.security.pkcs; package sun.security.pkcs;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.lang.reflect.Array;
import java.util.*;
import java.util.function.BiFunction;
import sun.security.x509.CertificateExtensions; import sun.security.x509.CertificateExtensions;
import sun.security.util.*; import sun.security.util.*;
@ -35,143 +37,6 @@ import sun.security.util.*;
* Class supporting any PKCS9 attributes. * Class supporting any PKCS9 attributes.
* Supports DER decoding/encoding and access to attribute values. * Supports DER decoding/encoding and access to attribute values.
* *
* <a name="classTable"><h3>Type/Class Table</h3></a>
* 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.
*
* <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
*
* <TR>
* <TH>Object Identifier</TH>
* <TH>Attribute Name</TH>
* <TH>Type</TH>
* <TH>Value Class</TH>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.1</TD>
* <TD>EmailAddress</TD>
* <TD>Multi-valued</TD>
* <TD><code>String[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.2</TD>
* <TD>UnstructuredName</TD>
* <TD>Multi-valued</TD>
* <TD><code>String[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.3</TD>
* <TD>ContentType</TD>
* <TD>Single-valued</TD>
* <TD><code>ObjectIdentifier</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.4</TD>
* <TD>MessageDigest</TD>
* <TD>Single-valued</TD>
* <TD><code>byte[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.5</TD>
* <TD>SigningTime</TD>
* <TD>Single-valued</TD>
* <TD><code>Date</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.6</TD>
* <TD>Countersignature</TD>
* <TD>Multi-valued</TD>
* <TD><code>SignerInfo[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.7</TD>
* <TD>ChallengePassword</TD>
* <TD>Single-valued</TD>
* <TD><code>String</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.8</TD>
* <TD>UnstructuredAddress</TD>
* <TD>Single-valued</TD>
* <TD><code>String</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.9</TD>
* <TD>ExtendedCertificateAttributes</TD>
* <TD>Multi-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.10</TD>
* <TD>IssuerAndSerialNumber</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.{11,12}</TD>
* <TD>RSA DSI proprietary</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.13</TD>
* <TD>S/MIME unused assignment</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.14</TD>
* <TD>ExtensionRequest</TD>
* <TD>Single-valued</TD>
* <TD>CertificateExtensions</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.15</TD>
* <TD>SMIMECapability</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.16.2.12</TD>
* <TD>SigningCertificate</TD>
* <TD>Single-valued</TD>
* <TD>SigningCertificateInfo</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.16.2.14</TD>
* <TD>SignatureTimestampToken</TD>
* <TD>Single-valued</TD>
* <TD>byte[]</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.16.2.52</TD>
* <TD>CMSAlgorithmProtection</TD>
* <TD>Single-valued</TD>
* <TD>byte[]</TD>
* </TR>
*
* </TABLE>
*
* @author Douglas Hoover * @author Douglas Hoover
*/ */
public class PKCS9Attribute implements DerEncoder { public class PKCS9Attribute implements DerEncoder {
@ -179,178 +44,181 @@ public class PKCS9Attribute implements DerEncoder {
/* Are we debugging ? */ /* Are we debugging ? */
private static final Debug debug = Debug.getInstance("jar"); private static final Debug debug = Debug.getInstance("jar");
/** /* OID Constants */
* Array of attribute OIDs defined in PKCS9, by number. public static final ObjectIdentifier EMAIL_ADDRESS_OID =
*/
static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[19];
private static final Class<?> BYTE_ARRAY_CLASS;
static {
// set unused PKCS9_OIDS entries to null
// rest are initialized with public constants
PKCS9_OIDS[0] = PKCS9_OIDS[11] = PKCS9_OIDS[12] = PKCS9_OIDS[13] =
PKCS9_OIDS[15] = null;
try {
BYTE_ARRAY_CLASS = Class.forName("[B");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e.toString());
}
}
public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1] =
ObjectIdentifier.of(KnownOIDs.EmailAddress); ObjectIdentifier.of(KnownOIDs.EmailAddress);
public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2] = public static final ObjectIdentifier UNSTRUCTURED_NAME_OID =
ObjectIdentifier.of(KnownOIDs.UnstructuredName); ObjectIdentifier.of(KnownOIDs.UnstructuredName);
public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3] = public static final ObjectIdentifier CONTENT_TYPE_OID =
ObjectIdentifier.of(KnownOIDs.ContentType); ObjectIdentifier.of(KnownOIDs.ContentType);
public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4] = public static final ObjectIdentifier MESSAGE_DIGEST_OID =
ObjectIdentifier.of(KnownOIDs.MessageDigest); ObjectIdentifier.of(KnownOIDs.MessageDigest);
public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5] = public static final ObjectIdentifier SIGNING_TIME_OID =
ObjectIdentifier.of(KnownOIDs.SigningTime); ObjectIdentifier.of(KnownOIDs.SigningTime);
public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6] = public static final ObjectIdentifier COUNTERSIGNATURE_OID =
ObjectIdentifier.of(KnownOIDs.CounterSignature); ObjectIdentifier.of(KnownOIDs.CounterSignature);
public static final ObjectIdentifier CHALLENGE_PASSWORD_OID = public static final ObjectIdentifier CHALLENGE_PASSWORD_OID =
PKCS9_OIDS[7] = ObjectIdentifier.of(KnownOIDs.ChallengePassword); ObjectIdentifier.of(KnownOIDs.ChallengePassword);
public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID = public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID =
PKCS9_OIDS[8] = ObjectIdentifier.of(KnownOIDs.UnstructuredAddress); ObjectIdentifier.of(KnownOIDs.UnstructuredAddress);
public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID = public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID =
PKCS9_OIDS[9] =
ObjectIdentifier.of(KnownOIDs.ExtendedCertificateAttributes); ObjectIdentifier.of(KnownOIDs.ExtendedCertificateAttributes);
public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID = public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID =
PKCS9_OIDS[10] =
ObjectIdentifier.of(KnownOIDs.IssuerAndSerialNumber); ObjectIdentifier.of(KnownOIDs.IssuerAndSerialNumber);
// [11], [12] are RSA DSI proprietary
// [13] ==> signingDescription, S/MIME, not used anymore
public static final ObjectIdentifier EXTENSION_REQUEST_OID = public static final ObjectIdentifier EXTENSION_REQUEST_OID =
PKCS9_OIDS[14] = ObjectIdentifier.of(KnownOIDs.ExtensionRequest); ObjectIdentifier.of(KnownOIDs.ExtensionRequest);
public static final ObjectIdentifier SIGNING_CERTIFICATE_OID = public static final ObjectIdentifier SIGNING_CERTIFICATE_OID =
PKCS9_OIDS[16] = ObjectIdentifier.of(KnownOIDs.SigningCertificate); ObjectIdentifier.of(KnownOIDs.SigningCertificate);
public static final ObjectIdentifier SIGNATURE_TIMESTAMP_TOKEN_OID = public static final ObjectIdentifier SIGNATURE_TIMESTAMP_TOKEN_OID =
PKCS9_OIDS[17] =
ObjectIdentifier.of(KnownOIDs.SignatureTimestampToken); ObjectIdentifier.of(KnownOIDs.SignatureTimestampToken);
public static final ObjectIdentifier CMS_ALGORITHM_PROTECTION_OID = public static final ObjectIdentifier CMS_ALGORITHM_PROTECTION_OID =
PKCS9_OIDS[18] =
ObjectIdentifier.of(KnownOIDs.CMSAlgorithmProtection); ObjectIdentifier.of(KnownOIDs.CMSAlgorithmProtection);
/** /**
* Acceptable ASN.1 tags for DER encodings of values of PKCS9 * Contains information for encoding and getting the value
* attributes, by index in <code>PKCS9_OIDS</code>. * of a given PKCS9 attribute
* Sets of acceptable tags are represented as arrays.
*/ */
private static final Byte[][] PKCS9_VALUE_TAGS = { private record AttributeInfo<T>(boolean singleValued, Class<?> valueClass,
null, Decoder<T> decoder,
{DerValue.tag_IA5String}, // EMailAddress Encoder<T> encoder,
{DerValue.tag_IA5String, byte... valueTags) {
@SuppressWarnings("unchecked")
DerOutputStream encode(Object o) {
var d = new DerOutputStream();
//This type is checked in the PKCS9Attribute constructor
encoder.encode(d, (T)o);
return d;
}
T decode(DerValue d) throws IOException {
return decoder.decode(d);
}
}
@FunctionalInterface
public interface Decoder<R> {
R decode(DerValue t) throws IOException;
}
@FunctionalInterface
public interface Encoder<R> {
void encode(DerOutputStream t, R r);
}
/**
* Map containing the AttributeInfo for supported OIDs
*/
private static final Map<ObjectIdentifier, AttributeInfo<?>> infoMap = new HashMap<>();
/* Helper function for building infoMap */
private static <T> void add(ObjectIdentifier oid, boolean singleValued,
Class<T> valueClass,
Decoder<T> decoder,
Encoder<T> encoder,
byte... valueTags) {
AttributeInfo<T> info =
new AttributeInfo<T>(singleValued, valueClass, decoder, encoder, valueTags);
if (infoMap.put(oid, info) != null) {
throw new RuntimeException("Duplicate oid: " + oid);
}
}
/* Set AttributeInfo for supported PKCS9 attributes */
static {
add(EMAIL_ADDRESS_OID, false, String.class,
DerValue::getAsString,
DerOutputStream::putIA5String,
DerValue.tag_IA5String);
add(UNSTRUCTURED_NAME_OID, false, String.class,
DerValue::getAsString,
DerOutputStream::putIA5String,
DerValue.tag_IA5String,
DerValue.tag_PrintableString, DerValue.tag_PrintableString,
DerValue.tag_T61String, DerValue.tag_T61String,
DerValue.tag_BMPString, DerValue.tag_BMPString,
DerValue.tag_UniversalString, DerValue.tag_UniversalString,
DerValue.tag_UTF8String}, // UnstructuredName DerValue.tag_UTF8String);
{DerValue.tag_ObjectId}, // ContentType
{DerValue.tag_OctetString}, // MessageDigest add(CONTENT_TYPE_OID, true, sun.security.util.ObjectIdentifier.class,
{DerValue.tag_UtcTime, DerValue::getOID,
DerValue.tag_GeneralizedTime}, // SigningTime DerOutputStream::putOID,
{DerValue.tag_Sequence}, // Countersignature DerValue.tag_ObjectId);
{DerValue.tag_PrintableString,
add(MESSAGE_DIGEST_OID, true, byte[].class,
DerValue::getOctetString,
DerOutputStream::putOctetString,
DerValue.tag_OctetString);
add(SIGNING_TIME_OID, true, java.util.Date.class,
DerValue::getTime,
DerOutputStream::putTime,
DerValue.tag_UtcTime,
DerValue.tag_GeneralizedTime);
add(COUNTERSIGNATURE_OID, false, sun.security.pkcs.SignerInfo.class,
e -> new SignerInfo(e.toDerInputStream()),
DerOutputStream::write,
DerValue.tag_Sequence);
add(CHALLENGE_PASSWORD_OID, true, String.class,
DerValue::getAsString,
DerOutputStream::putPrintableString,
DerValue.tag_PrintableString,
DerValue.tag_T61String, DerValue.tag_T61String,
DerValue.tag_BMPString, DerValue.tag_BMPString,
DerValue.tag_UniversalString, DerValue.tag_UniversalString,
DerValue.tag_UTF8String}, // ChallengePassword DerValue.tag_UTF8String);
{DerValue.tag_PrintableString,
add(UNSTRUCTURED_ADDRESS_OID, false, String.class,
DerValue::getAsString,
DerOutputStream::putPrintableString,
DerValue.tag_PrintableString,
DerValue.tag_T61String, DerValue.tag_T61String,
DerValue.tag_BMPString, DerValue.tag_BMPString,
DerValue.tag_UniversalString, DerValue.tag_UniversalString,
DerValue.tag_UTF8String}, // UnstructuredAddress DerValue.tag_UTF8String);
{DerValue.tag_SetOf}, // ExtendedCertificateAttributes
{DerValue.tag_Sequence}, // issuerAndSerialNumber
null,
null,
null,
{DerValue.tag_Sequence}, // extensionRequest
{DerValue.tag_Sequence}, // SMIMECapability
{DerValue.tag_Sequence}, // SigningCertificate
{DerValue.tag_Sequence}, // SignatureTimestampToken
{DerValue.tag_Sequence} // CMSAlgorithmProtection
};
private static final Class<?>[] VALUE_CLASSES = new Class<?>[19]; add(EXTENSION_REQUEST_OID, true, sun.security.x509.CertificateExtensions.class,
a -> new CertificateExtensions(new DerInputStream(a.toByteArray())),
(t, v) -> v.encode(t, true),
DerValue.tag_Sequence);
static { add(SIGNING_CERTIFICATE_OID, true, sun.security.pkcs.SigningCertificateInfo.class,
try { a -> new SigningCertificateInfo(a.toByteArray()),
Class<?> str = Class.forName("[Ljava.lang.String;"); (t, v) -> t.writeBytes(v.toByteArray()),
DerValue.tag_Sequence);
VALUE_CLASSES[0] = null; // not used add(SIGNATURE_TIMESTAMP_TOKEN_OID, true, byte[].class,
VALUE_CLASSES[1] = str; // EMailAddress DerValue::toByteArray,
VALUE_CLASSES[2] = str; // UnstructuredName (t, v) -> t.writeBytes(v),
VALUE_CLASSES[3] = // ContentType DerValue.tag_Sequence);
Class.forName("sun.security.util.ObjectIdentifier");
VALUE_CLASSES[4] = BYTE_ARRAY_CLASS; // MessageDigest (byte[]) add(CMS_ALGORITHM_PROTECTION_OID, true, byte[].class,
VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime DerValue::toByteArray,
VALUE_CLASSES[6] = // Countersignature (t, v) -> t.writeBytes(v),
Class.forName("[Lsun.security.pkcs.SignerInfo;"); DerValue.tag_Sequence);
VALUE_CLASSES[7] = // ChallengePassword
Class.forName("java.lang.String");
VALUE_CLASSES[8] = str; // UnstructuredAddress
VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes
VALUE_CLASSES[10] = null; // IssuerAndSerialNumber
VALUE_CLASSES[11] = null; // not used
VALUE_CLASSES[12] = null; // not used
VALUE_CLASSES[13] = null; // not used
VALUE_CLASSES[14] = // ExtensionRequest
Class.forName("sun.security.x509.CertificateExtensions");
VALUE_CLASSES[15] = null; // not supported yet
VALUE_CLASSES[16] = null; // not supported yet
VALUE_CLASSES[17] = BYTE_ARRAY_CLASS; // SignatureTimestampToken
VALUE_CLASSES[18] = BYTE_ARRAY_CLASS; // CMSAlgorithmProtection
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e.toString());
} }
}
/**
* Array indicating which PKCS9 attributes are single-valued,
* by index in <code>PKCS9_OIDS</code>.
*/
private static final boolean[] SINGLE_VALUED = {
false,
false, // EMailAddress
false, // UnstructuredName
true, // ContentType
true, // MessageDigest
true, // SigningTime
false, // Countersignature
true, // ChallengePassword
false, // UnstructuredAddress
false, // ExtendedCertificateAttributes
true, // IssuerAndSerialNumber - not supported yet
false, // not used
false, // not used
false, // not used
true, // ExtensionRequest
true, // SMIMECapability - not supported yet
true, // SigningCertificate
true, // SignatureTimestampToken
true, // CMSAlgorithmProtection
};
/** /**
* The OID of this attribute. * The OID of this attribute.
*/ */
private ObjectIdentifier oid; private final ObjectIdentifier oid;
/** /**
* The index of the OID of this attribute in <code>PKCS9_OIDS</code>, * The AttributeInfo of this attribute. Can be null if oid is unknown.
* or -1 if it's unknown.
*/ */
private int index; private final AttributeInfo<?> info;
/** /**
* Value set of this attribute. Its class is given by * Value set of this attribute. Its class is given by
* <code>VALUE_CLASSES[index]</code>. The SET itself * <code>AttributeInfo.valueClass</code>. The SET itself
* as byte[] if unknown. * as byte[] if unknown.
*/ */
private Object value; private final Object value;
/** /**
* Construct an attribute object from the attribute's OID and * Construct an attribute object from the attribute's OID and
@ -367,22 +235,13 @@ public class PKCS9Attribute implements DerEncoder {
* if the <code>value</code> has the wrong type. * if the <code>value</code> has the wrong type.
*/ */
public PKCS9Attribute(ObjectIdentifier oid, Object value) public PKCS9Attribute(ObjectIdentifier oid, Object value)
throws IllegalArgumentException {
init(oid, value);
}
private void init(ObjectIdentifier oid, Object value)
throws IllegalArgumentException { throws IllegalArgumentException {
this.oid = oid; this.oid = oid;
index = indexOf(oid, PKCS9_OIDS, 1); this.info = infoMap.get(oid);
Class<?> clazz = index == -1 ? BYTE_ARRAY_CLASS: VALUE_CLASSES[index]; Class<?> clazz = info == null
if (clazz == null) { ? byte[].class
throw new IllegalArgumentException( : info.singleValued ? info.valueClass() : info.valueClass.arrayType();
"No value class supported " +
" for attribute " + oid +
" constructing PKCS9Attribute");
}
if (!clazz.isInstance(value)) { if (!clazz.isInstance(value)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Wrong value class " + "Wrong value class " +
@ -394,7 +253,6 @@ public class PKCS9Attribute implements DerEncoder {
this.value = value; this.value = value;
} }
/** /**
* Construct a PKCS9Attribute from its encoding on an input * Construct a PKCS9Attribute from its encoding on an input
* stream. * stream.
@ -419,8 +277,8 @@ public class PKCS9Attribute implements DerEncoder {
byte[] content = val[1].toByteArray(); byte[] content = val[1].toByteArray();
DerValue[] elems = new DerInputStream(content).getSet(1); DerValue[] elems = new DerInputStream(content).getSet(1);
index = indexOf(oid, PKCS9_OIDS, 1); info = infoMap.get(oid);
if (index == -1) { if (info == null) {
if (debug != null) { if (debug != null) {
debug.println("Unsupported signer attribute: " + oid); debug.println("Unsupported signer attribute: " + oid);
} }
@ -429,94 +287,24 @@ public class PKCS9Attribute implements DerEncoder {
} }
// check single valued have only one value // check single valued have only one value
if (SINGLE_VALUED[index] && elems.length > 1) if (info.singleValued() && elems.length > 1)
throwSingleValuedException(); throwSingleValuedException();
// check for illegal element tags // check for illegal element tags
Byte tag; byte tag;
for (DerValue elem : elems) { for (DerValue elem : elems) {
tag = elem.tag; tag = elem.tag;
if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1) if (indexOf(tag, info.valueTags(), 0) == -1)
throwTagException(tag); throwTagException(tag);
} }
switch (index) { if (info.singleValued) {
case 1: // email address value = info.decode(elems[0]);
case 2: // unstructured name } else {
case 8: // unstructured address value = Array.newInstance(info.valueClass, elems.length);
{ // open scope for (int i = 0; i < elems.length; i++) {
String[] values = new String[elems.length]; Array.set(value, i, info.decode(elems[i]));
}
for (int i=0; i < elems.length; i++)
values[i] = elems[i].getAsString();
value = values;
} // close scope
break;
case 3: // content type
value = elems[0].getOID();
break;
case 4: // message digest
value = elems[0].getOctetString();
break;
case 5: // signing time
byte elemTag = elems[0].getTag();
DerInputStream dis = new DerInputStream(elems[0].toByteArray());
value = dis.getTime();
break;
case 6: // countersignature
{ // open scope
SignerInfo[] values = new SignerInfo[elems.length];
for (int i=0; i < elems.length; i++)
values[i] =
new SignerInfo(elems[i].toDerInputStream());
value = values;
} // close scope
break;
case 7: // challenge password
value = elems[0].getAsString();
break;
case 9: // extended-certificate attribute -- not supported
throw new IOException("PKCS9 extended-certificate " +
"attribute not supported.");
// break unnecessary
case 10: // issuerAndserialNumber attribute -- not supported
throw new IOException("PKCS9 IssuerAndSerialNumber " +
"attribute not supported.");
// break unnecessary
case 11: // RSA DSI proprietary
case 12: // RSA DSI proprietary
throw new IOException("PKCS9 RSA DSI attributes " +
"11 and 12, not supported.");
// break unnecessary
case 13: // S/MIME unused attribute
throw new IOException("PKCS9 attribute #13 not supported.");
// break unnecessary
case 14: // ExtensionRequest
value = new CertificateExtensions(
new DerInputStream(elems[0].toByteArray()));
break;
case 15: // SMIME-capability attribute -- not supported
throw new IOException("PKCS9 SMIMECapability " +
"attribute not supported.");
// break unnecessary
case 16: // SigningCertificate attribute
value = new SigningCertificateInfo(elems[0].toByteArray());
break;
case 17: // SignatureTimestampToken attribute
case 18: // CMSAlgorithmProtection
value = elems[0].toByteArray();
break;
default: // can't happen
} }
} }
@ -532,118 +320,24 @@ public class PKCS9Attribute implements DerEncoder {
public void encode(DerOutputStream out) { public void encode(DerOutputStream out) {
DerOutputStream temp = new DerOutputStream(); DerOutputStream temp = new DerOutputStream();
temp.putOID(oid); temp.putOID(oid);
switch (index) {
case -1: // Unknown if (info == null) {
temp.writeBytes((byte[])value); temp.writeBytes((byte[])value);
break; out.write(DerValue.tag_Sequence, temp.toByteArray());
case 1: // email address return;
case 2: // unstructured name }
{ // open scope
String[] values = (String[]) value; if (info.singleValued) {
temp.write(DerValue.tag_Set, info.encode(value));
} else {
Object[] values = (Object[]) value;
DerOutputStream[] temps = new DerOutputStream[] temps = new
DerOutputStream[values.length]; DerOutputStream[values.length];
for (int i=0; i < values.length; i++) { for (int i=0; i < values.length; i++) {
temps[i] = new DerOutputStream(); temps[i] = info.encode(values[i]);
temps[i].putIA5String( values[i]);
} }
temp.putOrderedSetOf(DerValue.tag_Set, temps); temp.putOrderedSetOf(DerValue.tag_Set, temps);
} // close scope
break;
case 3: // content type
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putOID((ObjectIdentifier) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 4: // message digest
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putOctetString((byte[]) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 5: // signing time
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putTime((Date) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 6: // countersignature
temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value);
break;
case 7: // challenge password
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putPrintableString((String) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 8: // unstructured address
{ // open scope
String[] values = (String[]) value;
DerOutputStream[] temps = new
DerOutputStream[values.length];
for (int i=0; i < values.length; i++) {
temps[i] = new DerOutputStream();
temps[i].putPrintableString(values[i]);
}
temp.putOrderedSetOf(DerValue.tag_Set, temps);
} // close scope
break;
case 9: // extended-certificate attribute -- not supported
throw new IllegalArgumentException("PKCS9 extended-certificate " +
"attribute not supported.");
// break unnecessary
case 10: // issuerAndserialNumber attribute -- not supported
throw new IllegalArgumentException("PKCS9 IssuerAndSerialNumber " +
"attribute not supported.");
// break unnecessary
case 11: // RSA DSI proprietary
case 12: // RSA DSI proprietary
throw new IllegalArgumentException("PKCS9 RSA DSI attributes " +
"11 and 12, not supported.");
// break unnecessary
case 13: // S/MIME unused attribute
throw new IllegalArgumentException("PKCS9 attribute #13 not supported.");
// break unnecessary
case 14: // ExtensionRequest
{
DerOutputStream temp2 = new DerOutputStream();
CertificateExtensions exts = (CertificateExtensions)value;
exts.encode(temp2, true);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 15: // SMIMECapability
throw new IllegalArgumentException("PKCS9 attribute #15 not supported.");
// break unnecessary
case 16: // SigningCertificate
{
DerOutputStream temp2 = new DerOutputStream();
SigningCertificateInfo info = (SigningCertificateInfo)value;
temp2.writeBytes(info.toByteArray());
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 17: // SignatureTimestampToken
case 18: // CMSAlgorithmProtection
temp.write(DerValue.tag_Set, (byte[])value);
break;
default: // can't happen
} }
out.write(DerValue.tag_Sequence, temp.toByteArray()); out.write(DerValue.tag_Sequence, temp.toByteArray());
@ -654,7 +348,7 @@ public class PKCS9Attribute implements DerEncoder {
* from DER encoding with unknown OIDs. * from DER encoding with unknown OIDs.
*/ */
public boolean isKnown() { public boolean isKnown() {
return index != -1; return info != null;
} }
/** /**
@ -675,7 +369,7 @@ public class PKCS9Attribute implements DerEncoder {
* Show whether this attribute is single-valued. * Show whether this attribute is single-valued.
*/ */
public boolean isSingleValued() { public boolean isSingleValued() {
return index == -1 || SINGLE_VALUED[index]; return info == null || info.singleValued();
} }
/** /**
@ -685,6 +379,8 @@ public class PKCS9Attribute implements DerEncoder {
return oid; return oid;
} }
public static Set<ObjectIdentifier> getOIDs() { return infoMap.keySet(); }
/** /**
* Return the name of this attribute. * Return the name of this attribute.
*/ */
@ -724,14 +420,14 @@ public class PKCS9Attribute implements DerEncoder {
sb.append("["); sb.append("[");
if (index == -1) { if (info == null) {
sb.append(oid.toString()); sb.append(oid.toString());
} else { } else {
sb.append(getName(oid)); sb.append(getName(oid));
} }
sb.append(": "); sb.append(": ");
if (index == -1 || SINGLE_VALUED[index]) { if (info == null || info.singleValued()) {
if (value instanceof byte[]) { // special case for octet string if (value instanceof byte[]) { // special case for octet string
HexDumpEncoder hexDump = new HexDumpEncoder(); HexDumpEncoder hexDump = new HexDumpEncoder();
sb.append(hexDump.encodeBuffer((byte[]) value)); sb.append(hexDump.encodeBuffer((byte[]) value));
@ -760,9 +456,9 @@ public class PKCS9Attribute implements DerEncoder {
* *
* @return the index, if found, and -1 otherwise. * @return the index, if found, and -1 otherwise.
*/ */
static int indexOf(Object obj, Object[] a, int start) { static int indexOf(byte b, byte[] bs, int start) {
for (int i=start; i < a.length; i++) { for (int i=start; i < bs.length; i++) {
if (obj.equals(a[i])) return i; if (b == bs[i]) return i;
} }
return -1; return -1;
} }
@ -782,23 +478,23 @@ public class PKCS9Attribute implements DerEncoder {
* wrong for the attribute whose value it is. This method * wrong for the attribute whose value it is. This method
* will only be called for known tags. * will only be called for known tags.
*/ */
private void throwTagException(Byte tag) private void throwTagException(byte tag)
throws IOException { throws IOException {
Byte[] expectedTags = PKCS9_VALUE_TAGS[index]; byte[] expectedTags = info.valueTags();
StringBuilder msg = new StringBuilder(100); StringBuilder msg = new StringBuilder(100);
msg.append("Value of attribute "); msg.append("Value of attribute ");
msg.append(oid.toString()); msg.append(oid.toString());
msg.append(" ("); msg.append(" (");
msg.append(getName()); msg.append(getName());
msg.append(") has wrong tag: "); msg.append(") has wrong tag: ");
msg.append(tag.toString()); msg.append(tag);
msg.append(". Expected tags: "); msg.append(". Expected tags: ");
msg.append(expectedTags[0].toString()); msg.append(expectedTags[0]);
for (int i = 1; i < expectedTags.length; i++) { for (int i = 1; i < expectedTags.length; i++) {
msg.append(", "); msg.append(", ");
msg.append(expectedTags[i].toString()); msg.append(expectedTags[i]);
} }
msg.append("."); msg.append(".");
throw new IOException(msg.toString()); throw new IOException(msg.toString());

View file

@ -267,27 +267,6 @@ public class PKCS9Attributes {
return attributes.get(PKCS9Attribute.getOID(name)); return attributes.get(PKCS9Attribute.getOID(name));
} }
/**
* Get an array of all attributes in this set, in order of OID.
*/
public PKCS9Attribute[] getAttributes() {
PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()];
int j = 0;
for (int i=1; i < PKCS9Attribute.PKCS9_OIDS.length &&
j < attribs.length; i++) {
if (PKCS9Attribute.PKCS9_OIDS[i] == null) {
continue;
}
attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
if (attribs[j] != null)
j++;
}
return attribs;
}
/** /**
* Get an attribute value by OID. * Get an attribute value by OID.
*/ */
@ -318,6 +297,7 @@ public class PKCS9Attributes {
/** /**
* Returns the PKCS9 block in a printable string form. * Returns the PKCS9 block in a printable string form.
*/ */
@Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(200); StringBuilder sb = new StringBuilder(200);
sb.append("PKCS9 Attributes: [\n\t"); sb.append("PKCS9 Attributes: [\n\t");
@ -325,12 +305,10 @@ public class PKCS9Attributes {
PKCS9Attribute value; PKCS9Attribute value;
boolean first = true; boolean first = true;
for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) { for (ObjectIdentifier oid : PKCS9Attribute.getOIDs()) {
if (PKCS9Attribute.PKCS9_OIDS[i] == null) { if (oid == null) continue;
continue;
}
value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
value = getAttribute(oid);
if (value == null) continue; if (value == null) continue;
// we have a value; print it // we have a value; print it

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2024, 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 8265372
* @summary checking PKCS#9 encoding and decoding
* @modules java.base/sun.security.pkcs:+open
* java.base/sun.security.util
* java.base/sun.security.x509
*/
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.SignerInfo;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.KnownOIDs;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
import sun.security.x509.BasicConstraintsExtension;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.X500Name;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.HexFormat;
import static sun.security.pkcs.PKCS9Attribute.*;
public class EncodeDecode {
public static void main(String[] args) throws Exception {
test(EMAIL_ADDRESS_OID, new String[]{"a@a.com", "b@b.org"},
"301f06092a864886f70d010901311216076140612e636f6d16076240622e6f7267");
test(UNSTRUCTURED_NAME_OID, new String[]{"a@a.com", "b@b.org"},
"301f06092a864886f70d010902311216076140612e636f6d16076240622e6f7267");
test(CONTENT_TYPE_OID, CONTENT_TYPE_OID,
"301806092a864886f70d010903310b06092a864886f70d010903");
test(MESSAGE_DIGEST_OID, new byte[10],
"301906092a864886f70d010904310c040a00000000000000000000");
test(SIGNING_TIME_OID, new Date(0),
"301c06092a864886f70d010905310f170d3730303130313030303030305a");
var sis = new SignerInfo[] {
new SignerInfo(new X500Name("CN=x"),
BigInteger.ONE,
AlgorithmId.get("SHA-256"),
AlgorithmId.get("Ed25519"),
new byte[10])
};
test(COUNTERSIGNATURE_OID, sis,
"304706092a864886f70d010906313a30380201013011300c310a30080603550403130178020101300d06096086480165030402010500300506032b6570040a00000000000000000000");
test(CHALLENGE_PASSWORD_OID, "password",
"301706092a864886f70d010907310a130870617373776f7264");
test(UNSTRUCTURED_ADDRESS_OID, new String[]{"a@a.com", "b@b.org"},
"301f06092a864886f70d010908311213076140612e636f6d13076240622e6f7267");
var exts = new CertificateExtensions();
exts.setExtension("bc", new BasicConstraintsExtension(true, true, 2));
test(EXTENSION_REQUEST_OID, exts,
"302306092a864886f70d01090e3116301430120603551d130101ff040830060101ff020102");
var c = Class.forName("sun.security.pkcs.SigningCertificateInfo");
var ctor = c.getDeclaredConstructor(byte[].class);
ctor.setAccessible(true);
// A SigningCertificateInfo with an empty ESSCertID
var sci = ctor.newInstance((Object) new DerOutputStream()
.write(DerValue.tag_Sequence, new DerOutputStream()
.write(DerValue.tag_Sequence, new DerOutputStream()))
.toByteArray());
test(SIGNING_CERTIFICATE_OID, sci,
"3013060b2a864886f70d010910020c310430023000");
var onev = new DerOutputStream().write(DerValue.tag_Sequence,
new DerOutputStream().putOctetString(new byte[10]))
.toByteArray();
test(SIGNATURE_TIMESTAMP_TOKEN_OID, onev,
"301d060b2a864886f70d010910020e310e300c040a00000000000000000000");
test(CMS_ALGORITHM_PROTECTION_OID, onev,
"301b06092a864886f70d010934310e300c040a00000000000000000000");
//Test whether unsupported OIDs are handled properly
test(AlgorithmId.SHA_oid,
new DerOutputStream().write(DerValue.tag_Set, new DerOutputStream().putBoolean(true)).toByteArray(),
"300c06052b0e03021a31030101ff");
}
static void test(ObjectIdentifier oid, Object value, String expected) throws Exception {
System.out.println("---------- " + KnownOIDs.findMatch(oid.toString()).name());
var p9 = new PKCS9Attribute(oid, value);
var enc = new DerOutputStream().write(p9).toByteArray();
if (!HexFormat.of().formatHex(enc).equals(expected)) {
throw new RuntimeException("encode unmatch");
}
var nv = new PKCS9Attribute(new DerValue(enc)).getValue();
boolean equals;
if (value instanceof SignerInfo[] si) {
// equals not defined for SignerInfo
equals = Arrays.toString(si).equals(Arrays.toString((SignerInfo[])nv));
} else if (value instanceof byte[] bb) {
equals = Arrays.equals(bb, (byte[]) nv);
} else if (value.getClass().isArray()) {
equals = Arrays.equals((Object[]) value, (Object[]) nv);
} else if (value.getClass().getName().equals("sun.security.pkcs.SigningCertificateInfo")) {
// equals not defined for SigningCertificateInfo
equals = value.toString().equals(nv.toString());
} else {
equals = nv.equals(value);
}
if (!equals) {
throw new RuntimeException("decode unmatch");
}
}
}

View file

@ -65,7 +65,6 @@ public class NonStandardNames {
// test PKCS9Attributes.toString(), PKCS9Attributes.getAttributes() // test PKCS9Attributes.toString(), PKCS9Attributes.getAttributes()
System.out.println(authed); System.out.println(authed);
authed.getAttributes();
Signature s = Signature.getInstance("SHA256withRSA"); Signature s = Signature.getInstance("SHA256withRSA");
s.initSign(cakg.getPrivateKey()); s.initSign(cakg.getPrivateKey());