| /* | |
| * Copyright (c) 1996, 2023, 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. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * 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. | |
| */ | |
| package sun.security.util; | |
| import java.io.ByteArrayOutputStream; | |
| import java.math.BigInteger; | |
| import java.nio.charset.Charset; | |
| import java.text.SimpleDateFormat; | |
| import java.util.Date; | |
| import java.util.TimeZone; | |
| import java.util.Comparator; | |
| import java.util.Arrays; | |
| import java.util.Locale; | |
| import static java.nio.charset.StandardCharsets.ISO_8859_1; | |
| import static java.nio.charset.StandardCharsets.US_ASCII; | |
| import static java.nio.charset.StandardCharsets.UTF_16BE; | |
| import static java.nio.charset.StandardCharsets.UTF_8; | |
| /** | |
| * Output stream marshaling DER-encoded data. This is eventually provided | |
| * in the form of a byte array; there is no advance limit on the size of | |
| * that byte array. | |
| * | |
| * <P>At this time, this class supports only a subset of the types of | |
| * DER data encodings which are defined. That subset is sufficient for | |
| * generating most X.509 certificates. | |
| * | |
| * | |
| * @author David Brownell | |
| * @author Amit Kapoor | |
| * @author Hemma Prafullchandra | |
| */ | |
| public final class DerOutputStream | |
| extends ByteArrayOutputStream implements DerEncoder { | |
| /** | |
| * Construct a DER output stream. | |
| * | |
| * @param size how large a buffer to preallocate. | |
| */ | |
| public DerOutputStream(int size) { super(size); } | |
| /** | |
| * Construct a DER output stream. | |
| */ | |
| public DerOutputStream() { } | |
| /** | |
| * Writes tagged, pre-marshaled data. This calculates and encodes | |
| * the length, so that the output data is the standard triple of | |
| * { tag, length, data } used by all DER values. | |
| * | |
| * @param tag the DER value tag for the data, such as | |
| * <em>DerValue.tag_Sequence</em> | |
| * @param buf buffered data, which must be DER-encoded | |
| */ | |
| public DerOutputStream write(byte tag, byte[] buf) { | |
| write(tag); | |
| putLength(buf.length); | |
| writeBytes(buf); | |
| return this; | |
| } | |
| /** | |
| * Writes tagged data using buffer-to-buffer copy. As above, | |
| * this writes a standard DER record. This is often used when | |
| * efficiently encapsulating values in sequences. | |
| * | |
| * @param tag the DER value tag for the data, such as | |
| * <em>DerValue.tag_Sequence</em> | |
| * @param out buffered data | |
| */ | |
| public DerOutputStream write(byte tag, DerOutputStream out) { | |
| write(tag); | |
| putLength(out.count); | |
| write(out.buf, 0, out.count); | |
| return this; | |
| } | |
| /** | |
| * Writes implicitly tagged data using buffer-to-buffer copy. As above, | |
| * this writes a standard DER record. This is often used when | |
| * efficiently encapsulating implicitly tagged values. | |
| * | |
| * @param tag the DER value of the context-specific tag that replaces | |
| * original tag of the value in the output, such as in | |
| * <pre> | |
| * <em> {@code <field> [N] IMPLICIT <type>}</em> | |
| * </pre> | |
| * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, with value=4; | |
| * would be encoded as "81 01 04" whereas in explicit | |
| * tagging it would be encoded as "A1 03 02 01 04". | |
| * Notice that the tag is A1 and not 81, this is because with | |
| * explicit tagging the form is always constructed. | |
| * @param value original value being implicitly tagged | |
| */ | |
| public DerOutputStream writeImplicit(byte tag, DerOutputStream value) { | |
| write(tag); | |
| write(value.buf, 1, value.count-1); | |
| return this; | |
| } | |
| /** | |
| * Marshals pre-encoded DER value onto the output stream. | |
| */ | |
| public DerOutputStream putDerValue(DerValue val) { | |
| val.encode(this); | |
| return this; | |
| } | |
| /* | |
| * PRIMITIVES -- these are "universal" ASN.1 simple types. | |
| * | |
| * BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL | |
| * OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF) | |
| * PrintableString, T61String, IA5String, UTCTime | |
| */ | |
| /** | |
| * Marshals a DER boolean on the output stream. | |
| */ | |
| public DerOutputStream putBoolean(boolean val) { | |
| write(DerValue.tag_Boolean); | |
| putLength(1); | |
| if (val) { | |
| write(0xff); | |
| } else { | |
| write(0); | |
| } | |
| return this; | |
| } | |
| /** | |
| * Marshals a DER enumerated on the output stream. | |
| * @param i the enumerated value. | |
| */ | |
| public DerOutputStream putEnumerated(int i) { | |
| write(DerValue.tag_Enumerated); | |
| putIntegerContents(i); | |
| return this; | |
| } | |
| /** | |
| * Marshals a DER integer on the output stream. | |
| * | |
| * @param i the integer in the form of a BigInteger. | |
| */ | |
| public DerOutputStream putInteger(BigInteger i) { | |
| write(DerValue.tag_Integer); | |
| byte[] buf = i.toByteArray(); // least number of bytes | |
| putLength(buf.length); | |
| writeBytes(buf); | |
| return this; | |
| } | |
| /** | |
| * Marshals a DER integer on the output stream. | |
| * | |
| * @param buf the integer in bytes, equivalent to BigInteger::toByteArray. | |
| */ | |
| public DerOutputStream putInteger(byte[] buf) { | |
| write(DerValue.tag_Integer); | |
| putLength(buf.length); | |
| writeBytes(buf); | |
| return this; | |
| } | |
| /** | |
| * Marshals a DER integer on the output stream. | |
| * @param i the integer in the form of an Integer. | |
| */ | |
| public DerOutputStream putInteger(Integer i) { | |
| return putInteger(i.intValue()); | |
| } | |
| /** | |
| * Marshals a DER integer on the output stream. | |
| * @param i the integer. | |
| */ | |
| public DerOutputStream putInteger(int i) { | |
| write(DerValue.tag_Integer); | |
| putIntegerContents(i); | |
| return this; | |
| } | |
| private void putIntegerContents(int i) { | |
| byte[] bytes = new byte[4]; | |
| int start = 0; | |
| // Obtain the four bytes of the int | |
| bytes[3] = (byte) (i & 0xff); | |
| bytes[2] = (byte)((i & 0xff00) >>> 8); | |
| bytes[1] = (byte)((i & 0xff0000) >>> 16); | |
| bytes[0] = (byte)((i & 0xff000000) >>> 24); | |
| // Reduce them to the least number of bytes needed to | |
| // represent this int | |
| if (bytes[0] == (byte)0xff) { | |
| // Eliminate redundant 0xff | |
| for (int j = 0; j < 3; j++) { | |
| if ((bytes[j] == (byte)0xff) && | |
| ((bytes[j+1] & 0x80) == 0x80)) | |
| start++; | |
| else | |
| break; | |
| } | |
| } else if (bytes[0] == 0x00) { | |
| // Eliminate redundant 0x00 | |
| for (int j = 0; j < 3; j++) { | |
| if ((bytes[j] == 0x00) && | |
| ((bytes[j+1] & 0x80) == 0)) | |
| start++; | |
| else | |
| break; | |
| } | |
| } | |
| putLength(4 - start); | |
| for (int k = start; k < 4; k++) | |
| write(bytes[k]); | |
| } | |
| /** | |
| * Marshals a DER bit string on the output stream. The bit | |
| * string must be byte-aligned. | |
| * | |
| * @param bits the bit string, MSB first | |
| */ | |
| public DerOutputStream putBitString(byte[] bits) { | |
| write(DerValue.tag_BitString); | |
| putLength(bits.length + 1); | |
| write(0); // all of last octet is used | |
| writeBytes(bits); | |
| return this; | |
| } | |
| /** | |
| * Marshals a DER bit string on the output stream. | |
| * The bit strings need not be byte-aligned. | |
| * | |
| * @param ba the bit string, MSB first | |
| */ | |
| public DerOutputStream putUnalignedBitString(BitArray ba) { | |
| byte[] bits = ba.toByteArray(); | |
| write(DerValue.tag_BitString); | |
| putLength(bits.length + 1); | |
| write(bits.length*8 - ba.length()); // excess bits in last octet | |
| writeBytes(bits); | |
| return this; | |
| } | |
| /** | |
| * Marshals a truncated DER bit string on the output stream. | |
| * The bit strings need not be byte-aligned. | |
| * | |
| * @param ba the bit string, MSB first | |
| */ | |
| public DerOutputStream putTruncatedUnalignedBitString(BitArray ba) { | |
| return putUnalignedBitString(ba.truncate()); | |
| } | |
| /** | |
| * DER-encodes an ASN.1 OCTET STRING value on the output stream. | |
| * | |
| * @param octets the octet string | |
| */ | |
| public DerOutputStream putOctetString(byte[] octets) { | |
| return write(DerValue.tag_OctetString, octets); | |
| } | |
| /** | |
| * Marshals a DER "null" value on the output stream. These are | |
| * often used to indicate optional values which have been omitted. | |
| */ | |
| public DerOutputStream putNull() { | |
| write(DerValue.tag_Null); | |
| putLength(0); | |
| return this; | |
| } | |
| /** | |
| * Marshals an object identifier (OID) on the output stream. | |
| * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct. | |
| */ | |
| public DerOutputStream putOID(ObjectIdentifier oid) { | |
| oid.encode(this); | |
| return this; | |
| } | |
| /** | |
| * Marshals a sequence on the output stream. This supports both | |
| * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF" | |
| * (one to N values) constructs. | |
| */ | |
| public DerOutputStream putSequence(DerValue[] seq) { | |
| DerOutputStream bytes = new DerOutputStream(); | |
| int i; | |
| for (i = 0; i < seq.length; i++) | |
| seq[i].encode(bytes); | |
| return write(DerValue.tag_Sequence, bytes); | |
| } | |
| /** | |
| * Marshals the contents of a set on the output stream without | |
| * ordering the elements. Ok for BER encoding, but not for DER | |
| * encoding. | |
| * | |
| * For DER encoding, use orderedPutSet() or orderedPutSetOf(). | |
| */ | |
| public DerOutputStream putSet(DerValue[] set) { | |
| DerOutputStream bytes = new DerOutputStream(); | |
| int i; | |
| for (i = 0; i < set.length; i++) | |
| set[i].encode(bytes); | |
| return write(DerValue.tag_Set, bytes); | |
| } | |
| /** | |
| * Marshals the contents of a set on the output stream. Sets | |
| * are semantically unordered, but DER requires that encodings of | |
| * set elements be sorted into ascending lexicographical order | |
| * before being output. Hence, sets with the same tags and | |
| * elements have the same DER encoding. | |
| * | |
| * This method supports the ASN.1 "SET OF" construct, but not | |
| * "SET", which uses a different order. | |
| */ | |
| public DerOutputStream putOrderedSetOf(byte tag, DerEncoder[] set) { | |
| return putOrderedSet(tag, set, lexOrder); | |
| } | |
| /** | |
| * Marshals the contents of a set on the output stream. Sets | |
| * are semantically unordered, but DER requires that encodings of | |
| * set elements be sorted into ascending tag order | |
| * before being output. Hence, sets with the same tags and | |
| * elements have the same DER encoding. | |
| * | |
| * This method supports the ASN.1 "SET" construct, but not | |
| * "SET OF", which uses a different order. | |
| */ | |
| public DerOutputStream putOrderedSet(byte tag, DerEncoder[] set) { | |
| return putOrderedSet(tag, set, tagOrder); | |
| } | |
| /** | |
| * Lexicographical order comparison on byte arrays, for ordering | |
| * elements of a SET OF objects in DER encoding. | |
| */ | |
| private static final ByteArrayLexOrder lexOrder = new ByteArrayLexOrder(); | |
| /** | |
| * Tag order comparison on byte arrays, for ordering elements of | |
| * SET objects in DER encoding. | |
| */ | |
| private static final ByteArrayTagOrder tagOrder = new ByteArrayTagOrder(); | |
| /** | |
| * Marshals the contents of a set on the output stream with the | |
| * encoding of elements sorted in increasing order. | |
| * | |
| * @param order the order to use when sorting encodings of components. | |
| */ | |
| private DerOutputStream putOrderedSet(byte tag, DerEncoder[] set, | |
| Comparator<byte[]> order) { | |
| DerOutputStream[] streams = new DerOutputStream[set.length]; | |
| for (int i = 0; i < set.length; i++) { | |
| streams[i] = new DerOutputStream(); | |
| set[i].encode(streams[i]); | |
| } | |
| // order the element encodings | |
| byte[][] bufs = new byte[streams.length][]; | |
| for (int i = 0; i < streams.length; i++) { | |
| bufs[i] = streams[i].toByteArray(); | |
| } | |
| Arrays.sort(bufs, order); | |
| DerOutputStream bytes = new DerOutputStream(); | |
| for (int i = 0; i < streams.length; i++) { | |
| bytes.writeBytes(bufs[i]); | |
| } | |
| return write(tag, bytes); | |
| } | |
| /** | |
| * Marshals a string as a DER encoded UTF8String. | |
| */ | |
| public DerOutputStream putUTF8String(String s) { | |
| return writeString(s, DerValue.tag_UTF8String, UTF_8); | |
| } | |
| /** | |
| * Marshals a string as a DER encoded PrintableString. | |
| */ | |
| public DerOutputStream putPrintableString(String s) { | |
| return writeString(s, DerValue.tag_PrintableString, US_ASCII); | |
| } | |
| /** | |
| * Marshals a string as a DER encoded T61String. | |
| */ | |
| public DerOutputStream putT61String(String s) { | |
| /* | |
| * Works for characters that are defined in both ASCII and | |
| * T61. | |
| */ | |
| return writeString(s, DerValue.tag_T61String, ISO_8859_1); | |
| } | |
| /** | |
| * Marshals a string as a DER encoded IA5String. | |
| */ | |
| public DerOutputStream putIA5String(String s) { | |
| return writeString(s, DerValue.tag_IA5String, US_ASCII); | |
| } | |
| /** | |
| * Marshals a string as a DER encoded BMPString. | |
| */ | |
| public DerOutputStream putBMPString(String s) { | |
| return writeString(s, DerValue.tag_BMPString, UTF_16BE); | |
| } | |
| /** | |
| * Marshals a string as a DER encoded GeneralString. | |
| */ | |
| public DerOutputStream putGeneralString(String s) { | |
| return writeString(s, DerValue.tag_GeneralString, US_ASCII); | |
| } | |
| /** | |
| * Private helper routine for writing DER encoded string values. | |
| * @param s the string to write | |
| * @param stringTag one of the DER string tags that indicate which | |
| * encoding should be used to write the string out. | |
| * @param charset the charset that should be used corresponding to | |
| * the above tag. | |
| */ | |
| private DerOutputStream writeString(String s, byte stringTag, Charset charset) { | |
| byte[] data = s.getBytes(charset); | |
| write(stringTag); | |
| putLength(data.length); | |
| writeBytes(data); | |
| return this; | |
| } | |
| /** | |
| * 1/1/1950 is the lowest date that RFC 2630 serializes to UTC time | |
| */ | |
| private static final Date utcLow = new Date(-631152000000L); // Dates before 1/1/1950 | |
| /** | |
| * 12/31/2049 is the highest date that RFC 2630 serializes to UTC time | |
| */ | |
| private static final Date utcHigh = new Date(2524607999000L); | |
| /** | |
| * Takes a Date and chooses UTC or GeneralizedTime as per RFC 2630 | |
| */ | |
| public DerOutputStream putTime(Date d) { | |
| return (d.before(utcLow) || d.after(utcHigh)) ? putGeneralizedTime(d) : putUTCTime(d); | |
| } | |
| /** | |
| * Marshals a DER UTC time/date value. | |
| * | |
| * <P>YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time | |
| * and with seconds (even if seconds=0) as per RFC 5280. | |
| */ | |
| public DerOutputStream putUTCTime(Date d) { | |
| return putTime(d, DerValue.tag_UtcTime); | |
| } | |
| /** | |
| * Marshals a DER Generalized Time/date value. | |
| * | |
| * <P>YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time | |
| * and with seconds (even if seconds=0) as per RFC 5280. | |
| */ | |
| public DerOutputStream putGeneralizedTime(Date d) { | |
| return putTime(d, DerValue.tag_GeneralizedTime); | |
| } | |
| /** | |
| * Private helper routine for marshalling a DER UTC/Generalized | |
| * time/date value. If the tag specified is not that for UTC Time | |
| * then it defaults to Generalized Time. | |
| * @param d the date to be marshalled | |
| * @param tag the tag for UTC Time or Generalized Time | |
| */ | |
| private DerOutputStream putTime(Date d, byte tag) { | |
| /* | |
| * Format the date. | |
| */ | |
| TimeZone tz = TimeZone.getTimeZone("GMT"); | |
| String pattern; | |
| if (tag == DerValue.tag_UtcTime) { | |
| pattern = "yyMMddHHmmss'Z'"; | |
| } else { | |
| tag = DerValue.tag_GeneralizedTime; | |
| pattern = "yyyyMMddHHmmss'Z'"; | |
| } | |
| SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.US); | |
| sdf.setTimeZone(tz); | |
| byte[] time = (sdf.format(d)).getBytes(ISO_8859_1); | |
| /* | |
| * Write the formatted date. | |
| */ | |
| write(tag); | |
| putLength(time.length); | |
| writeBytes(time); | |
| return this; | |
| } | |
| /** | |
| * Put the encoding of the length in the stream. | |
| * | |
| * @param len the length of the attribute. | |
| */ | |
| public void putLength(int len) { | |
| if (len < 128) { | |
| write((byte)len); | |
| } else if (len < (1 << 8)) { | |
| write((byte)0x081); | |
| write((byte)len); | |
| } else if (len < (1 << 16)) { | |
| write((byte)0x082); | |
| write((byte)(len >> 8)); | |
| write((byte)len); | |
| } else if (len < (1 << 24)) { | |
| write((byte)0x083); | |
| write((byte)(len >> 16)); | |
| write((byte)(len >> 8)); | |
| write((byte)len); | |
| } else { | |
| write((byte)0x084); | |
| write((byte)(len >> 24)); | |
| write((byte)(len >> 16)); | |
| write((byte)(len >> 8)); | |
| write((byte)len); | |
| } | |
| } | |
| /** | |
| * Write the current contents of this <code>DerOutputStream</code> | |
| * to an <code>OutputStream</code>. | |
| */ | |
| @Override | |
| public void encode(DerOutputStream out) { | |
| out.writeBytes(toByteArray()); | |
| } | |
| /** | |
| * Write a DerEncoder onto the output stream. | |
| * @param encoder the DerEncoder | |
| */ | |
| public DerOutputStream write(DerEncoder encoder) { | |
| encoder.encode(this); | |
| return this; | |
| } | |
| byte[] buf() { | |
| return buf; | |
| } | |
| } |