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