1 module hunt.security.util.DerOutputStream; 2 3 import hunt.stream.ByteArrayOutputStream; 4 import hunt.security.util.DerEncoder; 5 import hunt.security.util.DerValue; 6 7 import hunt.stream.Common; 8 import hunt.Exceptions; 9 10 import std.datetime; 11 import std.bitmanip; 12 13 /** 14 * Output stream marshaling DER-encoded data. This is eventually provided 15 * in the form of a byte array; there is no advance limit on the size of 16 * that byte array. 17 * 18 * <P>At this time, this class supports only a subset of the types of 19 * DER data encodings which are defined. That subset is sufficient for 20 * generating most X.509 certificates. 21 * 22 * 23 * @author David Brownell 24 * @author Amit Kapoor 25 * @author Hemma Prafullchandra 26 */ 27 class DerOutputStream : ByteArrayOutputStream, DerEncoder { 28 /** 29 * Construct an DER output stream. 30 * 31 * @param size how large a buffer to preallocate. 32 */ 33 this(int size) { super(size); } 34 35 /** 36 * Construct an DER output stream. 37 */ 38 this() { } 39 40 alias write = ByteArrayOutputStream.write; 41 42 /** 43 * Writes tagged, pre-marshaled data. This calcuates and encodes 44 * the length, so that the output data is the standard triple of 45 * { tag, length, data } used by all DER values. 46 * 47 * @param tag the DER value tag for the data, such as 48 * <em>DerValue.tag_Sequence</em> 49 * @param buf buffered data, which must be DER-encoded 50 */ 51 void write(byte tag, byte[] buf) { 52 write(tag); 53 putLength(cast(int)buf.length); 54 write(buf, 0, cast(int)buf.length); 55 } 56 57 /** 58 * Writes tagged data using buffer-to-buffer copy. As above, 59 * this writes a standard DER record. This is often used when 60 * efficiently encapsulating values in sequences. 61 * 62 * @param tag the DER value tag for the data, such as 63 * <em>DerValue.tag_Sequence</em> 64 * @param ot buffered data 65 */ 66 void write(byte tag, DerOutputStream ot) { 67 write(tag); 68 putLength(ot.count); 69 write(ot.buf, 0, ot.count); 70 } 71 72 /** 73 * Writes implicitly tagged data using buffer-to-buffer copy. As above, 74 * this writes a standard DER record. This is often used when 75 * efficiently encapsulating implicitly tagged values. 76 * 77 * @param tag the DER value of the context-specific tag that replaces 78 * original tag of the value in the output, such as in 79 * <pre> 80 * <em> <field> [N] IMPLICIT <type></em> 81 * </pre> 82 * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, with value=4; 83 * would be encoded as "81 01 04" whereas in explicit 84 * tagging it would be encoded as "A1 03 02 01 04". 85 * Notice that the tag is A1 and not 81, this is because with 86 * explicit tagging the form is always constructed. 87 * @param value original value being implicitly tagged 88 */ 89 void writeImplicit(byte tag, DerOutputStream value) 90 { 91 write(tag); 92 write(value.buf, 1, value.count-1); 93 } 94 95 /** 96 * Marshals pre-encoded DER value onto the output stream. 97 */ 98 void putDerValue(DerValue val) { 99 // val.encode(this); 100 implementationMissing(); 101 } 102 103 /* 104 * PRIMITIVES -- these are "universal" ASN.1 simple types. 105 * 106 * BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL 107 * OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF) 108 * PrintableString, T61String, IA5String, UTCTime 109 */ 110 111 /** 112 * Marshals a DER bool on the output stream. 113 */ 114 void putBoolean(bool val) { 115 write(DerValue.tag_Boolean); 116 putLength(1); 117 if (val) { 118 write(0xff); 119 } else { 120 write(0); 121 } 122 } 123 124 /** 125 * Marshals a DER enumerated on the output stream. 126 * @param i the enumerated value. 127 */ 128 void putEnumerated(int i) { 129 write(DerValue.tag_Enumerated); 130 putIntegerContents(i); 131 } 132 133 /** 134 * Marshals a DER integer on the output stream. 135 * 136 * @param i the integer in the form of a BigInt. 137 */ 138 // void putInteger(BigInt i) { 139 // write(DerValue.tag_Integer); 140 // byte[] buf = i.toByteArray(); // least number of bytes 141 // putLength(buf.length); 142 // write(buf, 0, buf.length); 143 // } 144 145 /** 146 * Marshals a DER integer on the output stream. 147 * @param i the integer in the form of an Integer. 148 */ 149 // void putInteger(Integer i) { 150 // putInteger(i.intValue()); 151 // } 152 153 /** 154 * Marshals a DER integer on the output stream. 155 * @param i the integer. 156 */ 157 void putInteger(int i) { 158 write(DerValue.tag_Integer); 159 putIntegerContents(i); 160 } 161 162 private void putIntegerContents(int i) { 163 164 byte[] bytes = new byte[4]; 165 int start = 0; 166 167 // Obtain the four bytes of the int 168 169 bytes[3] = cast(byte) (i & 0xff); 170 bytes[2] = cast(byte)((i & 0xff00) >>> 8); 171 bytes[1] = cast(byte)((i & 0xff0000) >>> 16); 172 bytes[0] = cast(byte)((i & 0xff000000) >>> 24); 173 174 // Reduce them to the least number of bytes needed to 175 // represent this int 176 177 if (bytes[0] == cast(byte)0xff) { 178 179 // Eliminate redundant 0xff 180 181 for (int j = 0; j < 3; j++) { 182 if ((bytes[j] == cast(byte)0xff) && 183 ((bytes[j+1] & 0x80) == 0x80)) 184 start++; 185 else 186 break; 187 } 188 } else if (bytes[0] == 0x00) { 189 190 // Eliminate redundant 0x00 191 192 for (int j = 0; j < 3; j++) { 193 if ((bytes[j] == 0x00) && 194 ((bytes[j+1] & 0x80) == 0)) 195 start++; 196 else 197 break; 198 } 199 } 200 201 putLength(4 - start); 202 for (int k = start; k < 4; k++) 203 write(bytes[k]); 204 } 205 206 /** 207 * Marshals a DER bit string on the output stream. The bit 208 * string must be byte-aligned. 209 * 210 * @param bits the bit string, MSB first 211 */ 212 void putBitString(byte[] bits) { 213 write(DerValue.tag_BitString); 214 putLength(cast(int)bits.length + 1); 215 write(0); // all of last octet is used 216 write(bits); 217 } 218 219 /** 220 * Marshals a DER bit string on the output stream. 221 * The bit strings need not be byte-aligned. 222 * 223 * @param bits the bit string, MSB first 224 */ 225 void putUnalignedBitString(BitArray ba) { 226 implementationMissing(); 227 // byte[] bits = ba.toByteArray(); 228 229 // write(DerValue.tag_BitString); 230 // putLength(bits.length + 1); 231 // write(bits.length*8 - ba.length()); // excess bits in last octet 232 // write(bits); 233 } 234 235 /** 236 * Marshals a truncated DER bit string on the output stream. 237 * The bit strings need not be byte-aligned. 238 * 239 * @param bits the bit string, MSB first 240 */ 241 // void putTruncatedUnalignedBitString(BitArray ba) { 242 // putUnalignedBitString(ba.truncate()); 243 // } 244 245 /** 246 * DER-encodes an ASN.1 OCTET STRING value on the output stream. 247 * 248 * @param octets the octet string 249 */ 250 void putOctetString(byte[] octets) { 251 write(DerValue.tag_OctetString, octets); 252 } 253 254 /** 255 * Marshals a DER "null" value on the output stream. These are 256 * often used to indicate optional values which have been omitted. 257 */ 258 void putNull() { 259 write(DerValue.tag_Null); 260 putLength(0); 261 } 262 263 /** 264 * Marshals an object identifier (OID) on the output stream. 265 * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct. 266 */ 267 // void putOID(ObjectIdentifier oid) { 268 // oid.encode(this); 269 // } 270 271 /** 272 * Marshals a sequence on the output stream. This supports both 273 * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF" 274 * (one to N values) constructs. 275 */ 276 // void putSequence(DerValue[] seq) { 277 // DerOutputStream bytes = new DerOutputStream(); 278 // int i; 279 280 // for (i = 0; i < seq.length; i++) 281 // seq[i].encode(bytes); 282 283 // write(DerValue.tag_Sequence, bytes); 284 // } 285 286 /** 287 * Marshals the contents of a set on the output stream without 288 * ordering the elements. Ok for BER encoding, but not for DER 289 * encoding. 290 * 291 * For DER encoding, use orderedPutSet() or orderedPutSetOf(). 292 */ 293 // void putSet(DerValue[] set) { 294 // DerOutputStream bytes = new DerOutputStream(); 295 // int i; 296 297 // for (i = 0; i < set.length; i++) 298 // set[i].encode(bytes); 299 300 // write(DerValue.tag_Set, bytes); 301 // } 302 303 /** 304 * Marshals the contents of a set on the output stream. Sets 305 * are semantically unordered, but DER requires that encodings of 306 * set elements be sorted into ascending lexicographical order 307 * before being output. Hence sets with the same tags and 308 * elements have the same DER encoding. 309 * 310 * This method supports the ASN.1 "SET OF" construct, but not 311 * "SET", which uses a different order. 312 */ 313 // void putOrderedSetOf(byte tag, DerEncoder[] set) { 314 // putOrderedSet(tag, set, lexOrder); 315 // } 316 317 /** 318 * Marshals the contents of a set on the output stream. Sets 319 * are semantically unordered, but DER requires that encodings of 320 * set elements be sorted into ascending tag order 321 * before being output. Hence sets with the same tags and 322 * elements have the same DER encoding. 323 * 324 * This method supports the ASN.1 "SET" construct, but not 325 * "SET OF", which uses a different order. 326 */ 327 // void putOrderedSet(byte tag, DerEncoder[] set) { 328 // putOrderedSet(tag, set, tagOrder); 329 // } 330 331 /** 332 * Lexicographical order comparison on byte arrays, for ordering 333 * elements of a SET OF objects in DER encoding. 334 */ 335 // private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder(); 336 337 /** 338 * Tag order comparison on byte arrays, for ordering elements of 339 * SET objects in DER encoding. 340 */ 341 // private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder(); 342 343 /** 344 * Marshals a the contents of a set on the output stream with the 345 * encodings of its sorted in increasing order. 346 * 347 * @param order the order to use when sorting encodings of components. 348 */ 349 // private void putOrderedSet(byte tag, DerEncoder[] set, 350 // Comparator<byte[]> order) { 351 // DerOutputStream[] streams = new DerOutputStream[set.length]; 352 353 // for (int i = 0; i < set.length; i++) { 354 // streams[i] = new DerOutputStream(); 355 // set[i].derEncode(streams[i]); 356 // } 357 358 // // order the element encodings 359 // byte[][] bufs = new byte[streams.length][]; 360 // for (int i = 0; i < streams.length; i++) { 361 // bufs[i] = streams[i].toByteArray(); 362 // } 363 // Arrays.<byte[]>sort(bufs, order); 364 365 // DerOutputStream bytes = new DerOutputStream(); 366 // for (int i = 0; i < streams.length; i++) { 367 // bytes.write(bufs[i]); 368 // } 369 // write(tag, bytes); 370 371 // } 372 373 /** 374 * Marshals a string as a DER encoded UTF8String. 375 */ 376 void putUTF8String(string s) { 377 writeString(s, DerValue.tag_UTF8String, "UTF8"); 378 } 379 380 /** 381 * Marshals a string as a DER encoded PrintableString. 382 */ 383 void putPrintableString(string s) { 384 writeString(s, DerValue.tag_PrintableString, "ASCII"); 385 } 386 387 /** 388 * Marshals a string as a DER encoded T61String. 389 */ 390 void putT61String(string s) { 391 /* 392 * Works for characters that are defined in both ASCII and 393 * T61. 394 */ 395 writeString(s, DerValue.tag_T61String, "ISO-8859-1"); 396 } 397 398 /** 399 * Marshals a string as a DER encoded IA5String. 400 */ 401 void putIA5String(string s) { 402 writeString(s, DerValue.tag_IA5String, "ASCII"); 403 } 404 405 /** 406 * Marshals a string as a DER encoded BMPString. 407 */ 408 void putBMPString(string s) { 409 writeString(s, DerValue.tag_BMPString, "UnicodeBigUnmarked"); 410 } 411 412 /** 413 * Marshals a string as a DER encoded GeneralString. 414 */ 415 void putGeneralString(string s) { 416 writeString(s, DerValue.tag_GeneralString, "ASCII"); 417 } 418 419 /** 420 * Private helper routine for writing DER encoded string values. 421 * @param s the string to write 422 * @param stringTag one of the DER string tags that indicate which 423 * encoding should be used to write the string out. 424 * @param enc the name of the encoder that should be used corresponding 425 * to the above tag. 426 */ 427 private void writeString(string s, byte stringTag, string enc) 428 { 429 implementationMissing(); 430 // byte[] data = s.getBytes(enc); 431 // write(stringTag); 432 // putLength(data.length); 433 // write(data); 434 } 435 436 /** 437 * Marshals a DER UTC time/date value. 438 * 439 * <P>YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time 440 * and with seconds (even if seconds=0) as per RFC 3280. 441 */ 442 void putUTCTime(Date d) { 443 putTime(d, DerValue.tag_UtcTime); 444 } 445 446 /** 447 * Marshals a DER Generalized Time/date value. 448 * 449 * <P>YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time 450 * and with seconds (even if seconds=0) as per RFC 3280. 451 */ 452 void putGeneralizedTime(Date d) { 453 putTime(d, DerValue.tag_GeneralizedTime); 454 } 455 456 /** 457 * Private helper routine for marshalling a DER UTC/Generalized 458 * time/date value. If the tag specified is not that for UTC Time 459 * then it defaults to Generalized Time. 460 * @param d the date to be marshalled 461 * @param tag the tag for UTC Time or Generalized Time 462 */ 463 private void putTime(Date d, byte tag) { 464 implementationMissing(); 465 466 /* 467 * Format the date. 468 */ 469 470 // TimeZone tz = TimeZone.getTimeZone("GMT"); 471 // string pattern = null; 472 473 // if (tag == DerValue.tag_UtcTime) { 474 // pattern = "yyMMddHHmmss'Z'"; 475 // } else { 476 // tag = DerValue.tag_GeneralizedTime; 477 // pattern = "yyyyMMddHHmmss'Z'"; 478 // } 479 480 // SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.US); 481 // sdf.setTimeZone(tz); 482 // byte[] time = (sdf.format(d)).getBytes("ISO-8859-1"); 483 484 // /* 485 // * Write the formatted date. 486 // */ 487 488 // write(tag); 489 // putLength(cast(int)time.length); 490 // write(time); 491 } 492 493 /** 494 * Put the encoding of the length in the stream. 495 * 496 * @params len the length of the attribute. 497 * @exception IOException on writing errors. 498 */ 499 void putLength(int len) { 500 if (len < 128) { 501 write(cast(byte)len); 502 503 } else if (len < (1 << 8)) { 504 write(cast(byte)0x081); 505 write(cast(byte)len); 506 507 } else if (len < (1 << 16)) { 508 write(cast(byte)0x082); 509 write(cast(byte)(len >> 8)); 510 write(cast(byte)len); 511 512 } else if (len < (1 << 24)) { 513 write(cast(byte)0x083); 514 write(cast(byte)(len >> 16)); 515 write(cast(byte)(len >> 8)); 516 write(cast(byte)len); 517 518 } else { 519 write(cast(byte)0x084); 520 write(cast(byte)(len >> 24)); 521 write(cast(byte)(len >> 16)); 522 write(cast(byte)(len >> 8)); 523 write(cast(byte)len); 524 } 525 } 526 527 /** 528 * Put the tag of the attribute in the stream. 529 * 530 * @params class the tag class type, one of UNIVERSAL, CONTEXT, 531 * APPLICATION or PRIVATE 532 * @params form if true, the value is constructed, otherwise it is 533 * primitive. 534 * @params val the tag value 535 */ 536 void putTag(byte tagClass, bool form, byte val) { 537 byte tag = cast(byte)(tagClass | val); 538 if (form) { 539 tag |= cast(byte)0x20; 540 } 541 write(tag); 542 } 543 544 /** 545 * Write the current contents of this <code>DerOutputStream</code> 546 * to an <code>OutputStream</code>. 547 * 548 * @exception IOException on output error. 549 */ 550 void derEncode(OutputStream ot) { 551 ot.write(toByteArray()); 552 } 553 }