Path: blob/master/src/java.base/share/classes/sun/security/util/DerOutputStream.java
41159 views
/*1* Copyright (c) 1996, 2021, 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.util;2627import java.io.ByteArrayOutputStream;28import java.io.OutputStream;29import java.io.IOException;30import java.math.BigInteger;31import java.nio.charset.Charset;32import java.text.SimpleDateFormat;33import java.util.Date;34import java.util.TimeZone;35import java.util.Comparator;36import java.util.Arrays;37import java.util.Locale;3839import static java.nio.charset.StandardCharsets.*;4041/**42* Output stream marshaling DER-encoded data. This is eventually provided43* in the form of a byte array; there is no advance limit on the size of44* that byte array.45*46* <P>At this time, this class supports only a subset of the types of47* DER data encodings which are defined. That subset is sufficient for48* generating most X.509 certificates.49*50*51* @author David Brownell52* @author Amit Kapoor53* @author Hemma Prafullchandra54*/55public class DerOutputStream56extends ByteArrayOutputStream implements DerEncoder {57/**58* Construct an DER output stream.59*60* @param size how large a buffer to preallocate.61*/62public DerOutputStream(int size) { super(size); }6364/**65* Construct an DER output stream.66*/67public DerOutputStream() { }6869/**70* Writes tagged, pre-marshaled data. This calcuates and encodes71* the length, so that the output data is the standard triple of72* { tag, length, data } used by all DER values.73*74* @param tag the DER value tag for the data, such as75* <em>DerValue.tag_Sequence</em>76* @param buf buffered data, which must be DER-encoded77*/78public void write(byte tag, byte[] buf) throws IOException {79write(tag);80putLength(buf.length);81write(buf, 0, buf.length);82}8384/**85* Writes tagged data using buffer-to-buffer copy. As above,86* this writes a standard DER record. This is often used when87* efficiently encapsulating values in sequences.88*89* @param tag the DER value tag for the data, such as90* <em>DerValue.tag_Sequence</em>91* @param out buffered data92*/93public void write(byte tag, DerOutputStream out) throws IOException {94write(tag);95putLength(out.count);96write(out.buf, 0, out.count);97}9899/**100* Writes implicitly tagged data using buffer-to-buffer copy. As above,101* this writes a standard DER record. This is often used when102* efficiently encapsulating implicitly tagged values.103*104* @param tag the DER value of the context-specific tag that replaces105* original tag of the value in the output, such as in106* <pre>107* <em> {@code <field> [N] IMPLICIT <type>}</em>108* </pre>109* For example, <em>FooLength [1] IMPLICIT INTEGER</em>, with value=4;110* would be encoded as "81 01 04" whereas in explicit111* tagging it would be encoded as "A1 03 02 01 04".112* Notice that the tag is A1 and not 81, this is because with113* explicit tagging the form is always constructed.114* @param value original value being implicitly tagged115*/116public void writeImplicit(byte tag, DerOutputStream value)117throws IOException {118write(tag);119write(value.buf, 1, value.count-1);120}121122/**123* Marshals pre-encoded DER value onto the output stream.124*/125public void putDerValue(DerValue val) throws IOException {126val.encode(this);127}128129/*130* PRIMITIVES -- these are "universal" ASN.1 simple types.131*132* BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL133* OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF)134* PrintableString, T61String, IA5String, UTCTime135*/136137/**138* Marshals a DER boolean on the output stream.139*/140public void putBoolean(boolean val) throws IOException {141write(DerValue.tag_Boolean);142putLength(1);143if (val) {144write(0xff);145} else {146write(0);147}148}149150/**151* Marshals a DER enumerated on the output stream.152* @param i the enumerated value.153*/154public void putEnumerated(int i) throws IOException {155write(DerValue.tag_Enumerated);156putIntegerContents(i);157}158159/**160* Marshals a DER integer on the output stream.161*162* @param i the integer in the form of a BigInteger.163*/164public void putInteger(BigInteger i) throws IOException {165write(DerValue.tag_Integer);166byte[] buf = i.toByteArray(); // least number of bytes167putLength(buf.length);168write(buf, 0, buf.length);169}170171/**172* Marshals a DER integer on the output stream.173*174* @param i the integer in bytes, equivalent to BigInteger::toByteArray.175*/176public void putInteger(byte[] buf) throws IOException {177write(DerValue.tag_Integer);178putLength(buf.length);179write(buf, 0, buf.length);180}181182/**183* Marshals a DER integer on the output stream.184* @param i the integer in the form of an Integer.185*/186public void putInteger(Integer i) throws IOException {187putInteger(i.intValue());188}189190/**191* Marshals a DER integer on the output stream.192* @param i the integer.193*/194public void putInteger(int i) throws IOException {195write(DerValue.tag_Integer);196putIntegerContents(i);197}198199private void putIntegerContents(int i) throws IOException {200201byte[] bytes = new byte[4];202int start = 0;203204// Obtain the four bytes of the int205206bytes[3] = (byte) (i & 0xff);207bytes[2] = (byte)((i & 0xff00) >>> 8);208bytes[1] = (byte)((i & 0xff0000) >>> 16);209bytes[0] = (byte)((i & 0xff000000) >>> 24);210211// Reduce them to the least number of bytes needed to212// represent this int213214if (bytes[0] == (byte)0xff) {215216// Eliminate redundant 0xff217218for (int j = 0; j < 3; j++) {219if ((bytes[j] == (byte)0xff) &&220((bytes[j+1] & 0x80) == 0x80))221start++;222else223break;224}225} else if (bytes[0] == 0x00) {226227// Eliminate redundant 0x00228229for (int j = 0; j < 3; j++) {230if ((bytes[j] == 0x00) &&231((bytes[j+1] & 0x80) == 0))232start++;233else234break;235}236}237238putLength(4 - start);239for (int k = start; k < 4; k++)240write(bytes[k]);241}242243/**244* Marshals a DER bit string on the output stream. The bit245* string must be byte-aligned.246*247* @param bits the bit string, MSB first248*/249public void putBitString(byte[] bits) throws IOException {250write(DerValue.tag_BitString);251putLength(bits.length + 1);252write(0); // all of last octet is used253write(bits);254}255256/**257* Marshals a DER bit string on the output stream.258* The bit strings need not be byte-aligned.259*260* @param ba the bit string, MSB first261*/262public void putUnalignedBitString(BitArray ba) throws IOException {263byte[] bits = ba.toByteArray();264265write(DerValue.tag_BitString);266putLength(bits.length + 1);267write(bits.length*8 - ba.length()); // excess bits in last octet268write(bits);269}270271/**272* Marshals a truncated DER bit string on the output stream.273* The bit strings need not be byte-aligned.274*275* @param ba the bit string, MSB first276*/277public void putTruncatedUnalignedBitString(BitArray ba) throws IOException {278putUnalignedBitString(ba.truncate());279}280281/**282* DER-encodes an ASN.1 OCTET STRING value on the output stream.283*284* @param octets the octet string285*/286public void putOctetString(byte[] octets) throws IOException {287write(DerValue.tag_OctetString, octets);288}289290/**291* Marshals a DER "null" value on the output stream. These are292* often used to indicate optional values which have been omitted.293*/294public void putNull() throws IOException {295write(DerValue.tag_Null);296putLength(0);297}298299/**300* Marshals an object identifier (OID) on the output stream.301* Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct.302*/303public void putOID(ObjectIdentifier oid) throws IOException {304oid.encode(this);305}306307/**308* Marshals a sequence on the output stream. This supports both309* the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF"310* (one to N values) constructs.311*/312public void putSequence(DerValue[] seq) throws IOException {313DerOutputStream bytes = new DerOutputStream();314int i;315316for (i = 0; i < seq.length; i++)317seq[i].encode(bytes);318319write(DerValue.tag_Sequence, bytes);320}321322/**323* Marshals the contents of a set on the output stream without324* ordering the elements. Ok for BER encoding, but not for DER325* encoding.326*327* For DER encoding, use orderedPutSet() or orderedPutSetOf().328*/329public void putSet(DerValue[] set) throws IOException {330DerOutputStream bytes = new DerOutputStream();331int i;332333for (i = 0; i < set.length; i++)334set[i].encode(bytes);335336write(DerValue.tag_Set, bytes);337}338339/**340* Marshals the contents of a set on the output stream. Sets341* are semantically unordered, but DER requires that encodings of342* set elements be sorted into ascending lexicographical order343* before being output. Hence sets with the same tags and344* elements have the same DER encoding.345*346* This method supports the ASN.1 "SET OF" construct, but not347* "SET", which uses a different order.348*/349public void putOrderedSetOf(byte tag, DerEncoder[] set) throws IOException {350putOrderedSet(tag, set, lexOrder);351}352353/**354* Marshals the contents of a set on the output stream. Sets355* are semantically unordered, but DER requires that encodings of356* set elements be sorted into ascending tag order357* before being output. Hence sets with the same tags and358* elements have the same DER encoding.359*360* This method supports the ASN.1 "SET" construct, but not361* "SET OF", which uses a different order.362*/363public void putOrderedSet(byte tag, DerEncoder[] set) throws IOException {364putOrderedSet(tag, set, tagOrder);365}366367/**368* Lexicographical order comparison on byte arrays, for ordering369* elements of a SET OF objects in DER encoding.370*/371private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder();372373/**374* Tag order comparison on byte arrays, for ordering elements of375* SET objects in DER encoding.376*/377private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder();378379/**380* Marshals a the contents of a set on the output stream with the381* encodings of its sorted in increasing order.382*383* @param order the order to use when sorting encodings of components.384*/385private void putOrderedSet(byte tag, DerEncoder[] set,386Comparator<byte[]> order) throws IOException {387DerOutputStream[] streams = new DerOutputStream[set.length];388389for (int i = 0; i < set.length; i++) {390streams[i] = new DerOutputStream();391set[i].derEncode(streams[i]);392}393394// order the element encodings395byte[][] bufs = new byte[streams.length][];396for (int i = 0; i < streams.length; i++) {397bufs[i] = streams[i].toByteArray();398}399Arrays.<byte[]>sort(bufs, order);400401DerOutputStream bytes = new DerOutputStream();402for (int i = 0; i < streams.length; i++) {403bytes.write(bufs[i]);404}405write(tag, bytes);406407}408409/**410* Marshals a string as a DER encoded UTF8String.411*/412public void putUTF8String(String s) throws IOException {413writeString(s, DerValue.tag_UTF8String, UTF_8);414}415416/**417* Marshals a string as a DER encoded PrintableString.418*/419public void putPrintableString(String s) throws IOException {420writeString(s, DerValue.tag_PrintableString, US_ASCII);421}422423/**424* Marshals a string as a DER encoded T61String.425*/426public void putT61String(String s) throws IOException {427/*428* Works for characters that are defined in both ASCII and429* T61.430*/431writeString(s, DerValue.tag_T61String, ISO_8859_1);432}433434/**435* Marshals a string as a DER encoded IA5String.436*/437public void putIA5String(String s) throws IOException {438writeString(s, DerValue.tag_IA5String, US_ASCII);439}440441/**442* Marshals a string as a DER encoded BMPString.443*/444public void putBMPString(String s) throws IOException {445writeString(s, DerValue.tag_BMPString, UTF_16BE);446}447448/**449* Marshals a string as a DER encoded GeneralString.450*/451public void putGeneralString(String s) throws IOException {452writeString(s, DerValue.tag_GeneralString, US_ASCII);453}454455/**456* Private helper routine for writing DER encoded string values.457* @param s the string to write458* @param stringTag one of the DER string tags that indicate which459* encoding should be used to write the string out.460* @param enc the name of the encoder that should be used corresponding461* to the above tag.462*/463private void writeString(String s, byte stringTag, Charset charset)464throws IOException {465466byte[] data = s.getBytes(charset);467write(stringTag);468putLength(data.length);469write(data);470}471472/**473* Marshals a DER UTC time/date value.474*475* <P>YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time476* and with seconds (even if seconds=0) as per RFC 5280.477*/478public void putUTCTime(Date d) throws IOException {479putTime(d, DerValue.tag_UtcTime);480}481482/**483* Marshals a DER Generalized Time/date value.484*485* <P>YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time486* and with seconds (even if seconds=0) as per RFC 5280.487*/488public void putGeneralizedTime(Date d) throws IOException {489putTime(d, DerValue.tag_GeneralizedTime);490}491492/**493* Private helper routine for marshalling a DER UTC/Generalized494* time/date value. If the tag specified is not that for UTC Time495* then it defaults to Generalized Time.496* @param d the date to be marshalled497* @param tag the tag for UTC Time or Generalized Time498*/499private void putTime(Date d, byte tag) throws IOException {500501/*502* Format the date.503*/504505TimeZone tz = TimeZone.getTimeZone("GMT");506String pattern = null;507508if (tag == DerValue.tag_UtcTime) {509pattern = "yyMMddHHmmss'Z'";510} else {511tag = DerValue.tag_GeneralizedTime;512pattern = "yyyyMMddHHmmss'Z'";513}514515SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.US);516sdf.setTimeZone(tz);517byte[] time = (sdf.format(d)).getBytes(ISO_8859_1);518519/*520* Write the formatted date.521*/522523write(tag);524putLength(time.length);525write(time);526}527528/**529* Put the encoding of the length in the stream.530*531* @param len the length of the attribute.532* @exception IOException on writing errors.533*/534public void putLength(int len) throws IOException {535if (len < 128) {536write((byte)len);537538} else if (len < (1 << 8)) {539write((byte)0x081);540write((byte)len);541542} else if (len < (1 << 16)) {543write((byte)0x082);544write((byte)(len >> 8));545write((byte)len);546547} else if (len < (1 << 24)) {548write((byte)0x083);549write((byte)(len >> 16));550write((byte)(len >> 8));551write((byte)len);552553} else {554write((byte)0x084);555write((byte)(len >> 24));556write((byte)(len >> 16));557write((byte)(len >> 8));558write((byte)len);559}560}561562/**563* Put the tag of the attribute in the stream.564*565* @param tagClass the tag class type, one of UNIVERSAL, CONTEXT,566* APPLICATION or PRIVATE567* @param form if true, the value is constructed, otherwise it is568* primitive.569* @param val the tag value570*/571public void putTag(byte tagClass, boolean form, byte val) {572byte tag = (byte)(tagClass | val);573if (form) {574tag |= (byte)0x20;575}576write(tag);577}578579/**580* Write the current contents of this <code>DerOutputStream</code>581* to an <code>OutputStream</code>.582*583* @exception IOException on output error.584*/585public void derEncode(OutputStream out) throws IOException {586out.write(toByteArray());587}588589byte[] buf() {590return buf;591}592}593594595