• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.bouncycastle.asn1;
2 
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.util.Enumeration;
6 import java.util.NoSuchElementException;
7 
8 /**
9  * ASN.1 OctetStrings, with indefinite length rules, and <i>constructed form</i> support.
10  * <p>
11  * The Basic Encoding Rules (BER) format allows encoding using so called "<i>constructed form</i>",
12  * which DER and CER formats forbid allowing only "primitive form".
13  * </p><p>
14  * This class <b>always</b> produces the constructed form with underlying segments
15  * in an indefinite length array.  If the input wasn't the same, then this output
16  * is not faithful reproduction.
17  * </p>
18  * <p>
19  * See {@link ASN1OctetString} for X.690 encoding rules of OCTET-STRING objects.
20  * </p>
21  */
22 public class BEROctetString
23     extends ASN1OctetString
24 {
25     private static final int DEFAULT_CHUNK_SIZE = 1000;
26 
27     private final int chunkSize;
28     private final ASN1OctetString[] octs;
29 
30     /**
31      * Convert a vector of octet strings into a single byte string
32      */
toBytes( ASN1OctetString[] octs)33     static private byte[] toBytes(
34         ASN1OctetString[]  octs)
35     {
36         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
37 
38         for (int i = 0; i != octs.length; i++)
39         {
40             try
41             {
42                 bOut.write(octs[i].getOctets());
43             }
44             catch (IOException e)
45             {
46                 throw new IllegalArgumentException("exception converting octets " + e.toString());
47             }
48         }
49 
50         return bOut.toByteArray();
51     }
52 
53     /**
54      * Create an OCTET-STRING object from a byte[]
55      * @param string the octets making up the octet string.
56      */
BEROctetString( byte[] string)57     public BEROctetString(
58         byte[] string)
59     {
60         this(string, DEFAULT_CHUNK_SIZE);
61     }
62 
63     /**
64      * Multiple {@link ASN1OctetString} data blocks are input,
65      * the result is <i>constructed form</i>.
66      *
67      * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
68      */
BEROctetString( ASN1OctetString[] octs)69     public BEROctetString(
70         ASN1OctetString[] octs)
71     {
72         this(octs, DEFAULT_CHUNK_SIZE);
73     }
74 
75     /**
76      * Create an OCTET-STRING object from a byte[]
77      * @param string the octets making up the octet string.
78      * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
79      */
BEROctetString( byte[] string, int chunkSize)80     public BEROctetString(
81         byte[] string,
82         int    chunkSize)
83     {
84         this(string, null, chunkSize);
85     }
86 
87     /**
88      * Multiple {@link ASN1OctetString} data blocks are input,
89      * the result is <i>constructed form</i>.
90      *
91      * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
92      * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
93      */
BEROctetString( ASN1OctetString[] octs, int chunkSize)94     public BEROctetString(
95         ASN1OctetString[] octs,
96         int chunkSize)
97     {
98         this(toBytes(octs), octs, chunkSize);
99     }
100 
BEROctetString(byte[] string, ASN1OctetString[] octs, int chunkSize)101     private BEROctetString(byte[] string, ASN1OctetString[] octs, int chunkSize)
102     {
103         super(string);
104         this.octs = octs;
105         this.chunkSize = chunkSize;
106     }
107 
108     /**
109      * Return the OCTET STRINGs that make up this string.
110      *
111      * @return an Enumeration of the component OCTET STRINGs.
112      */
getObjects()113     public Enumeration getObjects()
114     {
115         if (octs == null)
116         {
117             return new Enumeration()
118             {
119                 int pos = 0;
120 
121                 public boolean hasMoreElements()
122                 {
123                     return pos < string.length;
124                 }
125 
126                 public Object nextElement()
127                 {
128                     if (pos < string.length)
129                     {
130                         int length = Math.min(string.length - pos, chunkSize);
131                         byte[] chunk = new byte[length];
132                         System.arraycopy(string, pos, chunk, 0, length);
133                         pos += length;
134                         return new DEROctetString(chunk);
135                     }
136                     throw new NoSuchElementException();
137                 }
138             };
139         }
140 
141         return new Enumeration()
142         {
143             int counter = 0;
144 
145             public boolean hasMoreElements()
146             {
147                 return counter < octs.length;
148             }
149 
150             public Object nextElement()
151             {
152                 if (counter < octs.length)
153                 {
154                     return octs[counter++];
155                 }
156                 throw new NoSuchElementException();
157             }
158         };
159     }
160 
161     boolean isConstructed()
162     {
163         return true;
164     }
165 
166     int encodedLength()
167         throws IOException
168     {
169         int length = 0;
170         for (Enumeration e = getObjects(); e.hasMoreElements();)
171         {
172             length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
173         }
174 
175         return 2 + length + 2;
176     }
177 
178     void encode(ASN1OutputStream out, boolean withTag) throws IOException
179     {
180         out.writeEncodedIndef(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING,  getObjects());
181     }
182 
183     static BEROctetString fromSequence(ASN1Sequence seq)
184     {
185         int count = seq.size();
186         ASN1OctetString[] v = new ASN1OctetString[count];
187         for (int i = 0; i < count; ++i)
188         {
189             v[i] = ASN1OctetString.getInstance(seq.getObjectAt(i));
190         }
191         return new BEROctetString(v);
192     }
193 }
194