001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2009-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.asn1;
037
038
039
040import java.io.IOException;
041import java.io.OutputStream;
042import java.io.Serializable;
043import java.math.BigInteger;
044import java.nio.ByteBuffer;
045import java.util.Date;
046import java.util.concurrent.atomic.AtomicBoolean;
047
048import com.unboundid.util.ByteStringBuffer;
049import com.unboundid.util.Debug;
050import com.unboundid.util.DebugType;
051import com.unboundid.util.Mutable;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056
057
058
059/**
060 * This class provides a mechanism for writing one or more ASN.1 elements into a
061 * byte string buffer.  It may be cleared and re-used any number of times, and
062 * the contents may be written to an {@code OutputStream} or {@code ByteBuffer},
063 * or copied to a byte array.  {@code ASN1Buffer} instances are not threadsafe
064 * and should not be accessed concurrently by multiple threads.
065 */
066@Mutable()
067@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
068public final class ASN1Buffer
069       implements Serializable
070{
071  /**
072   * The default maximum buffer size.
073   */
074  private static final int DEFAULT_MAX_BUFFER_SIZE = 1_048_576;
075
076
077
078  /**
079   * An array that will be inserted when completing a sequence whose
080   * multi-byte length should be encoded with one byte for the header and one
081   * byte for the number of value bytes.
082   */
083  @NotNull private static final byte[] MULTIBYTE_LENGTH_HEADER_PLUS_ONE =
084       { (byte) 0x81, (byte) 0x00 };
085
086
087
088  /**
089   * An array that will be inserted when completing a sequence whose
090   * multi-byte length should be encoded with one byte for the header and two
091   * bytes for the number of value bytes.
092   */
093  @NotNull private static final byte[] MULTIBYTE_LENGTH_HEADER_PLUS_TWO =
094       { (byte) 0x82, (byte) 0x00, (byte) 0x00 };
095
096
097
098  /**
099   * An array that will be inserted when completing a sequence whose
100   * multi-byte length should be encoded with one byte for the header and three
101   * bytes for the number of value bytes.
102   */
103  @NotNull private static final byte[] MULTIBYTE_LENGTH_HEADER_PLUS_THREE =
104       { (byte) 0x83, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
105
106
107
108  /**
109   * An array that will be inserted when completing a sequence whose
110   * multi-byte length should be encoded with one byte for the header and four
111   * bytes for the number of value bytes.
112   */
113  @NotNull private static final byte[] MULTIBYTE_LENGTH_HEADER_PLUS_FOUR =
114       { (byte) 0x84, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
115
116
117
118  /**
119   * The serial version UID for this serializable class.
120   */
121  private static final long serialVersionUID = -4898230771376551562L;
122
123
124
125  // Indicates whether to zero out the contents of the buffer the next time it
126  // is cleared in order to wipe out any sensitive data it may contain.
127  @NotNull private final AtomicBoolean zeroBufferOnClear;
128
129  // The buffer to which all data will be written.
130  @NotNull private final ByteStringBuffer buffer;
131
132  // The maximum buffer size that should be retained.
133  private final int maxBufferSize;
134
135
136
137  /**
138   * Creates a new instance of this ASN.1 buffer.
139   */
140  public ASN1Buffer()
141  {
142    this(DEFAULT_MAX_BUFFER_SIZE);
143  }
144
145
146
147  /**
148   * Creates a new instance of this ASN.1 buffer with an optional maximum
149   * retained size.  If a maximum size is defined, then this buffer may be used
150   * to hold elements larger than that, but when the buffer is cleared it will
151   * be shrunk to the maximum size.
152   *
153   * @param  maxBufferSize  The maximum buffer size that will be retained by
154   *                        this ASN.1 buffer.  A value less than or equal to
155   *                        zero indicates that no maximum size should be
156   *                        enforced.
157   */
158  public ASN1Buffer(final int maxBufferSize)
159  {
160    this.maxBufferSize = maxBufferSize;
161
162    buffer            = new ByteStringBuffer();
163    zeroBufferOnClear = new AtomicBoolean(false);
164  }
165
166
167
168  /**
169   * Indicates whether the content of the buffer should be zeroed out the next
170   * time it is cleared in order to wipe any sensitive information it may
171   * contain.
172   *
173   * @return  {@code true} if the content of the buffer should be zeroed out the
174   *          next time it is cleared, or {@code false} if not.
175   */
176  public boolean zeroBufferOnClear()
177  {
178    return zeroBufferOnClear.get();
179  }
180
181
182
183  /**
184   * Specifies that the content of the buffer should be zeroed out the next time
185   * it is cleared in order to wipe any sensitive information it may contain.
186   */
187  public void setZeroBufferOnClear()
188  {
189    zeroBufferOnClear.set(true);
190  }
191
192
193
194  /**
195   * Clears the contents of this buffer.  If there are any outstanding sequences
196   * or sets that have been created but not closed, then they must no longer be
197   * used and any attempt to do so may yield unpredictable results.
198   */
199  public void clear()
200  {
201    buffer.clear(zeroBufferOnClear.getAndSet(false));
202
203    if ((maxBufferSize > 0) && (buffer.capacity() > maxBufferSize))
204    {
205      buffer.setCapacity(maxBufferSize);
206    }
207  }
208
209
210
211  /**
212   * Retrieves the current length of this buffer in bytes.
213   *
214   * @return  The current length of this buffer in bytes.
215   */
216  public int length()
217  {
218    return buffer.length();
219  }
220
221
222
223  /**
224   * Adds the provided ASN.1 element to this ASN.1 buffer.
225   *
226   * @param  element  The element to be added.  It must not be {@code null}.
227   */
228  public void addElement(@NotNull final ASN1Element element)
229  {
230    element.encodeTo(buffer);
231  }
232
233
234
235  /**
236   * Adds a Boolean element to this ASN.1 buffer using the default BER type.
237   *
238   * @param  booleanValue  The value to use for the Boolean element.
239   */
240  public void addBoolean(final boolean booleanValue)
241  {
242    addBoolean(ASN1Constants.UNIVERSAL_BOOLEAN_TYPE, booleanValue);
243  }
244
245
246
247  /**
248   * Adds a Boolean element to this ASN.1 buffer using the provided BER type.
249   *
250   * @param  type          The BER type to use for the Boolean element.
251   * @param  booleanValue  The value to use for the Boolean element.
252   */
253  public void addBoolean(final byte type, final boolean booleanValue)
254  {
255    buffer.append(type);
256    buffer.append((byte) 0x01);
257
258    if (booleanValue)
259    {
260      buffer.append((byte) 0xFF);
261    }
262    else
263    {
264      buffer.append((byte) 0x00);
265    }
266  }
267
268
269
270  /**
271   * Adds an enumerated element to this ASN.1 buffer using the default BER type.
272   *
273   * @param  intValue  The value to use for the enumerated element.
274   */
275  public void addEnumerated(final int intValue)
276  {
277    addInteger(ASN1Constants.UNIVERSAL_ENUMERATED_TYPE, intValue);
278  }
279
280
281
282  /**
283   * Adds an enumerated element to this ASN.1 buffer using the provided BER
284   * type.
285   *
286   * @param  type      The BER type to use for the enumerated element.
287   * @param  intValue  The value to use for the enumerated element.
288   */
289  public void addEnumerated(final byte type, final int intValue)
290  {
291    addInteger(type, intValue);
292  }
293
294
295
296  /**
297   * Adds a generalized time element to this ASN.1 buffer using the default BER
298   * type.
299   *
300   * @param  date  The date value that specifies the time to represent.  This
301   *               must not be {@code null}.
302   */
303  public void addGeneralizedTime(@NotNull final Date date)
304  {
305    addGeneralizedTime(date.getTime());
306  }
307
308
309
310  /**
311   * Adds a generalized time element to this ASN.1 buffer using the provided BER
312   * type.
313   *
314   * @param  type  The BER type to use for the generalized time element.
315   * @param  date  The date value that specifies the time to represent.  This
316   *               must not be {@code null}.
317   */
318  public void addGeneralizedTime(final byte type, @NotNull final Date date)
319  {
320    addGeneralizedTime(type, date.getTime());
321  }
322
323
324
325  /**
326   * Adds a generalized time element to this ASN.1 buffer using the default BER
327   * type.
328   *
329   * @param  time  The time to represent.  This must be expressed in
330   *               milliseconds since the epoch (the same format used by
331   *               {@code System.currentTimeMillis()} and
332   *               {@code Date.getTime()}).
333   */
334  public void addGeneralizedTime(final long time)
335  {
336    addGeneralizedTime(ASN1Constants.UNIVERSAL_GENERALIZED_TIME_TYPE, time);
337  }
338
339
340
341  /**
342   * Adds a generalized time element to this ASN.1 buffer using the provided BER
343   * type.
344   *
345   * @param  type  The BER type to use for the generalized time element.
346   * @param  time  The time to represent.  This must be expressed in
347   *               milliseconds since the epoch (the same format used by
348   *               {@code System.currentTimeMillis()} and
349   *               {@code Date.getTime()}).
350   */
351  public void addGeneralizedTime(final byte type, final long time)
352  {
353    buffer.append(type);
354
355    final String timestamp = ASN1GeneralizedTime.encodeTimestamp(time, true);
356    ASN1Element.encodeLengthTo(timestamp.length(), buffer);
357    buffer.append(timestamp);
358  }
359
360
361
362  /**
363   * Adds an integer element to this ASN.1 buffer using the default BER type.
364   *
365   * @param  intValue  The value to use for the integer element.
366   */
367  public void addInteger(final int intValue)
368  {
369    addInteger(ASN1Constants.UNIVERSAL_INTEGER_TYPE, intValue);
370  }
371
372
373
374  /**
375   * Adds an integer element to this ASN.1 buffer using the provided BER type.
376   *
377   * @param  type      The BER type to use for the integer element.
378   * @param  intValue  The value to use for the integer element.
379   */
380  public void addInteger(final byte type, final int intValue)
381  {
382    buffer.append(type);
383
384    if (intValue < 0)
385    {
386      if ((intValue & 0xFFFF_FF80) == 0xFFFF_FF80)
387      {
388        buffer.append((byte) 0x01);
389        buffer.append((byte) (intValue & 0xFF));
390      }
391      else if ((intValue & 0xFFFF_8000) == 0xFFFF_8000)
392      {
393        buffer.append((byte) 0x02);
394        buffer.append((byte) ((intValue >> 8) & 0xFF));
395        buffer.append((byte) (intValue & 0xFF));
396      }
397      else if ((intValue & 0xFF80_0000) == 0xFF80_0000)
398      {
399        buffer.append((byte) 0x03);
400        buffer.append((byte) ((intValue >> 16) & 0xFF));
401        buffer.append((byte) ((intValue >> 8) & 0xFF));
402        buffer.append((byte) (intValue & 0xFF));
403      }
404      else
405      {
406        buffer.append((byte) 0x04);
407        buffer.append((byte) ((intValue >> 24) & 0xFF));
408        buffer.append((byte) ((intValue >> 16) & 0xFF));
409        buffer.append((byte) ((intValue >> 8) & 0xFF));
410        buffer.append((byte) (intValue & 0xFF));
411      }
412    }
413    else
414    {
415      if ((intValue & 0x0000_007F) == intValue)
416      {
417        buffer.append((byte) 0x01);
418        buffer.append((byte) (intValue & 0x7F));
419      }
420      else if ((intValue & 0x0000_7FFF) == intValue)
421      {
422        buffer.append((byte) 0x02);
423        buffer.append((byte) ((intValue >> 8) & 0x7F));
424        buffer.append((byte) (intValue & 0xFF));
425      }
426      else if ((intValue & 0x007F_FFFF) == intValue)
427      {
428        buffer.append((byte) 0x03);
429        buffer.append((byte) ((intValue >> 16) & 0x7F));
430        buffer.append((byte) ((intValue >> 8) & 0xFF));
431        buffer.append((byte) (intValue & 0xFF));
432      }
433      else
434      {
435        buffer.append((byte) 0x04);
436        buffer.append((byte) ((intValue >> 24) & 0x7F));
437        buffer.append((byte) ((intValue >> 16) & 0xFF));
438        buffer.append((byte) ((intValue >> 8) & 0xFF));
439        buffer.append((byte) (intValue & 0xFF));
440      }
441    }
442  }
443
444
445
446  /**
447   * Adds an integer element to this ASN.1 buffer using the default BER type.
448   *
449   * @param  longValue  The value to use for the integer element.
450   */
451  public void addInteger(final long longValue)
452  {
453    addInteger(ASN1Constants.UNIVERSAL_INTEGER_TYPE, longValue);
454  }
455
456
457
458  /**
459   * Adds an integer element to this ASN.1 buffer using the provided BER type.
460   *
461   * @param  type       The BER type to use for the integer element.
462   * @param  longValue  The value to use for the integer element.
463   */
464  public void addInteger(final byte type, final long longValue)
465  {
466    buffer.append(type);
467
468    if (longValue < 0)
469    {
470      if ((longValue & 0xFFFF_FFFF_FFFF_FF80L) == 0xFFFF_FFFF_FFFF_FF80L)
471      {
472        buffer.append((byte) 0x01);
473        buffer.append((byte) (longValue & 0xFFL));
474      }
475      else if ((longValue & 0xFFFF_FFFF_FFFF_8000L) == 0xFFFF_FFFF_FFFF_8000L)
476      {
477        buffer.append((byte) 0x02);
478        buffer.append((byte) ((longValue >> 8) & 0xFFL));
479        buffer.append((byte) (longValue & 0xFFL));
480      }
481      else if ((longValue & 0xFFFF_FFFF_FF80_0000L) == 0xFFFF_FFFF_FF80_0000L)
482      {
483        buffer.append((byte) 0x03);
484        buffer.append((byte) ((longValue >> 16) & 0xFFL));
485        buffer.append((byte) ((longValue >> 8) & 0xFFL));
486        buffer.append((byte) (longValue & 0xFFL));
487      }
488      else if ((longValue & 0xFFFF_FFFF_8000_0000L) == 0xFFFF_FFFF_8000_0000L)
489      {
490        buffer.append((byte) 0x04);
491        buffer.append((byte) ((longValue >> 24) & 0xFFL));
492        buffer.append((byte) ((longValue >> 16) & 0xFFL));
493        buffer.append((byte) ((longValue >> 8) & 0xFFL));
494        buffer.append((byte) (longValue & 0xFFL));
495      }
496      else if ((longValue & 0xFFFF_FF80_0000_0000L) == 0xFFFF_FF80_0000_0000L)
497      {
498        buffer.append((byte) 0x05);
499        buffer.append((byte) ((longValue >> 32) & 0xFFL));
500        buffer.append((byte) ((longValue >> 24) & 0xFFL));
501        buffer.append((byte) ((longValue >> 16) & 0xFFL));
502        buffer.append((byte) ((longValue >> 8) & 0xFFL));
503        buffer.append((byte) (longValue & 0xFFL));
504      }
505      else if ((longValue & 0xFFFF_8000_0000_0000L) == 0xFFFF_8000_0000_0000L)
506      {
507        buffer.append((byte) 0x06);
508        buffer.append((byte) ((longValue >> 40) & 0xFFL));
509        buffer.append((byte) ((longValue >> 32) & 0xFFL));
510        buffer.append((byte) ((longValue >> 24) & 0xFFL));
511        buffer.append((byte) ((longValue >> 16) & 0xFFL));
512        buffer.append((byte) ((longValue >> 8) & 0xFFL));
513        buffer.append((byte) (longValue & 0xFFL));
514      }
515      else if ((longValue & 0xFF80_0000_0000_0000L) == 0xFF80_0000_0000_0000L)
516      {
517        buffer.append((byte) 0x07);
518        buffer.append((byte) ((longValue >> 48) & 0xFFL));
519        buffer.append((byte) ((longValue >> 40) & 0xFFL));
520        buffer.append((byte) ((longValue >> 32) & 0xFFL));
521        buffer.append((byte) ((longValue >> 24) & 0xFFL));
522        buffer.append((byte) ((longValue >> 16) & 0xFFL));
523        buffer.append((byte) ((longValue >> 8) & 0xFFL));
524        buffer.append((byte) (longValue & 0xFFL));
525      }
526      else
527      {
528        buffer.append((byte) 0x08);
529        buffer.append((byte) ((longValue >> 56) & 0xFFL));
530        buffer.append((byte) ((longValue >> 48) & 0xFFL));
531        buffer.append((byte) ((longValue >> 40) & 0xFFL));
532        buffer.append((byte) ((longValue >> 32) & 0xFFL));
533        buffer.append((byte) ((longValue >> 24) & 0xFFL));
534        buffer.append((byte) ((longValue >> 16) & 0xFFL));
535        buffer.append((byte) ((longValue >> 8) & 0xFFL));
536        buffer.append((byte) (longValue & 0xFFL));
537      }
538    }
539    else
540    {
541      if ((longValue & 0x0000_0000_0000_007FL) == longValue)
542      {
543        buffer.append((byte) 0x01);
544        buffer.append((byte) (longValue & 0x7FL));
545      }
546      else if ((longValue & 0x0000_0000_0000_7FFFL) == longValue)
547      {
548        buffer.append((byte) 0x02);
549        buffer.append((byte) ((longValue >> 8) & 0x7FL));
550        buffer.append((byte) (longValue & 0xFFL));
551      }
552      else if ((longValue & 0x0000_0000_007F_FFFFL) == longValue)
553      {
554        buffer.append((byte) 0x03);
555        buffer.append((byte) ((longValue >> 16) & 0x7FL));
556        buffer.append((byte) ((longValue >> 8) & 0xFFL));
557        buffer.append((byte) (longValue & 0xFFL));
558      }
559      else if ((longValue & 0x0000_0000_7FFF_FFFFL) == longValue)
560      {
561        buffer.append((byte) 0x04);
562        buffer.append((byte) ((longValue >> 24) & 0x7FL));
563        buffer.append((byte) ((longValue >> 16) & 0xFFL));
564        buffer.append((byte) ((longValue >> 8) & 0xFFL));
565        buffer.append((byte) (longValue & 0xFFL));
566      }
567      else if ((longValue & 0x0000_007F_FFFF_FFFFL) == longValue)
568      {
569        buffer.append((byte) 0x05);
570        buffer.append((byte) ((longValue >> 32) & 0x7FL));
571        buffer.append((byte) ((longValue >> 24) & 0xFFL));
572        buffer.append((byte) ((longValue >> 16) & 0xFFL));
573        buffer.append((byte) ((longValue >> 8) & 0xFFL));
574        buffer.append((byte) (longValue & 0xFFL));
575      }
576      else if ((longValue & 0x0000_7FFF_FFFF_FFFFL) == longValue)
577      {
578        buffer.append((byte) 0x06);
579        buffer.append((byte) ((longValue >> 40) & 0x7FL));
580        buffer.append((byte) ((longValue >> 32) & 0xFFL));
581        buffer.append((byte) ((longValue >> 24) & 0xFFL));
582        buffer.append((byte) ((longValue >> 16) & 0xFFL));
583        buffer.append((byte) ((longValue >> 8) & 0xFFL));
584        buffer.append((byte) (longValue & 0xFFL));
585      }
586      else if ((longValue & 0x007F_FFFF_FFFF_FFFFL) == longValue)
587      {
588        buffer.append((byte) 0x07);
589        buffer.append((byte) ((longValue >> 48) & 0x7FL));
590        buffer.append((byte) ((longValue >> 40) & 0xFFL));
591        buffer.append((byte) ((longValue >> 32) & 0xFFL));
592        buffer.append((byte) ((longValue >> 24) & 0xFFL));
593        buffer.append((byte) ((longValue >> 16) & 0xFFL));
594        buffer.append((byte) ((longValue >> 8) & 0xFFL));
595        buffer.append((byte) (longValue & 0xFFL));
596      }
597      else
598      {
599        buffer.append((byte) 0x08);
600        buffer.append((byte) ((longValue >> 56) & 0x7FL));
601        buffer.append((byte) ((longValue >> 48) & 0xFFL));
602        buffer.append((byte) ((longValue >> 40) & 0xFFL));
603        buffer.append((byte) ((longValue >> 32) & 0xFFL));
604        buffer.append((byte) ((longValue >> 24) & 0xFFL));
605        buffer.append((byte) ((longValue >> 16) & 0xFFL));
606        buffer.append((byte) ((longValue >> 8) & 0xFFL));
607        buffer.append((byte) (longValue & 0xFFL));
608      }
609    }
610  }
611
612
613
614  /**
615   * Adds an integer element to this ASN.1 buffer using the default BER type.
616   *
617   * @param  value  The value to use for the integer element.  It must not be
618   *                {@code null}.
619   */
620  public void addInteger(@NotNull final BigInteger value)
621  {
622    addInteger(ASN1Constants.UNIVERSAL_INTEGER_TYPE, value);
623  }
624
625
626
627  /**
628   * Adds an integer element to this ASN.1 buffer using the provided BER type.
629   *
630   * @param  type   The BER type to use for the integer element.
631   * @param  value  The value to use for the integer element.  It must not be
632   *                {@code null}.
633   */
634  public void addInteger(final byte type, @NotNull final BigInteger value)
635  {
636    buffer.append(type);
637
638    final byte[] valueBytes = value.toByteArray();
639    ASN1Element.encodeLengthTo(valueBytes.length, buffer);
640    buffer.append(valueBytes);
641  }
642
643
644
645  /**
646   * Adds a null element to this ASN.1 buffer using the default BER type.
647   */
648  public void addNull()
649  {
650    addNull(ASN1Constants.UNIVERSAL_NULL_TYPE);
651  }
652
653
654
655  /**
656   * Adds a null element to this ASN.1 buffer using the provided BER type.
657   *
658   * @param  type  The BER type to use for the null element.
659   */
660  public void addNull(final byte type)
661  {
662    buffer.append(type);
663    buffer.append((byte) 0x00);
664  }
665
666
667
668  /**
669   * Adds an octet string element to this ASN.1 buffer using the default BER
670   * type and no value.
671   */
672  public void addOctetString()
673  {
674    addOctetString(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
675  }
676
677
678
679  /**
680   * Adds an octet string element to this ASN.1 buffer using the provided BER
681   * type and no value.
682   *
683   * @param  type  The BER type to use for the octet string element.
684   */
685  public void addOctetString(final byte type)
686  {
687    buffer.append(type);
688    buffer.append((byte) 0x00);
689  }
690
691
692
693  /**
694   * Adds an octet string element to this ASN.1 buffer using the default BER
695   * type.
696   *
697   * @param  value  The value to use for the octet string element.
698   */
699  public void addOctetString(@Nullable final byte[] value)
700  {
701    addOctetString(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE, value);
702  }
703
704
705
706  /**
707   * Adds an octet string element to this ASN.1 buffer using the default BER
708   * type.
709   *
710   * @param  value  The value to use for the octet string element.
711   */
712  public void addOctetString(@Nullable final CharSequence value)
713  {
714    if (value == null)
715    {
716      addOctetString(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
717    }
718    else
719    {
720      addOctetString(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE,
721                     value.toString());
722    }
723  }
724
725
726
727  /**
728   * Adds an octet string element to this ASN.1 buffer using the default BER
729   * type.
730   *
731   * @param  value  The value to use for the octet string element.
732   */
733  public void addOctetString(@Nullable final String value)
734  {
735    addOctetString(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE, value);
736  }
737
738
739
740  /**
741   * Adds an octet string element to this ASN.1 buffer using the provided BER
742   * type.
743   *
744   * @param  type   The BER type to use for the octet string element.
745   * @param  value  The value to use for the octet string element.
746   */
747  public void addOctetString(final byte type, @Nullable final byte[] value)
748  {
749    buffer.append(type);
750
751    if (value == null)
752    {
753      buffer.append((byte) 0x00);
754    }
755    else
756    {
757      ASN1Element.encodeLengthTo(value.length, buffer);
758      buffer.append(value);
759    }
760  }
761
762
763
764  /**
765   * Adds an octet string element to this ASN.1 buffer using the provided BER
766   * type.
767   *
768   * @param  type   The BER type to use for the octet string element.
769   * @param  value  The value to use for the octet string element.
770   */
771  public void addOctetString(final byte type,
772                             @Nullable final CharSequence value)
773  {
774    if (value == null)
775    {
776      addOctetString(type);
777    }
778    else
779    {
780      addOctetString(type, value.toString());
781    }
782  }
783
784
785
786  /**
787   * Adds an octet string element to this ASN.1 buffer using the provided BER
788   * type.
789   *
790   * @param  type   The BER type to use for the octet string element.
791   * @param  value  The value to use for the octet string element.
792   */
793  public void addOctetString(final byte type, @Nullable final String value)
794  {
795    buffer.append(type);
796
797    if (value == null)
798    {
799      buffer.append((byte) 0x00);
800    }
801    else
802    {
803      // We'll assume that the string contains only ASCII characters and
804      // therefore the number of bytes will equal the number of characters.
805      // However, save the position in case we're wrong and need to re-encode.
806      final int lengthStartPos = buffer.length();
807      ASN1Element.encodeLengthTo(value.length(), buffer);
808
809      final int valueStartPos = buffer.length();
810      buffer.append(value);
811
812      if (buffer.length() != (valueStartPos + value.length()))
813      {
814        final byte[] valueBytes = new byte[buffer.length() - valueStartPos];
815        System.arraycopy(buffer.getBackingArray(), valueStartPos, valueBytes, 0,
816                         valueBytes.length);
817
818        buffer.setLength(lengthStartPos);
819        ASN1Element.encodeLengthTo(valueBytes.length, buffer);
820        buffer.append(valueBytes);
821      }
822    }
823  }
824
825
826
827  /**
828   * Adds a UTC time element to this ASN.1 buffer using the default BER type.
829   *
830   * @param  date  The date value that specifies the time to represent.  This
831   *               must not be {@code null}.
832   */
833  public void addUTCTime(@NotNull final Date date)
834  {
835    addUTCTime(date.getTime());
836  }
837
838
839
840  /**
841   * Adds a UTC time element to this ASN.1 buffer using the provided BER type.
842   *
843   * @param  type  The BER type to use for the UTC time element.
844   * @param  date  The date value that specifies the time to represent.  This
845   *               must not be {@code null}.
846   */
847  public void addUTCTime(final byte type, @NotNull final Date date)
848  {
849    addUTCTime(type, date.getTime());
850  }
851
852
853
854  /**
855   * Adds a UTC time element to this ASN.1 buffer using the default BER type.
856   *
857   * @param  time  The time to represent.  This must be expressed in
858   *               milliseconds since the epoch (the same format used by
859   *               {@code System.currentTimeMillis()} and
860   *               {@code Date.getTime()}).
861   */
862  public void addUTCTime(final long time)
863  {
864    addUTCTime(ASN1Constants.UNIVERSAL_UTC_TIME_TYPE, time);
865  }
866
867
868
869  /**
870   * Adds a UTC time element to this ASN.1 buffer using the provided BER type.
871   *
872   * @param  type  The BER type to use for the UTC time element.
873   * @param  time  The time to represent.  This must be expressed in
874   *               milliseconds since the epoch (the same format used by
875   *               {@code System.currentTimeMillis()} and
876   *               {@code Date.getTime()}).
877   */
878  public void addUTCTime(final byte type, final long time)
879  {
880    buffer.append(type);
881
882    final String timestamp = ASN1UTCTime.encodeTimestamp(time);
883    ASN1Element.encodeLengthTo(timestamp.length(), buffer);
884    buffer.append(timestamp);
885  }
886
887
888
889  /**
890   * Begins adding elements to an ASN.1 sequence using the default BER type.
891   *
892   * @return  An object that may be used to indicate when the end of the
893   *          sequence has been reached.  Once all embedded sequence elements
894   *          have been added, then the {@link ASN1BufferSequence#end} method
895   *          MUST be called to ensure that the sequence is properly encoded.
896   */
897  @NotNull()
898  public ASN1BufferSequence beginSequence()
899  {
900    return beginSequence(ASN1Constants.UNIVERSAL_SEQUENCE_TYPE);
901  }
902
903
904
905  /**
906   * Begins adding elements to an ASN.1 sequence using the provided BER type.
907   *
908   * @param  type  The BER type to use for the sequence.
909   *
910   * @return  An object that may be used to indicate when the end of the
911   *          sequence has been reached.  Once all embedded sequence elements
912   *          have been added, then the {@link ASN1BufferSequence#end} method
913   *          MUST be called to ensure that the sequence is properly encoded.
914   */
915  @NotNull()
916  public ASN1BufferSequence beginSequence(final byte type)
917  {
918    buffer.append(type);
919    return new ASN1BufferSequence(this);
920  }
921
922
923
924  /**
925   * Begins adding elements to an ASN.1 set using the default BER type.
926   *
927   * @return  An object that may be used to indicate when the end of the set has
928   *          been reached.  Once all embedded set elements have been added,
929   *          then the {@link ASN1BufferSet#end} method MUST be called to ensure
930   *          that the set is properly encoded.
931   */
932  @NotNull()
933  public ASN1BufferSet beginSet()
934  {
935    return beginSet(ASN1Constants.UNIVERSAL_SET_TYPE);
936  }
937
938
939
940  /**
941   * Begins adding elements to an ASN.1 set using the provided BER type.
942   *
943   * @param  type  The BER type to use for the set.
944   *
945   * @return  An object that may be used to indicate when the end of the set has
946   *          been reached.  Once all embedded set elements have been added,
947   *          then the {@link ASN1BufferSet#end} method MUST be called to ensure
948   *          that the set is properly encoded.
949   */
950  @NotNull()
951  public ASN1BufferSet beginSet(final byte type)
952  {
953    buffer.append(type);
954    return new ASN1BufferSet(this);
955  }
956
957
958
959  /**
960   * Ensures that the appropriate length is inserted into the internal buffer
961   * after all elements in a sequence or set have been added.
962   *
963   * @param  valueStartPos  The position in which the first value was added.
964   */
965  void endSequenceOrSet(final int valueStartPos)
966  {
967    final int length = buffer.length() - valueStartPos;
968    if (length == 0)
969    {
970      buffer.append((byte) 0x00);
971      return;
972    }
973
974    if ((length & 0x7F) == length)
975    {
976      buffer.insert(valueStartPos, (byte) length);
977    }
978    else if ((length & 0xFF) == length)
979    {
980      buffer.insert(valueStartPos, MULTIBYTE_LENGTH_HEADER_PLUS_ONE);
981
982      final byte[] backingArray = buffer.getBackingArray();
983      backingArray[valueStartPos+1] = (byte) (length & 0xFF);
984    }
985    else if ((length & 0xFFFF) == length)
986    {
987      buffer.insert(valueStartPos, MULTIBYTE_LENGTH_HEADER_PLUS_TWO);
988
989      final byte[] backingArray = buffer.getBackingArray();
990      backingArray[valueStartPos+1] = (byte) ((length >> 8) & 0xFF);
991      backingArray[valueStartPos+2] = (byte) (length & 0xFF);
992    }
993    else if ((length & 0x00FF_FFFF) == length)
994    {
995      buffer.insert(valueStartPos, MULTIBYTE_LENGTH_HEADER_PLUS_THREE);
996
997      final byte[] backingArray = buffer.getBackingArray();
998      backingArray[valueStartPos+1] = (byte) ((length >> 16) & 0xFF);
999      backingArray[valueStartPos+2] = (byte) ((length >> 8) & 0xFF);
1000      backingArray[valueStartPos+3] = (byte) (length & 0xFF);
1001    }
1002    else
1003    {
1004      buffer.insert(valueStartPos, MULTIBYTE_LENGTH_HEADER_PLUS_FOUR);
1005
1006      final byte[] backingArray = buffer.getBackingArray();
1007      backingArray[valueStartPos+1] = (byte) ((length >> 24) & 0xFF);
1008      backingArray[valueStartPos+2] = (byte) ((length >> 16) & 0xFF);
1009      backingArray[valueStartPos+3] = (byte) ((length >> 8) & 0xFF);
1010      backingArray[valueStartPos+4] = (byte) (length & 0xFF);
1011    }
1012  }
1013
1014
1015
1016  /**
1017   * Writes the contents of this buffer to the provided output stream.
1018   *
1019   * @param  outputStream  The output stream to which the data should be
1020   *                       written.
1021   *
1022   * @throws  IOException  If a problem occurs while writing to the provided
1023   *                       output stream.
1024   */
1025  public void writeTo(@NotNull final OutputStream outputStream)
1026         throws IOException
1027  {
1028    if (Debug.debugEnabled(DebugType.ASN1))
1029    {
1030      Debug.debugASN1Write(this);
1031    }
1032
1033    buffer.write(outputStream);
1034  }
1035
1036
1037
1038  /**
1039   * Retrieves a byte array containing the contents of this ASN.1 buffer.
1040   *
1041   * @return  A byte array containing the contents of this ASN.1 buffer.
1042   */
1043  @NotNull()
1044  public byte[] toByteArray()
1045  {
1046    return buffer.toByteArray();
1047  }
1048
1049
1050
1051  /**
1052   * Retrieves a byte buffer that wraps the data associated with this ASN.1
1053   * buffer.  The position will be set to the beginning of the data, and the
1054   * limit will be set to one byte after the end of the data.  The contents
1055   * of the returned byte buffer must not be altered in any way, and the
1056   * contents of this ASN.1 buffer must not be altered until the
1057   * {@code ByteBuffer} is no longer needed.
1058   *
1059   * @return  A byte buffer that wraps the data associated with this ASN.1
1060   *          buffer.
1061   */
1062  @NotNull()
1063  public ByteBuffer asByteBuffer()
1064  {
1065    return ByteBuffer.wrap(buffer.getBackingArray(), 0, buffer.length());
1066  }
1067}