2 * Copyright 2011-2012 BitMeister Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package jp.bitmeister.asn1.codec.ber;
18 import java.io.IOException;
19 import java.io.OutputStream;
20 import java.math.BigInteger;
21 import java.util.ArrayList;
22 import java.util.List;
24 import jp.bitmeister.asn1.codec.ASN1Encoder;
25 import jp.bitmeister.asn1.exception.ASN1EncodingException;
26 import jp.bitmeister.asn1.processor.ASN1Visitor;
27 import jp.bitmeister.asn1.type.ASN1TagClass;
28 import jp.bitmeister.asn1.type.ASN1TagMode;
29 import jp.bitmeister.asn1.type.ASN1TagValue;
30 import jp.bitmeister.asn1.type.ASN1Type;
31 import jp.bitmeister.asn1.type.CollectionType;
32 import jp.bitmeister.asn1.type.ConstructiveType;
33 import jp.bitmeister.asn1.type.ElementSpecification;
34 import jp.bitmeister.asn1.type.StringType;
35 import jp.bitmeister.asn1.type.TimeType;
36 import jp.bitmeister.asn1.type.TypeSpecification;
37 import jp.bitmeister.asn1.type.UnknownType;
38 import jp.bitmeister.asn1.type.builtin.ANY;
39 import jp.bitmeister.asn1.type.builtin.BIT_STRING;
40 import jp.bitmeister.asn1.type.builtin.BOOLEAN;
41 import jp.bitmeister.asn1.type.builtin.CHOICE;
42 import jp.bitmeister.asn1.type.builtin.ENUMERATED;
43 import jp.bitmeister.asn1.type.builtin.INTEGER;
44 import jp.bitmeister.asn1.type.builtin.NULL;
45 import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
46 import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
47 import jp.bitmeister.asn1.type.builtin.REAL;
48 import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
49 import jp.bitmeister.asn1.type.builtin.SEQUENCE;
50 import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
51 import jp.bitmeister.asn1.type.builtin.SET;
52 import jp.bitmeister.asn1.type.builtin.SET_OF;
55 * BER (Basic Encoding Rules) encoder.
58 * {@code BerEncoder} is an implementation of {@code ASN1Encoder}. It encodes an
59 * ASN.1 data to an array of {@code byte} using Basic Encoding Rules(BER) and
60 * writes the result to {@code OutputStream} that is specified when the encoder
61 * was instantiated. BER encoding process is light-weight compared with
62 * Distinguished Encoding Rules (DER) encoding because some restrictions on DER
66 * @author WATANABE, Jun. <jwat at bitmeister.jp>
71 public class BerEncoder implements ASN1Encoder,
72 ASN1Visitor<EncodedOctets, ASN1EncodingException> {
74 private OutputStream out;
77 * Instantiates a BER encoder.
80 * The {@code OutputStream} that encoded octets will be written.
82 public BerEncoder(OutputStream out) {
87 * Encodes an ASN.1 data.
90 * The ASN.1 data to be encoded
91 * @return The size of encoded octets.
92 * @throws ASN1EncodingException
93 * When an error occurred while the encoding process.
95 public int encode(ASN1Type data) throws ASN1EncodingException {
98 return encode(data, null, null).write(out);
99 } catch (ASN1EncodingException e) {
101 } catch (Exception e) {
102 ASN1EncodingException ex = new ASN1EncodingException();
103 ex.setMessage("Exception thrown while encoding process.", e,
104 data.getClass(), null, data);
110 * Encodes an ASN.1 data or an element of ASN.1 data.
113 * The ASN.1 data to be encoded.
115 * The tag assigned to the element to be encoded.
117 * The {@code TypeSpecification} of a type that tagged
119 * @throws ASN1EncodingException
120 * When an error occurred while the encoding process.
122 EncodedOctets encode(ASN1Type data, ASN1TagValue tag,
123 TypeSpecification typeSpec) throws ASN1EncodingException {
124 if (typeSpec == null) {
125 typeSpec = data.specification();
129 tag = typeSpec.tag();
130 typeSpec = typeSpec.reference();
131 } while (typeSpec != null && tag == null);
133 EncodedOctets octets;
134 if (tag != null && tag.tagMode() == ASN1TagMode.EXPLICIT) {
135 ConstructedOctets constructed = newConstructedOctets();
136 constructed.addElement(encode(data, null, typeSpec));
137 octets = constructed;
139 octets = data.accept(this);
142 octets.fix(tag.tagClass(), tag.tagNumber());
151 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
154 public EncodedOctets visit(BOOLEAN data) {
155 return newPrimitiveOctets(data.value() ? (byte) 0xff : (byte) 0x00);
162 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
165 public EncodedOctets visit(INTEGER data) {
166 return newPrimitiveOctets(data.value().toByteArray());
173 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
174 * .builtin.ENUMERATED)
176 public EncodedOctets visit(ENUMERATED data) {
177 return visit((INTEGER) data);
184 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
187 public EncodedOctets visit(REAL data) {
189 if (data.value() == 0) { // zero.
190 encoded = new byte[0];
191 } else if (data.value().isInfinite()) { // special value.
192 encoded = new byte[] { data.value() == Double.POSITIVE_INFINITY ? (byte) 0x40
194 } else if (data.value().isNaN()) {
195 encoded = new byte[] {0x43}; // X.690 Amendment1 (10/2003)
197 String str = data.value().toString();
198 encoded = new byte[str.length() + 1];
199 if (str.indexOf("E") < 0) {
200 encoded[0] = 0x02; // NR2
202 encoded[0] = 0x03; // NR3
204 System.arraycopy(str.getBytes(), 0, encoded, 1, str.length());
206 return newPrimitiveOctets(encoded);
213 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
214 * .builtin.BIT_STRING)
216 public EncodedOctets visit(BIT_STRING data) {
217 if (data.value().length == 0) {
218 return newPrimitiveOctets((byte) 0x00);
220 int mod = data.value().length % 8;
221 byte[] encoded = new byte[1 + data.value().length / 8
222 + (mod == 0 ? 0 : 1)];
223 encoded[0] = (byte) (mod == 0 ? 0 : 8 - mod);
226 for (boolean b : data.value()) {
228 encoded[index] |= mask;
236 return newPrimitiveOctets(encoded);
243 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
244 * .builtin.OCTET_STRING)
246 public EncodedOctets visit(OCTET_STRING data) throws ASN1EncodingException {
247 return newPrimitiveOctets(data.value());
254 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
257 public EncodedOctets visit(NULL data) throws ASN1EncodingException {
258 return newPrimitiveOctets();
265 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
268 public EncodedOctets visit(SEQUENCE data) throws ASN1EncodingException {
269 return processConstructive(data);
276 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
277 * .builtin.SEQUENCE_OF)
279 public EncodedOctets visit(SEQUENCE_OF<?> data)
280 throws ASN1EncodingException {
281 return processCollection(data);
288 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
291 public ConstructedOctets visit(SET data) throws ASN1EncodingException {
292 return processConstructive(data);
299 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
302 public ConstructedOctets visit(SET_OF<?> data)
303 throws ASN1EncodingException {
304 return processCollection(data);
311 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
314 public EncodedOctets visit(CHOICE data) throws ASN1EncodingException {
315 return encode(data.selectedValue(), data.selectedTag(), null);
322 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
323 * .builtin.OBJECT_IDENTIFIER)
325 public EncodedOctets visit(OBJECT_IDENTIFIER data)
326 throws ASN1EncodingException {
327 if (data.value().size() < 2 || data.value().get(0) < 0
328 || 2 < data.value().get(0) || data.value().get(1) < 0
329 || 39 < data.value().get(1)) {
330 ASN1EncodingException ex = new ASN1EncodingException();
331 ex.setMessage("Invalid OBJECT IDENTIFIER value.", null,
332 data.getClass(), null, data);
335 byte[] encoded = new byte[calculateEncodedOidSize(data, 2) + 1];
336 encoded[0] = (byte) (data.value().get(0) * 40 + data.value().get(1));
337 encodeOid(data, encoded, 2, 1);
338 return newPrimitiveOctets(encoded);
342 * @see jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type.builtin.RELATIVE_OID)
344 public EncodedOctets visit(RELATIVE_OID data) throws ASN1EncodingException {
345 byte[] encoded = new byte[calculateEncodedOidSize(data, 0)];
346 encodeOid(data, encoded, 0, 0);
347 return newPrimitiveOctets(encoded);
350 private int calculateEncodedOidSize(OBJECT_IDENTIFIER data, int start) throws ASN1EncodingException {
352 for (int i = start; i < data.value().size(); i++) {
353 if (data.value().get(i) < 0) {
354 ASN1EncodingException ex = new ASN1EncodingException();
356 "OBJECT IDENTIFIER value must be a positive number.",
357 null, data.getClass(), null, data);
360 size += sizeBy7bits(data.value().get(i));
365 private void encodeOid(OBJECT_IDENTIFIER data, byte[] dest, int start, int offset) {
366 for (int i = start; i < data.value().size(); i++) {
367 offset += encodeToMutipleOctets(dest, offset, data.value().get(i));
375 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
378 public EncodedOctets visit(StringType data) throws ASN1EncodingException {
379 return newPrimitiveOctets(data.value());
386 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
389 public EncodedOctets visit(TimeType data) throws ASN1EncodingException {
390 return newPrimitiveOctets(data.value());
397 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
400 public EncodedOctets visit(ANY data) throws ASN1EncodingException {
401 return encode(data.value(), null, null);
408 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
411 public EncodedOctets visit(UnknownType data) throws ASN1EncodingException {
412 EncodedOctets octets = newPrimitiveOctets(data.value());
413 octets.fix(data.tagClass(), data.tagNumber());
418 * Encodes each element of the {@code ConstructiveType} data.
421 * The data to be encoded.
422 * @throws ASN1EncodingException
423 * When an error occurred while the encoding process.
425 ConstructedOctets processConstructive(ConstructiveType data)
426 throws ASN1EncodingException {
427 ConstructedOctets octets = newConstructedOctets();
428 for (ElementSpecification e : data.getElementTypeList()) {
429 ASN1Type element = data.getComponent(e);
430 if (element == null || !element.hasValue()) {
433 octets.addElement(encode(element, e.tag(), null));
439 * Encodes each component of the {@code CollectionType} data.
442 * The data to be encoded.
443 * @throws ASN1EncodingException
444 * When an error occurred while the encoding process.
446 ConstructedOctets processCollection(CollectionType<? extends ASN1Type, ?> data)
447 throws ASN1EncodingException {
448 ConstructedOctets octets = newConstructedOctets();
449 for (ASN1Type e : data.collection()) {
450 octets.addElement(encode(e, null, null));
456 * Encodes ASN.1 tag to octets.
462 * @param isConstructed
463 * {@code true} the data applied to this tag is constructed.
464 * @return Encoded octets.
466 byte[] encodeTag(ASN1TagClass tagClass, int tagNumber, boolean isConstructed) {
475 case CONTEXT_SPECIFIC:
476 leading = (byte) 0x80;
479 leading = (byte) 0xc0;
486 if (tagNumber <= 0x1e) {
487 leading |= tagNumber;
488 return new byte[] { leading };
491 int size = sizeBy7bits(tagNumber);
492 byte[] id = new byte[size + 1];
494 encodeToMutipleOctets(id, 1, tagNumber);
499 * Encodes the length to octets.
503 * @return Encoded octets.
505 byte[] encodeLength(int length) {
506 if (length <= 0x7f) {
507 // length in short form.
508 return new byte[] { (byte) length };
510 // length in long form.
511 byte[] value = BigInteger.valueOf(length).toByteArray();
512 byte[] encoded = new byte[value.length + 1];
513 encoded[0] = (byte) (value.length | 0x80);
514 System.arraycopy(value, 0, encoded, 1, value.length);
519 * Encodes the value to long form octets.
522 * The array of byte where the result to be written.
524 * The index in the {@code dest} array that indicates where the
525 * result to be written.
528 * @return The size of the result.
530 private int encodeToMutipleOctets(byte[] dest, int offset, long value) {
531 int size = sizeBy7bits(value);
532 int lastIndex = offset + size - 1;
533 for (int index = lastIndex; index >= offset; index--) {
534 dest[index] |= (byte) (value & 0x7f);
535 if (index != lastIndex) {
544 * Estimates the size of the result that the value is encoded to long form.
547 * The value to be encoded.
548 * @return Estimated size.
550 private int sizeBy7bits(long value) {
552 while ((value >>= 7) > 0) {
559 * Instantiates a new primitive octets.
562 * The content octets.
563 * @return An instance of primitive octets.
565 EncodedOctets newPrimitiveOctets(byte... contents) {
566 return new BerPrimitiveOctets(contents);
570 * Instantiates a new constructed octets.
572 * @return An instance of constructed octets.
574 ConstructedOctets newConstructedOctets() {
575 return new BerConstructedOctets();
579 * Abstract base class for classes represent BER encoded octets.
581 * @author WATANABE, Jun. <jwat at bitmeister.jp>
583 abstract class BerOctets implements EncodedOctets {
590 * Returns BER encoded octets length includes prefix and contents.
592 * @return Total length of BER encoded octets.
594 public int totalLength() {
595 return identifier.length + length.length + contentsLength();
599 * Sets identifier and length octets to fix this octets.
602 * The ASN.1 tag class for this data.
604 * The tag number for this data.
606 public void fix(ASN1TagClass tagClass, int tagNumber) {
607 identifier = encodeTag(tagClass, tagNumber, isConstructed());
608 length = encodeLength(contentsLength());
612 * Writes all BER encoded octets to the {@code OutputStream}
615 * The stream to be written.
616 * @throws IOException
617 * when {@code IOException} thrown by {@code OutputStream}.
619 public int write(OutputStream out) throws IOException {
620 out.write(identifier);
623 return totalLength();
627 * Writes contents octets to the {@code OutputStream}.
630 * The stream to be written.
631 * @throws IOException
632 * when {@code OutputStream} throws {@code IOException}.
634 abstract void writeContents(OutputStream out) throws IOException;
639 * Represents primitive BER octets.
642 * This class represents a BER encoded octets that has only one contents
646 * @author WATANABE, Jun. <jwat at bitmeister.jp>
648 class BerPrimitiveOctets extends BerOctets {
650 private byte[] contents;
653 * Instantiates a {@code PrimitiveOctets}.
657 BerPrimitiveOctets(byte... contents) {
658 this.contents = contents;
664 * @see jp.bitmeister.asn1.codec.ber.BerOctets#isConstructed()
666 public boolean isConstructed() {
674 * jp.bitmeister.asn1.codec.ber.BerEncoder2.BerOctets#contentsLength()
676 public int contentsLength() {
677 return contents.length;
684 * jp.bitmeister.asn1.codec.ber.BerEncoder2.BerOctets#writeContents(
685 * java.io.OutputStream)
687 void writeContents(OutputStream out) throws IOException {
694 * Represents constructed BER octets.
697 * This class represents a BER encoded octets that contains some contents
701 * @author WATANABE, Jun. <jwat at bitmeister.jp>
703 class BerConstructedOctets extends BerOctets implements ConstructedOctets {
705 private List<EncodedOctets> elements = new ArrayList<EncodedOctets>();
710 * Appends an element to this octets.
713 * The element to be added.
715 public void addElement(EncodedOctets element) {
716 elements.add(element);
717 length += element.totalLength();
723 * @see jp.bitmeister.asn1.codec.ber.BerOctets#isConstructed()
725 public boolean isConstructed() {
732 * @see jp.bitmeister.asn1.codec.ber.BerOctets#octetsLength()
734 public int contentsLength() {
742 * jp.bitmeister.asn1.codec.ber.BerEncoder.BerOctets#writeContents(java
745 void writeContents(OutputStream out) throws IOException {
746 for (EncodedOctets e : elements) {