Showing the changes to java/pkg/sun/security/util/DerInputBuffer.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,433 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package sun.security.util; | ||
| 27 | |||
| 28 | import java.io.ByteArrayInputStream; | ||
| 29 | import java.io.IOException; | ||
| 30 | import java.io.OutputStream; | ||
| 31 | import java.math.BigInteger; | ||
| 32 | import java.util.Date; | ||
| 33 | import sun.util.calendar.CalendarDate; | ||
| 34 | import sun.util.calendar.CalendarSystem; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * DER input buffer ... this is the main abstraction in the DER library | ||
| 38 | * which actively works with the "untyped byte stream" abstraction. It | ||
| 39 | * does so with impunity, since it's not intended to be exposed to | ||
| 40 | * anyone who could violate the "typed value stream" DER model and hence | ||
| 41 | * corrupt the input stream of DER values. | ||
| 42 | * | ||
| 43 | * @author David Brownell | ||
| 44 | */ | ||
| 45 | class DerInputBuffer extends ByteArrayInputStream implements Cloneable { | ||
| 46 | |||
| 47 | DerInputBuffer(byte[] buf) { super(buf); } | ||
| 48 | |||
| 49 | DerInputBuffer(byte[] buf, int offset, int len) { | ||
| 50 | super(buf, offset, len); | ||
| 51 | } | ||
| 52 | |||
| 53 | DerInputBuffer dup() { | ||
| 54 | try { | ||
| 55 | DerInputBuffer retval = (DerInputBuffer)clone(); | ||
| 56 | |||
| 57 | retval.mark(Integer.MAX_VALUE); | ||
| 58 | return retval; | ||
| 59 | } catch (CloneNotSupportedException e) { | ||
| 60 | throw new IllegalArgumentException(e.toString()); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | byte[] toByteArray() { | ||
| 65 | int len = available(); | ||
| 66 | if (len <= 0) | ||
| 67 | return null; | ||
| 68 | byte[] retval = new byte[len]; | ||
| 69 | |||
| 70 | System.arraycopy(buf, pos, retval, 0, len); | ||
| 71 | return retval; | ||
| 72 | } | ||
| 73 | |||
| 74 | int peek() throws IOException { | ||
| 75 | if (pos >= count) | ||
| 76 | throw new IOException("out of data"); | ||
| 77 | else | ||
| 78 | return buf[pos]; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Compares this DerInputBuffer for equality with the specified | ||
| 83 | * object. | ||
| 84 | */ | ||
| 85 | public boolean equals(Object other) { | ||
| 86 | if (other instanceof DerInputBuffer) | ||
| 87 | return equals((DerInputBuffer)other); | ||
| 88 | else | ||
| 89 | return false; | ||
| 90 | } | ||
| 91 | |||
| 92 | boolean equals(DerInputBuffer other) { | ||
| 93 | if (this == other) | ||
| 94 | return true; | ||
| 95 | |||
| 96 | int max = this.available(); | ||
| 97 | if (other.available() != max) | ||
| 98 | return false; | ||
| 99 | for (int i = 0; i < max; i++) { | ||
| 100 | if (this.buf[this.pos + i] != other.buf[other.pos + i]) { | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | return true; | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Returns a hashcode for this DerInputBuffer. | ||
| 109 | * | ||
| 110 | * @return a hashcode for this DerInputBuffer. | ||
| 111 | */ | ||
| 112 | public int hashCode() { | ||
| 113 | int retval = 0; | ||
| 114 | |||
| 115 | int len = available(); | ||
| 116 | int p = pos; | ||
| 117 | |||
| 118 | for (int i = 0; i < len; i++) | ||
| 119 | retval += buf[p + i] * i; | ||
| 120 | return retval; | ||
| 121 | } | ||
| 122 | |||
| 123 | void truncate(int len) throws IOException { | ||
| 124 | if (len > available()) | ||
| 125 | throw new IOException("insufficient data"); | ||
| 126 | count = pos + len; | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Returns the integer which takes up the specified number | ||
| 131 | * of bytes in this buffer as a BigInteger. | ||
| 132 | * @param len the number of bytes to use. | ||
| 133 | * @param makePositive whether to always return a positive value, | ||
| 134 | * irrespective of actual encoding | ||
| 135 | * @return the integer as a BigInteger. | ||
| 136 | */ | ||
| 137 | BigInteger getBigInteger(int len, boolean makePositive) throws IOException { | ||
| 138 | if (len > available()) | ||
| 139 | throw new IOException("short read of integer"); | ||
| 140 | |||
| 141 | if (len == 0) { | ||
| 142 | throw new IOException("Invalid encoding: zero length Int value"); | ||
| 143 | } | ||
| 144 | |||
| 145 | byte[] bytes = new byte[len]; | ||
| 146 | |||
| 147 | System.arraycopy(buf, pos, bytes, 0, len); | ||
| 148 | skip(len); | ||
| 149 | |||
| 150 | if (makePositive) { | ||
| 151 | return new BigInteger(1, bytes); | ||
| 152 | } else { | ||
| 153 | return new BigInteger(bytes); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Returns the integer which takes up the specified number | ||
| 159 | * of bytes in this buffer. | ||
| 160 | * @throws IOException if the result is not within the valid | ||
| 161 | * range for integer, i.e. between Integer.MIN_VALUE and | ||
| 162 | * Integer.MAX_VALUE. | ||
| 163 | * @param len the number of bytes to use. | ||
| 164 | * @return the integer. | ||
| 165 | */ | ||
| 166 | public int getInteger(int len) throws IOException { | ||
| 167 | |||
| 168 | BigInteger result = getBigInteger(len, false); | ||
| 169 | if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) { | ||
| 170 | throw new IOException("Integer below minimum valid value"); | ||
| 171 | } | ||
| 172 | if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { | ||
| 173 | throw new IOException("Integer exceeds maximum valid value"); | ||
| 174 | } | ||
| 175 | return result.intValue(); | ||
| 176 | } | ||
| 177 | |||
| 178 | /** | ||
| 179 | * Returns the bit string which takes up the specified | ||
| 180 | * number of bytes in this buffer. | ||
| 181 | */ | ||
| 182 | public byte[] getBitString(int len) throws IOException { | ||
| 183 | if (len > available()) | ||
| 184 | throw new IOException("short read of bit string"); | ||
| 185 | |||
| 186 | if (len == 0) { | ||
| 187 | throw new IOException("Invalid encoding: zero length bit string"); | ||
| 188 | } | ||
| 189 | |||
| 190 | int numOfPadBits = buf[pos]; | ||
| 191 | if ((numOfPadBits < 0) || (numOfPadBits > 7)) { | ||
| 192 | throw new IOException("Invalid number of padding bits"); | ||
| 193 | } | ||
| 194 | // minus the first byte which indicates the number of padding bits | ||
| 195 | byte[] retval = new byte[len - 1]; | ||
| 196 | System.arraycopy(buf, pos + 1, retval, 0, len - 1); | ||
| 197 | if (numOfPadBits != 0) { | ||
| 198 | // get rid of the padding bits | ||
| 199 | retval[len - 2] &= (0xff << numOfPadBits); | ||
| 200 | } | ||
| 201 | skip(len); | ||
| 202 | return retval; | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * Returns the bit string which takes up the rest of this buffer. | ||
| 207 | */ | ||
| 208 | byte[] getBitString() throws IOException { | ||
| 209 | return getBitString(available()); | ||
| 210 | } | ||
| 211 | |||
| 212 | /** | ||
| 213 | * Returns the bit string which takes up the rest of this buffer. | ||
| 214 | * The bit string need not be byte-aligned. | ||
| 215 | */ | ||
| 216 | BitArray getUnalignedBitString() throws IOException { | ||
| 217 | if (pos >= count) | ||
| 218 | return null; | ||
| 219 | /* | ||
| 220 | * Just copy the data into an aligned, padded octet buffer, | ||
| 221 | * and consume the rest of the buffer. | ||
| 222 | */ | ||
| 223 | int len = available(); | ||
| 224 | int unusedBits = buf[pos] & 0xff; | ||
| 225 | if (unusedBits > 7 ) { | ||
| 226 | throw new IOException("Invalid value for unused bits: " + unusedBits); | ||
| 227 | } | ||
| 228 | byte[] bits = new byte[len - 1]; | ||
| 229 | // number of valid bits | ||
| 230 | int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits; | ||
| 231 | |||
| 232 | System.arraycopy(buf, pos + 1, bits, 0, len - 1); | ||
| 233 | |||
| 234 | BitArray bitArray = new BitArray(length, bits); | ||
| 235 | pos = count; | ||
| 236 | return bitArray; | ||
| 237 | } | ||
| 238 | |||
| 239 | /** | ||
| 240 | * Returns the UTC Time value that takes up the specified number | ||
| 241 | * of bytes in this buffer. | ||
| 242 | * @param len the number of bytes to use | ||
| 243 | */ | ||
| 244 | public Date getUTCTime(int len) throws IOException { | ||
| 245 | if (len > available()) | ||
| 246 | throw new IOException("short read of DER UTC Time"); | ||
| 247 | |||
| 248 | if (len < 11 || len > 17) | ||
| 249 | throw new IOException("DER UTC Time length error"); | ||
| 250 | |||
| 251 | return getTime(len, false); | ||
| 252 | } | ||
| 253 | |||
| 254 | /** | ||
| 255 | * Returns the Generalized Time value that takes up the specified | ||
| 256 | * number of bytes in this buffer. | ||
| 257 | * @param len the number of bytes to use | ||
| 258 | */ | ||
| 259 | public Date getGeneralizedTime(int len) throws IOException { | ||
| 260 | if (len > available()) | ||
| 261 | throw new IOException("short read of DER Generalized Time"); | ||
| 262 | |||
| 263 | if (len < 13 || len > 23) | ||
| 264 | throw new IOException("DER Generalized Time length error"); | ||
| 265 | |||
| 266 | return getTime(len, true); | ||
| 267 | |||
| 268 | } | ||
| 269 | |||
| 270 | /** | ||
| 271 | * Private helper routine to extract time from the der value. | ||
| 272 | * @param len the number of bytes to use | ||
| 273 | * @param generalized true if Generalized Time is to be read, false | ||
| 274 | * if UTC Time is to be read. | ||
| 275 | */ | ||
| 276 | private Date getTime(int len, boolean generalized) throws IOException { | ||
| 277 | |||
| 278 | /* | ||
| 279 | * UTC time encoded as ASCII chars: | ||
| 280 | * YYMMDDhhmmZ | ||
| 281 | * YYMMDDhhmmssZ | ||
| 282 | * YYMMDDhhmm+hhmm | ||
| 283 | * YYMMDDhhmm-hhmm | ||
| 284 | * YYMMDDhhmmss+hhmm | ||
| 285 | * YYMMDDhhmmss-hhmm | ||
| 286 | * UTC Time is broken in storing only two digits of year. | ||
| 287 | * If YY < 50, we assume 20YY; | ||
| 288 | * if YY >= 50, we assume 19YY, as per RFC 3280. | ||
| 289 | * | ||
| 290 | * Generalized time has a four-digit year and allows any | ||
| 291 | * precision specified in ISO 8601. However, for our purposes, | ||
| 292 | * we will only allow the same format as UTC time, except that | ||
| 293 | * fractional seconds (millisecond precision) are supported. | ||
| 294 | */ | ||
| 295 | |||
| 296 | int year, month, day, hour, minute, second, millis; | ||
| 297 | String type = null; | ||
| 298 | |||
| 299 | if (generalized) { | ||
| 300 | type = "Generalized"; | ||
| 301 | year = 1000 * Character.digit((char)buf[pos++], 10); | ||
| 302 | year += 100 * Character.digit((char)buf[pos++], 10); | ||
| 303 | year += 10 * Character.digit((char)buf[pos++], 10); | ||
| 304 | year += Character.digit((char)buf[pos++], 10); | ||
| 305 | len -= 2; // For the two extra YY | ||
| 306 | } else { | ||
| 307 | type = "UTC"; | ||
| 308 | year = 10 * Character.digit((char)buf[pos++], 10); | ||
| 309 | year += Character.digit((char)buf[pos++], 10); | ||
| 310 | |||
| 311 | if (year < 50) // origin 2000 | ||
| 312 | year += 2000; | ||
| 313 | else | ||
| 314 | year += 1900; // origin 1900 | ||
| 315 | } | ||
| 316 | |||
| 317 | month = 10 * Character.digit((char)buf[pos++], 10); | ||
| 318 | month += Character.digit((char)buf[pos++], 10); | ||
| 319 | |||
| 320 | day = 10 * Character.digit((char)buf[pos++], 10); | ||
| 321 | day += Character.digit((char)buf[pos++], 10); | ||
| 322 | |||
| 323 | hour = 10 * Character.digit((char)buf[pos++], 10); | ||
| 324 | hour += Character.digit((char)buf[pos++], 10); | ||
| 325 | |||
| 326 | minute = 10 * Character.digit((char)buf[pos++], 10); | ||
| 327 | minute += Character.digit((char)buf[pos++], 10); | ||
| 328 | |||
| 329 | len -= 10; // YYMMDDhhmm | ||
| 330 | |||
| 331 | /* | ||
| 332 | * We allow for non-encoded seconds, even though the | ||
| 333 | * IETF-PKIX specification says that the seconds should | ||
| 334 | * always be encoded even if it is zero. | ||
| 335 | */ | ||
| 336 | |||
| 337 | millis = 0; | ||
| 338 | if (len > 2 && len < 12) { | ||
| 339 | second = 10 * Character.digit((char)buf[pos++], 10); | ||
| 340 | second += Character.digit((char)buf[pos++], 10); | ||
| 341 | len -= 2; | ||
| 342 | // handle fractional seconds (if present) | ||
| 343 | if (buf[pos] == '.' || buf[pos] == ',') { | ||
| 344 | len --; | ||
| 345 | pos++; | ||
| 346 | // handle upto milisecond precision only | ||
| 347 | int precision = 0; | ||
| 348 | int peek = pos; | ||
| 349 | while (buf[peek] != 'Z' && | ||
| 350 | buf[peek] != '+' && | ||
| 351 | buf[peek] != '-') { | ||
| 352 | peek++; | ||
| 353 | precision++; | ||
| 354 | } | ||
| 355 | switch (precision) { | ||
| 356 | case 3: | ||
| 357 | millis += 100 * Character.digit((char)buf[pos++], 10); | ||
| 358 | millis += 10 * Character.digit((char)buf[pos++], 10); | ||
| 359 | millis += Character.digit((char)buf[pos++], 10); | ||
| 360 | break; | ||
| 361 | case 2: | ||
| 362 | millis += 100 * Character.digit((char)buf[pos++], 10); | ||
| 363 | millis += 10 * Character.digit((char)buf[pos++], 10); | ||
| 364 | break; | ||
| 365 | case 1: | ||
| 366 | millis += 100 * Character.digit((char)buf[pos++], 10); | ||
| 367 | break; | ||
| 368 | default: | ||
| 369 | throw new IOException("Parse " + type + | ||
| 370 | " time, unsupported precision for seconds value"); | ||
| 371 | } | ||
| 372 | len -= precision; | ||
| 373 | } | ||
| 374 | } else | ||
| 375 | second = 0; | ||
| 376 | |||
| 377 | if (month == 0 || day == 0 | ||
| 378 | || month > 12 || day > 31 | ||
| 379 | || hour >= 24 || minute >= 60 || second >= 60) | ||
| 380 | throw new IOException("Parse " + type + " time, invalid format"); | ||
| 381 | |||
| 382 | /* | ||
| 383 | * Generalized time can theoretically allow any precision, | ||
| 384 | * but we're not supporting that. | ||
| 385 | */ | ||
| 386 | CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); | ||
| 387 | CalendarDate date = gcal.newCalendarDate(null); // no time zone | ||
| 388 | date.setDate(year, month, day); | ||
| 389 | date.setTimeOfDay(hour, minute, second, millis); | ||
| 390 | long time = gcal.getTime(date); | ||
| 391 | |||
| 392 | /* | ||
| 393 | * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm | ||
| 394 | */ | ||
| 395 | if (! (len == 1 || len == 5)) | ||
| 396 | throw new IOException("Parse " + type + " time, invalid offset"); | ||
| 397 | |||
| 398 | int hr, min; | ||
| 399 | |||
| 400 | switch (buf[pos++]) { | ||
| 401 | case '+': | ||
| 402 | hr = 10 * Character.digit((char)buf[pos++], 10); | ||
| 403 | hr += Character.digit((char)buf[pos++], 10); | ||
| 404 | min = 10 * Character.digit((char)buf[pos++], 10); | ||
| 405 | min += Character.digit((char)buf[pos++], 10); | ||
| 406 | |||
| 407 | if (hr >= 24 || min >= 60) | ||
| 408 | throw new IOException("Parse " + type + " time, +hhmm"); | ||
| 409 | |||
| 410 | time -= ((hr * 60) + min) * 60 * 1000; | ||
| 411 | break; | ||
| 412 | |||
| 413 | case '-': | ||
| 414 | hr = 10 * Character.digit((char)buf[pos++], 10); | ||
| 415 | hr += Character.digit((char)buf[pos++], 10); | ||
| 416 | min = 10 * Character.digit((char)buf[pos++], 10); | ||
| 417 | min += Character.digit((char)buf[pos++], 10); | ||
| 418 | |||
| 419 | if (hr >= 24 || min >= 60) | ||
| 420 | throw new IOException("Parse " + type + " time, -hhmm"); | ||
| 421 | |||
| 422 | time += ((hr * 60) + min) * 60 * 1000; | ||
| 423 | break; | ||
| 424 | |||
| 425 | case 'Z': | ||
| 426 | break; | ||
| 427 | |||
| 428 | default: | ||
| 429 | throw new IOException("Parse " + type + " time, garbage offset"); | ||
| 430 | } | ||
| 431 | return new Date(time); | ||
| 432 | } | ||
| 433 | } | ||