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