/*
* Copyright (c) 1996, 2006, 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.FilterOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.Vector;
import java.util.Comparator;
import java.util.Arrays;
import java.math.BigInteger;
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.
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, Amit Kapoor, Hemma Prafullchandra
/**
* 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 class DerOutputStream
extends ByteArrayOutputStream implements DerEncoder {
Construct an DER output stream.
Params: - size – how large a buffer to preallocate.
/**
* Construct an DER output stream.
*
* @param size how large a buffer to preallocate.
*/
public DerOutputStream(int size) { super(size); }
Construct an DER output stream.
/**
* Construct an DER output stream.
*/
public DerOutputStream() { }
Writes tagged, pre-marshaled data. This calcuates and encodes
the length, so that the output data is the standard triple of
{ tag, length, data } used by all DER values.
Params: - tag – the DER value tag for the data, such as
DerValue.tag_Sequence
- buf – buffered data, which must be DER-encoded
/**
* Writes tagged, pre-marshaled data. This calcuates 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 void write(byte tag, byte[] buf) throws IOException {
write(tag);
putLength(buf.length);
write(buf, 0, buf.length);
}
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.
Params: - tag – the DER value tag for the data, such as
DerValue.tag_Sequence
- out – buffered data
/**
* 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 void write(byte tag, DerOutputStream out) throws IOException {
write(tag);
putLength(out.count);
write(out.buf, 0, out.count);
}
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.
Params: - tag – the DER value of the context-specific tag that replaces
original tag of the value in the output, such as in
[N] IMPLICIT
For example, FooLength [1] IMPLICIT INTEGER, 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. - value – original value being implicitly tagged
/**
* 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> <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 void writeImplicit(byte tag, DerOutputStream value)
throws IOException {
write(tag);
write(value.buf, 1, value.count-1);
}
Marshals pre-encoded DER value onto the output stream.
/**
* Marshals pre-encoded DER value onto the output stream.
*/
public void putDerValue(DerValue val) throws IOException {
val.encode(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.
/**
* Marshals a DER boolean on the output stream.
*/
public void putBoolean(boolean val) throws IOException {
write(DerValue.tag_Boolean);
putLength(1);
if (val) {
write(0xff);
} else {
write(0);
}
}
Marshals a DER enumerated on the output stream.
Params: - i – the enumerated value.
/**
* Marshals a DER enumerated on the output stream.
* @param i the enumerated value.
*/
public void putEnumerated(int i) throws IOException {
write(DerValue.tag_Enumerated);
putIntegerContents(i);
}
Marshals a DER integer on the output stream.
Params: - i – the integer in the form of a BigInteger.
/**
* Marshals a DER integer on the output stream.
*
* @param i the integer in the form of a BigInteger.
*/
public void putInteger(BigInteger i) throws IOException {
write(DerValue.tag_Integer);
byte[] buf = i.toByteArray(); // least number of bytes
putLength(buf.length);
write(buf, 0, buf.length);
}
Marshals a DER integer on the output stream.
Params: - i – the integer in the form of an Integer.
/**
* Marshals a DER integer on the output stream.
* @param i the integer in the form of an Integer.
*/
public void putInteger(Integer i) throws IOException {
putInteger(i.intValue());
}
Marshals a DER integer on the output stream.
Params: - i – the integer.
/**
* Marshals a DER integer on the output stream.
* @param i the integer.
*/
public void putInteger(int i) throws IOException {
write(DerValue.tag_Integer);
putIntegerContents(i);
}
private void putIntegerContents(int i) throws IOException {
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] == 0xff) {
// Eliminate redundant 0xff
for (int j = 0; j < 3; j++) {
if ((bytes[j] == 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.
Params: - bits – the bit string, MSB first
/**
* Marshals a DER bit string on the output stream. The bit
* string must be byte-aligned.
*
* @param bits the bit string, MSB first
*/
public void putBitString(byte[] bits) throws IOException {
write(DerValue.tag_BitString);
putLength(bits.length + 1);
write(0); // all of last octet is used
write(bits);
}
Marshals a DER bit string on the output stream.
The bit strings need not be byte-aligned.
Params: - bits – the bit string, MSB first
/**
* Marshals a DER bit string on the output stream.
* The bit strings need not be byte-aligned.
*
* @param bits the bit string, MSB first
*/
public void putUnalignedBitString(BitArray ba) throws IOException {
byte[] bits = ba.toByteArray();
write(DerValue.tag_BitString);
putLength(bits.length + 1);
write(bits.length*8 - ba.length()); // excess bits in last octet
write(bits);
}
Marshals a truncated DER bit string on the output stream.
The bit strings need not be byte-aligned.
Params: - bits – the bit string, MSB first
/**
* Marshals a truncated DER bit string on the output stream.
* The bit strings need not be byte-aligned.
*
* @param bits the bit string, MSB first
*/
public void putTruncatedUnalignedBitString(BitArray ba) throws IOException {
putUnalignedBitString(ba.truncate());
}
DER-encodes an ASN.1 OCTET STRING value on the output stream.
Params: - octets – the octet string
/**
* DER-encodes an ASN.1 OCTET STRING value on the output stream.
*
* @param octets the octet string
*/
public void putOctetString(byte[] octets) throws IOException {
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.
/**
* Marshals a DER "null" value on the output stream. These are
* often used to indicate optional values which have been omitted.
*/
public void putNull() throws IOException {
write(DerValue.tag_Null);
putLength(0);
}
Marshals an object identifier (OID) on the output stream.
Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct.
/**
* Marshals an object identifier (OID) on the output stream.
* Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct.
*/
public void putOID(ObjectIdentifier oid) throws IOException {
oid.encode(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.
/**
* 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 void putSequence(DerValue[] seq) throws IOException {
DerOutputStream bytes = new DerOutputStream();
int i;
for (i = 0; i < seq.length; i++)
seq[i].encode(bytes);
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().
/**
* 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 void putSet(DerValue[] set) throws IOException {
DerOutputStream bytes = new DerOutputStream();
int i;
for (i = 0; i < set.length; i++)
set[i].encode(bytes);
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.
/**
* 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 void putOrderedSetOf(byte tag, DerEncoder[] set) throws IOException {
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.
/**
* 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 void putOrderedSet(byte tag, DerEncoder[] set) throws IOException {
putOrderedSet(tag, set, tagOrder);
}
Lexicographical order comparison on byte arrays, for ordering
elements of a SET OF objects in DER encoding.
/**
* Lexicographical order comparison on byte arrays, for ordering
* elements of a SET OF objects in DER encoding.
*/
private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder();
Tag order comparison on byte arrays, for ordering elements of
SET objects in DER encoding.
/**
* Tag order comparison on byte arrays, for ordering elements of
* SET objects in DER encoding.
*/
private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder();
Marshals a the contents of a set on the output stream with the
encodings of its sorted in increasing order.
Params: - order – the order to use when sorting encodings of components.
/**
* Marshals a the contents of a set on the output stream with the
* encodings of its sorted in increasing order.
*
* @param order the order to use when sorting encodings of components.
*/
private void putOrderedSet(byte tag, DerEncoder[] set,
Comparator<byte[]> order) throws IOException {
DerOutputStream[] streams = new DerOutputStream[set.length];
for (int i = 0; i < set.length; i++) {
streams[i] = new DerOutputStream();
set[i].derEncode(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.<byte[]>sort(bufs, order);
DerOutputStream bytes = new DerOutputStream();
for (int i = 0; i < streams.length; i++) {
bytes.write(bufs[i]);
}
write(tag, bytes);
}
Marshals a string as a DER encoded UTF8String.
/**
* Marshals a string as a DER encoded UTF8String.
*/
public void putUTF8String(String s) throws IOException {
writeString(s, DerValue.tag_UTF8String, "UTF8");
}
Marshals a string as a DER encoded PrintableString.
/**
* Marshals a string as a DER encoded PrintableString.
*/
public void putPrintableString(String s) throws IOException {
writeString(s, DerValue.tag_PrintableString, "ASCII");
}
Marshals a string as a DER encoded T61String.
/**
* Marshals a string as a DER encoded T61String.
*/
public void putT61String(String s) throws IOException {
/*
* Works for characters that are defined in both ASCII and
* T61.
*/
writeString(s, DerValue.tag_T61String, "ISO-8859-1");
}
Marshals a string as a DER encoded IA5String.
/**
* Marshals a string as a DER encoded IA5String.
*/
public void putIA5String(String s) throws IOException {
writeString(s, DerValue.tag_IA5String, "ASCII");
}
Marshals a string as a DER encoded BMPString.
/**
* Marshals a string as a DER encoded BMPString.
*/
public void putBMPString(String s) throws IOException {
writeString(s, DerValue.tag_BMPString, "UnicodeBigUnmarked");
}
Marshals a string as a DER encoded GeneralString.
/**
* Marshals a string as a DER encoded GeneralString.
*/
public void putGeneralString(String s) throws IOException {
writeString(s, DerValue.tag_GeneralString, "ASCII");
}
Private helper routine for writing DER encoded string values.
Params: - s – the string to write
- stringTag – one of the DER string tags that indicate which
encoding should be used to write the string out.
- enc – the name of the encoder that should be used corresponding
to the above tag.
/**
* 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 enc the name of the encoder that should be used corresponding
* to the above tag.
*/
private void writeString(String s, byte stringTag, String enc)
throws IOException {
byte[] data = s.getBytes(enc);
write(stringTag);
putLength(data.length);
write(data);
}
Marshals a DER UTC time/date value.
YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time
and with seconds (even if seconds=0) as per RFC 3280.
/**
* 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 3280.
*/
public void putUTCTime(Date d) throws IOException {
putTime(d, DerValue.tag_UtcTime);
}
Marshals a DER Generalized Time/date value.
YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time
and with seconds (even if seconds=0) as per RFC 3280.
/**
* 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 3280.
*/
public void putGeneralizedTime(Date d) throws IOException {
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.
Params: - d – the date to be marshalled
- tag – the tag for UTC Time or Generalized Time
/**
* 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 void putTime(Date d, byte tag) throws IOException {
/*
* Format the date.
*/
TimeZone tz = TimeZone.getTimeZone("GMT");
String pattern = null;
if (tag == DerValue.tag_UtcTime) {
pattern = "yyMMddHHmmss'Z'";
} else {
tag = DerValue.tag_GeneralizedTime;
pattern = "yyyyMMddHHmmss'Z'";
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.setTimeZone(tz);
byte[] time = (sdf.format(d)).getBytes("ISO-8859-1");
/*
* Write the formatted date.
*/
write(tag);
putLength(time.length);
write(time);
}
Put the encoding of the length in the stream.
Throws: - IOException – on writing errors.
@params len the length of the attribute.
/**
* Put the encoding of the length in the stream.
*
* @params len the length of the attribute.
* @exception IOException on writing errors.
*/
public void putLength(int len) throws IOException {
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);
}
}
Put the tag of the attribute in the stream.
@params class the tag class type, one of UNIVERSAL, CONTEXT,
APPLICATION or PRIVATE @params form if true, the value is constructed, otherwise it is
primitive. @params val the tag value
/**
* Put the tag of the attribute in the stream.
*
* @params class the tag class type, one of UNIVERSAL, CONTEXT,
* APPLICATION or PRIVATE
* @params form if true, the value is constructed, otherwise it is
* primitive.
* @params val the tag value
*/
public void putTag(byte tagClass, boolean form, byte val) {
byte tag = (byte)(tagClass | val);
if (form) {
tag |= (byte)0x20;
}
write(tag);
}
Write the current contents of this DerOutputStream
to an OutputStream
.
@exception IOException on output error.
/**
* Write the current contents of this <code>DerOutputStream</code>
* to an <code>OutputStream</code>.
*
* @exception IOException on output error.
*/
public void derEncode(OutputStream out) throws IOException {
out.write(toByteArray());
}
}