001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.protocols.asn1;
028 import org.opends.messages.Message;
029
030
031
032 import org.opends.server.types.ByteString;
033 import org.opends.server.types.DebugLogLevel;
034
035 import static org.opends.server.loggers.debug.DebugLogger.*;
036 import org.opends.server.loggers.debug.DebugTracer;
037 import static org.opends.messages.ProtocolMessages.*;
038 import static org.opends.server.protocols.asn1.ASN1Constants.*;
039 import static org.opends.server.util.ServerConstants.*;
040 import static org.opends.server.util.StaticUtils.*;
041
042
043
044 /**
045 * This class defines the data structures and methods to use when interacting
046 * with ASN.1 octet string elements.
047 * <BR><BR>
048 * Note that this class also implements the <CODE>ByteString</CODE> interface,
049 * but in most cases whenever it is necessary to create an instance of a
050 * <CODE>ByteString</CODE> object, the caller should use one of the
051 * <CODE>ByteStringFactory.create</CODE> methods rather than creating an
052 * <CODE>ASN1OctetString</CODE> object directly. In general, direct references
053 * to ASN.1 elements should be limited to cases in which ASN.1 is actually
054 * involved.
055 */
056 @org.opends.server.types.PublicAPI(
057 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
058 mayInstantiate=true,
059 mayExtend=false,
060 mayInvoke=true)
061 public final class ASN1OctetString
062 extends ASN1Element
063 implements ByteString
064 {
065 /**
066 * The tracer object for the debug logger.
067 */
068 private static final DebugTracer TRACER = getTracer();
069
070
071
072
073 /**
074 * The serial version identifier required to satisfy the compiler because this
075 * class implements the <CODE>java.io.Serializable</CODE> interface. This
076 * value was generated using the <CODE>serialver</CODE> command-line utility
077 * included with the Java SDK.
078 */
079 private static final long serialVersionUID = -6101268916754431502L;
080
081
082
083 // The string value for this element. It may be null due to lazy
084 // initialization.
085 private String stringValue;
086
087
088
089 /**
090 * Creates a new ASN.1 octet string element with the default type and no
091 * value.
092 */
093 public ASN1OctetString()
094 {
095 super(UNIVERSAL_OCTET_STRING_TYPE);
096
097 }
098
099
100
101 /**
102 * Creates a new ASN.1 octet string element with the specified type and no
103 * value.
104 *
105 * @param type The BER type for this ASN.1 octet string element.
106 */
107 public ASN1OctetString(byte type)
108 {
109 super(type);
110
111 }
112
113
114
115 /**
116 * Creates a new ASN.1 octet string element with the default type and the
117 * provided value.
118 *
119 * @param value The value for this ASN.1 octet string element.
120 */
121 public ASN1OctetString(byte[] value)
122 {
123 super(UNIVERSAL_OCTET_STRING_TYPE, value);
124
125
126 this.stringValue = null;
127 }
128
129
130
131 /**
132 * Creates a new ASN.1 octet string element with the default type and the
133 * provided value.
134 *
135 * @param messageValue The value for this ASN.1 octet string element as a
136 * string.
137 */
138 public ASN1OctetString(Message messageValue)
139 {
140 this(messageValue != null ? messageValue.toString() : null);
141 }
142
143
144
145 /**
146 * Creates a new ASN.1 octet string element with the default type and the
147 * provided value.
148 *
149 * @param stringValue The value for this ASN.1 octet string element as a
150 * string.
151 */
152 public ASN1OctetString(String stringValue)
153 {
154 super(UNIVERSAL_OCTET_STRING_TYPE, getBytes(stringValue));
155
156
157 this.stringValue = stringValue;
158 }
159
160
161
162 /**
163 * Creates a new ASN.1 octet string element with the specified type and the
164 * provided value.
165 *
166 * @param type The BER type for this ASN.1 octet string element.
167 * @param value The value for this ASN.1 octet string element.
168 */
169 public ASN1OctetString(byte type, byte[] value)
170 {
171 super(type, value);
172
173
174 this.stringValue = null;
175 }
176
177
178
179 /**
180 * Creates a new ASN.1 octet string element with the specified type and the
181 * provided value.
182 *
183 * @param type The BER type for this ASN.1 octet string element.
184 * @param stringValue The value for this ASN.1 octet string element as a
185 * string.
186 */
187 public ASN1OctetString(byte type, String stringValue)
188 {
189 super(type, getBytes(stringValue));
190
191
192 this.stringValue = stringValue;
193 }
194
195
196
197 /**
198 * Retrieves the string representation of the value for this ASN.1 octet
199 * string element. The behavior of this method when the bytes are not
200 * valid in the UTF-8 charset is unspecified. In particular the behavior for
201 * binary values is unspecified.
202 *
203 * @return The string representation of the value for this ASN.1 octet string
204 * element.
205 */
206 public String stringValue()
207 {
208 if (stringValue == null)
209 {
210 /*
211 // This code could be used to explicitly detect and handle binary values.
212 Charset charset = Charset.forName("UTF-8");
213 CharsetDecoder decoder = charset.newDecoder();
214 ByteBuffer bb = ByteBuffer.wrap(value());
215 try
216 {
217 CharBuffer cb = decoder.decode(bb);
218 stringValue = cb.toString();
219 }
220 catch (CharacterCodingException e)
221 {
222 // Handle binary values here.
223 return "[Binary]";
224 }
225 */
226 try
227 {
228 stringValue = new String(value(), "UTF-8");
229 }
230 catch (Exception e)
231 {
232 if (debugEnabled())
233 {
234 TRACER.debugCaught(DebugLogLevel.ERROR, e);
235 }
236
237 stringValue = new String(value());
238 }
239 }
240
241 return stringValue;
242 }
243
244
245
246 /**
247 * Appends a string representation of the value for this ASN.1 octet string
248 * element to the provided buffer.
249 *
250 * @param buffer The buffer to which the string representation should be
251 * appended.
252 */
253 public void stringValue(StringBuilder buffer)
254 {
255 if (stringValue != null)
256 {
257 buffer.append(stringValue);
258 return;
259 }
260
261 byte[] value = value();
262 int length = value.length;
263
264 for (int i=0; i < length; i++)
265 {
266 if ((value[i] & 0x7F) == value[i])
267 {
268 buffer.append((char) value[i]);
269 }
270 else
271 {
272 String s;
273 try
274 {
275 s = new String(value, i, (length-i), "UTF-8");
276 }
277 catch (Exception e)
278 {
279 if (debugEnabled())
280 {
281 TRACER.debugCaught(DebugLogLevel.ERROR, e);
282 }
283
284 s = new String(value, i, (length - i));
285 }
286
287 buffer.append(s);
288 return;
289 }
290 }
291 }
292
293
294
295 /**
296 * Specifies the string value for this ASN.1 octet string element.
297 *
298 * @param stringValue The string value for this ASN.1 octet string element.
299 */
300 public void setValue(String stringValue)
301 {
302 if (stringValue == null)
303 {
304 this.stringValue = null;
305 setValueInternal(new byte[0]);
306 }
307 else
308 {
309 this.stringValue = stringValue;
310 setValueInternal(getBytes(stringValue));
311 }
312 }
313
314
315
316 /**
317 * Specifies the value for this ASN.1 octet string element.
318 *
319 * @param value The encoded value for this ASN.1 octet string element.
320 */
321 public void setValue(byte[] value)
322 {
323 if (value == null)
324 {
325 setValueInternal(NO_VALUE);
326 }
327 else
328 {
329 setValueInternal(value);
330 }
331
332 stringValue = null;
333 }
334
335
336
337 /**
338 * Decodes the provided ASN.1 element as an octet string element.
339 *
340 * @param element The ASN.1 element to decode as an octet string element.
341 *
342 * @return The decoded ASN.1 octet string element.
343 *
344 * @throws ASN1Exception If the provided ASN.1 element cannot be decoded as
345 * an octet string element.
346 */
347 public static ASN1OctetString decodeAsOctetString(ASN1Element element)
348 throws ASN1Exception
349 {
350 if (element == null)
351 {
352 Message message = ERR_ASN1_OCTET_STRING_DECODE_ELEMENT_NULL.get();
353 throw new ASN1Exception(message);
354 }
355
356 return new ASN1OctetString(element.getType(), element.value());
357 }
358
359
360
361 /**
362 * Decodes the provided byte array as an ASN.1 octet string element.
363 *
364 * @param encodedElement The byte array to decode as an ASN.1 octet string
365 * element.
366 *
367 * @return The decoded ASN.1 octet string element.
368 *
369 * @throws ASN1Exception If the provided byte array cannot be decoded as an
370 * ASN.1 octet string element.
371 */
372 public static ASN1OctetString decodeAsOctetString(byte[] encodedElement)
373 throws ASN1Exception
374 {
375 // First make sure that the array is not null and long enough to contain
376 // a valid ASN.1 element.
377 if (encodedElement == null)
378 {
379 Message message = ERR_ASN1_OCTET_STRING_DECODE_ARRAY_NULL.get();
380 throw new ASN1Exception(message);
381 }
382
383 if (encodedElement.length < 2)
384 {
385 Message message = ERR_ASN1_SHORT_ELEMENT.get(encodedElement.length);
386 throw new ASN1Exception(message);
387 }
388
389
390 // Next, decode the length. This allows multi-byte lengths with up to four
391 // bytes used to indicate how many bytes are in the length.
392 byte type = encodedElement[0];
393 int length = (encodedElement[1] & 0x7F);
394 int valueStartPos = 2;
395 if (length != encodedElement[1])
396 {
397 int numLengthBytes = length;
398 if (numLengthBytes > 4)
399 {
400 Message message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES.get(numLengthBytes);
401 throw new ASN1Exception(message);
402 }
403 else if (encodedElement.length < (2 + numLengthBytes))
404 {
405 Message message = ERR_ASN1_TRUNCATED_LENGTH.get(numLengthBytes);
406 throw new ASN1Exception(message);
407 }
408
409 length = 0x00;
410 valueStartPos = 2 + numLengthBytes;
411 for (int i=0; i < numLengthBytes; i++)
412 {
413 length = (length << 8) | (encodedElement[i+2] & 0xFF);
414 }
415 }
416
417
418 // Make sure that the number of bytes left is equal to the number of bytes
419 // in the value.
420 if ((encodedElement.length - valueStartPos) != length)
421 {
422 Message message = ERR_ASN1_LENGTH_MISMATCH.get(
423 length, (encodedElement.length - valueStartPos));
424 throw new ASN1Exception(message);
425 }
426
427
428 // Copy the value and construct the element to return.
429 byte[] value = new byte[length];
430 System.arraycopy(encodedElement, valueStartPos, value, 0, length);
431 return new ASN1OctetString(type, value);
432 }
433
434
435
436 /**
437 * Creates a duplicate of this ASN.1 octet string.
438 *
439 * @return A duplicate of this ASN.1 octet string.
440 */
441 public ASN1OctetString duplicate()
442 {
443 byte[] value = value();
444 int length = value.length;
445
446 byte[] duplicateValue = new byte[length];
447 System.arraycopy(value, 0, duplicateValue, 0, length);
448
449 return new ASN1OctetString(getType(), value);
450 }
451
452
453
454 /**
455 * Appends a string representation of this ASN.1 octet string element to the
456 * provided buffer.
457 *
458 * @param buffer The buffer to which the information should be appended.
459 */
460 public void toString(StringBuilder buffer)
461 {
462 buffer.append(stringValue());
463 }
464
465
466
467 /**
468 * Appends a string representation of this protocol element to the provided
469 * buffer.
470 *
471 * @param buffer The buffer into which the string representation should be
472 * written.
473 * @param indent The number of spaces that should be used to indent the
474 * resulting string representation.
475 */
476 public void toString(StringBuilder buffer, int indent)
477 {
478 StringBuilder indentBuf = new StringBuilder(indent);
479 for (int i=0 ; i < indent; i++)
480 {
481 indentBuf.append(' ');
482 }
483
484 buffer.append(indentBuf);
485 buffer.append("ASN.1 Octet String");
486 buffer.append(EOL);
487
488 buffer.append(indentBuf);
489 buffer.append(" BER Type: ");
490 buffer.append(byteToHex(getType()));
491 buffer.append(EOL);
492
493 byte[] value = value();
494 buffer.append(indentBuf);
495 buffer.append(" Value (");
496 buffer.append(value.length);
497 buffer.append(" bytes)");
498 buffer.append(EOL);
499
500 byteArrayToHexPlusAscii(buffer, value, indent+2);
501 }
502
503
504
505 /**
506 * Retrieves this byte string as an ASN.1 octet string.
507 *
508 * @return An ASN.1 octet string with the value of this byte string.
509 */
510 public ASN1OctetString toASN1OctetString()
511 {
512 return this;
513 }
514 }
515