1 /* 2 * Copyright (c) 1998, 2018, 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.provider; 27 28 import java.io.*; 29 import java.security.PublicKey; 30 import java.util.*; 31 import java.security.cert.*; 32 33 import jdk.internal.event.EventHelper; 34 import jdk.internal.event.X509CertificateEvent; 35 import sun.security.util.KeyUtil; 36 import sun.security.util.Pem; 37 import sun.security.x509.*; 38 import sun.security.pkcs.PKCS7; 39 import sun.security.provider.certpath.X509CertPath; 40 import sun.security.provider.certpath.X509CertificatePair; 41 import sun.security.util.DerValue; 42 import sun.security.util.Cache; 43 import java.util.Base64; 44 import sun.security.pkcs.ParsingException; 45 46 /** 47 * This class defines a certificate factory for X.509 v3 certificates {@literal &} 48 * certification paths, and X.509 v2 certificate revocation lists (CRLs). 49 * 50 * @author Jan Luehe 51 * @author Hemma Prafullchandra 52 * @author Sean Mullan 53 * 54 * 55 * @see java.security.cert.CertificateFactorySpi 56 * @see java.security.cert.Certificate 57 * @see java.security.cert.CertPath 58 * @see java.security.cert.CRL 59 * @see java.security.cert.X509Certificate 60 * @see java.security.cert.X509CRL 61 * @see sun.security.x509.X509CertImpl 62 * @see sun.security.x509.X509CRLImpl 63 */ 64 65 public class X509Factory extends CertificateFactorySpi { 66 67 public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; 68 public static final String END_CERT = "-----END CERTIFICATE-----"; 69 70 private static final int ENC_MAX_LENGTH = 4096 * 1024; // 4 MB MAX 71 72 private static final Cache<Object, X509CertImpl> certCache 73 = Cache.newSoftMemoryCache(750); 74 private static final Cache<Object, X509CRLImpl> crlCache 75 = Cache.newSoftMemoryCache(750); 76 77 /** 78 * Generates an X.509 certificate object and initializes it with 79 * the data read from the input stream <code>is</code>. 80 * 81 * @param is an input stream with the certificate data. 82 * 83 * @return an X.509 certificate object initialized with the data 84 * from the input stream. 85 * 86 * @exception CertificateException on parsing errors. 87 */ 88 @Override engineGenerateCertificate(InputStream is)89 public Certificate engineGenerateCertificate(InputStream is) 90 throws CertificateException 91 { 92 if (is == null) { 93 // clear the caches (for debugging) 94 certCache.clear(); 95 X509CertificatePair.clearCache(); 96 throw new CertificateException("Missing input stream"); 97 } 98 try { 99 byte[] encoding = readOneBlock(is); 100 if (encoding != null) { 101 X509CertImpl cert = getFromCache(certCache, encoding); 102 if (cert != null) { 103 return cert; 104 } 105 cert = new X509CertImpl(encoding); 106 addToCache(certCache, cert.getEncodedInternal(), cert); 107 // record cert details if necessary 108 commitEvent(cert); 109 return cert; 110 } else { 111 throw new IOException("Empty input"); 112 } 113 } catch (IOException ioe) { 114 throw new CertificateException("Could not parse certificate: " + 115 ioe.toString(), ioe); 116 } 117 } 118 119 /** 120 * Read from the stream until length bytes have been read or EOF has 121 * been reached. Return the number of bytes actually read. 122 */ readFully(InputStream in, ByteArrayOutputStream bout, int length)123 private static int readFully(InputStream in, ByteArrayOutputStream bout, 124 int length) throws IOException { 125 int read = 0; 126 byte[] buffer = new byte[2048]; 127 while (length > 0) { 128 int n = in.read(buffer, 0, length<2048?length:2048); 129 if (n <= 0) { 130 break; 131 } 132 bout.write(buffer, 0, n); 133 read += n; 134 length -= n; 135 } 136 return read; 137 } 138 139 /** 140 * Return an interned X509CertImpl for the given certificate. 141 * If the given X509Certificate or X509CertImpl is already present 142 * in the cert cache, the cached object is returned. Otherwise, 143 * if it is a X509Certificate, it is first converted to a X509CertImpl. 144 * Then the X509CertImpl is added to the cache and returned. 145 * 146 * Note that all certificates created via generateCertificate(InputStream) 147 * are already interned and this method does not need to be called. 148 * It is useful for certificates that cannot be created via 149 * generateCertificate() and for converting other X509Certificate 150 * implementations to an X509CertImpl. 151 * 152 * @param c The source X509Certificate 153 * @return An X509CertImpl object that is either a cached certificate or a 154 * newly built X509CertImpl from the provided X509Certificate 155 * @throws CertificateException if failures occur while obtaining the DER 156 * encoding for certificate data. 157 */ intern(X509Certificate c)158 public static synchronized X509CertImpl intern(X509Certificate c) 159 throws CertificateException { 160 if (c == null) { 161 return null; 162 } 163 boolean isImpl = c instanceof X509CertImpl; 164 byte[] encoding; 165 if (isImpl) { 166 encoding = ((X509CertImpl)c).getEncodedInternal(); 167 } else { 168 encoding = c.getEncoded(); 169 } 170 X509CertImpl newC = getFromCache(certCache, encoding); 171 if (newC != null) { 172 return newC; 173 } 174 if (isImpl) { 175 newC = (X509CertImpl)c; 176 } else { 177 newC = new X509CertImpl(encoding); 178 encoding = newC.getEncodedInternal(); 179 } 180 addToCache(certCache, encoding, newC); 181 return newC; 182 } 183 184 /** 185 * Return an interned X509CRLImpl for the given certificate. 186 * For more information, see intern(X509Certificate). 187 * 188 * @param c The source X509CRL 189 * @return An X509CRLImpl object that is either a cached CRL or a 190 * newly built X509CRLImpl from the provided X509CRL 191 * @throws CRLException if failures occur while obtaining the DER 192 * encoding for CRL data. 193 */ intern(X509CRL c)194 public static synchronized X509CRLImpl intern(X509CRL c) 195 throws CRLException { 196 if (c == null) { 197 return null; 198 } 199 boolean isImpl = c instanceof X509CRLImpl; 200 byte[] encoding; 201 if (isImpl) { 202 encoding = ((X509CRLImpl)c).getEncodedInternal(); 203 } else { 204 encoding = c.getEncoded(); 205 } 206 X509CRLImpl newC = getFromCache(crlCache, encoding); 207 if (newC != null) { 208 return newC; 209 } 210 if (isImpl) { 211 newC = (X509CRLImpl)c; 212 } else { 213 newC = new X509CRLImpl(encoding); 214 encoding = newC.getEncodedInternal(); 215 } 216 addToCache(crlCache, encoding, newC); 217 return newC; 218 } 219 220 /** 221 * Get the X509CertImpl or X509CRLImpl from the cache. 222 */ getFromCache(Cache<K,V> cache, byte[] encoding)223 private static synchronized <K,V> V getFromCache(Cache<K,V> cache, 224 byte[] encoding) { 225 Object key = new Cache.EqualByteArray(encoding); 226 return cache.get(key); 227 } 228 229 /** 230 * Add the X509CertImpl or X509CRLImpl to the cache. 231 */ addToCache(Cache<Object, V> cache, byte[] encoding, V value)232 private static synchronized <V> void addToCache(Cache<Object, V> cache, 233 byte[] encoding, V value) { 234 if (encoding.length > ENC_MAX_LENGTH) { 235 return; 236 } 237 Object key = new Cache.EqualByteArray(encoding); 238 cache.put(key, value); 239 } 240 241 /** 242 * Generates a <code>CertPath</code> object and initializes it with 243 * the data read from the <code>InputStream</code> inStream. The data 244 * is assumed to be in the default encoding. 245 * 246 * @param inStream an <code>InputStream</code> containing the data 247 * @return a <code>CertPath</code> initialized with the data from the 248 * <code>InputStream</code> 249 * @exception CertificateException if an exception occurs while decoding 250 * @since 1.4 251 */ 252 @Override engineGenerateCertPath(InputStream inStream)253 public CertPath engineGenerateCertPath(InputStream inStream) 254 throws CertificateException 255 { 256 if (inStream == null) { 257 throw new CertificateException("Missing input stream"); 258 } 259 try { 260 byte[] encoding = readOneBlock(inStream); 261 if (encoding != null) { 262 return new X509CertPath(new ByteArrayInputStream(encoding)); 263 } else { 264 throw new IOException("Empty input"); 265 } 266 } catch (IOException ioe) { 267 throw new CertificateException(ioe.getMessage()); 268 } 269 } 270 271 /** 272 * Generates a <code>CertPath</code> object and initializes it with 273 * the data read from the <code>InputStream</code> inStream. The data 274 * is assumed to be in the specified encoding. 275 * 276 * @param inStream an <code>InputStream</code> containing the data 277 * @param encoding the encoding used for the data 278 * @return a <code>CertPath</code> initialized with the data from the 279 * <code>InputStream</code> 280 * @exception CertificateException if an exception occurs while decoding or 281 * the encoding requested is not supported 282 * @since 1.4 283 */ 284 @Override engineGenerateCertPath(InputStream inStream, String encoding)285 public CertPath engineGenerateCertPath(InputStream inStream, 286 String encoding) throws CertificateException 287 { 288 if (inStream == null) { 289 throw new CertificateException("Missing input stream"); 290 } 291 try { 292 byte[] data = readOneBlock(inStream); 293 if (data != null) { 294 return new X509CertPath(new ByteArrayInputStream(data), encoding); 295 } else { 296 throw new IOException("Empty input"); 297 } 298 } catch (IOException ioe) { 299 throw new CertificateException(ioe.getMessage()); 300 } 301 } 302 303 /** 304 * Generates a <code>CertPath</code> object and initializes it with 305 * a <code>List</code> of <code>Certificate</code>s. 306 * <p> 307 * The certificates supplied must be of a type supported by the 308 * <code>CertificateFactory</code>. They will be copied out of the supplied 309 * <code>List</code> object. 310 * 311 * @param certificates a <code>List</code> of <code>Certificate</code>s 312 * @return a <code>CertPath</code> initialized with the supplied list of 313 * certificates 314 * @exception CertificateException if an exception occurs 315 * @since 1.4 316 */ 317 @Override 318 public CertPath engineGenerateCertPath(List<? extends Certificate> certificates)319 engineGenerateCertPath(List<? extends Certificate> certificates) 320 throws CertificateException 321 { 322 return(new X509CertPath(certificates)); 323 } 324 325 /** 326 * Returns an iteration of the <code>CertPath</code> encodings supported 327 * by this certificate factory, with the default encoding first. 328 * <p> 329 * Attempts to modify the returned <code>Iterator</code> via its 330 * <code>remove</code> method result in an 331 * <code>UnsupportedOperationException</code>. 332 * 333 * @return an <code>Iterator</code> over the names of the supported 334 * <code>CertPath</code> encodings (as <code>String</code>s) 335 * @since 1.4 336 */ 337 @Override engineGetCertPathEncodings()338 public Iterator<String> engineGetCertPathEncodings() { 339 return(X509CertPath.getEncodingsStatic()); 340 } 341 342 /** 343 * Returns a (possibly empty) collection view of X.509 certificates read 344 * from the given input stream <code>is</code>. 345 * 346 * @param is the input stream with the certificates. 347 * 348 * @return a (possibly empty) collection view of X.509 certificate objects 349 * initialized with the data from the input stream. 350 * 351 * @exception CertificateException on parsing errors. 352 */ 353 @Override 354 public Collection<? extends java.security.cert.Certificate> engineGenerateCertificates(InputStream is)355 engineGenerateCertificates(InputStream is) 356 throws CertificateException { 357 if (is == null) { 358 throw new CertificateException("Missing input stream"); 359 } 360 try { 361 return parseX509orPKCS7Cert(is); 362 } catch (IOException ioe) { 363 throw new CertificateException(ioe); 364 } 365 } 366 367 /** 368 * Generates an X.509 certificate revocation list (CRL) object and 369 * initializes it with the data read from the given input stream 370 * <code>is</code>. 371 * 372 * @param is an input stream with the CRL data. 373 * 374 * @return an X.509 CRL object initialized with the data 375 * from the input stream. 376 * 377 * @exception CRLException on parsing errors. 378 */ 379 @Override engineGenerateCRL(InputStream is)380 public CRL engineGenerateCRL(InputStream is) 381 throws CRLException 382 { 383 if (is == null) { 384 // clear the cache (for debugging) 385 crlCache.clear(); 386 throw new CRLException("Missing input stream"); 387 } 388 try { 389 byte[] encoding = readOneBlock(is); 390 if (encoding != null) { 391 X509CRLImpl crl = getFromCache(crlCache, encoding); 392 if (crl != null) { 393 return crl; 394 } 395 crl = new X509CRLImpl(encoding); 396 addToCache(crlCache, crl.getEncodedInternal(), crl); 397 return crl; 398 } else { 399 throw new IOException("Empty input"); 400 } 401 } catch (IOException ioe) { 402 throw new CRLException(ioe.getMessage()); 403 } 404 } 405 406 /** 407 * Returns a (possibly empty) collection view of X.509 CRLs read 408 * from the given input stream <code>is</code>. 409 * 410 * @param is the input stream with the CRLs. 411 * 412 * @return a (possibly empty) collection view of X.509 CRL objects 413 * initialized with the data from the input stream. 414 * 415 * @exception CRLException on parsing errors. 416 */ 417 @Override engineGenerateCRLs( InputStream is)418 public Collection<? extends java.security.cert.CRL> engineGenerateCRLs( 419 InputStream is) throws CRLException 420 { 421 if (is == null) { 422 throw new CRLException("Missing input stream"); 423 } 424 try { 425 return parseX509orPKCS7CRL(is); 426 } catch (IOException ioe) { 427 throw new CRLException(ioe.getMessage()); 428 } 429 } 430 431 /* 432 * Parses the data in the given input stream as a sequence of DER 433 * encoded X.509 certificates (in binary or base 64 encoded format) OR 434 * as a single PKCS#7 encoded blob (in binary or base64 encoded format). 435 */ 436 private Collection<? extends java.security.cert.Certificate> parseX509orPKCS7Cert(InputStream is)437 parseX509orPKCS7Cert(InputStream is) 438 throws CertificateException, IOException 439 { 440 int peekByte; 441 byte[] data; 442 PushbackInputStream pbis = new PushbackInputStream(is); 443 Collection<X509CertImpl> coll = new ArrayList<>(); 444 445 // Test the InputStream for end-of-stream. If the stream's 446 // initial state is already at end-of-stream then return 447 // an empty collection. Otherwise, push the byte back into the 448 // stream and let readOneBlock look for the first certificate. 449 peekByte = pbis.read(); 450 if (peekByte == -1) { 451 return new ArrayList<>(0); 452 } else { 453 pbis.unread(peekByte); 454 data = readOneBlock(pbis); 455 } 456 457 // If we end up with a null value after reading the first block 458 // then we know the end-of-stream has been reached and no certificate 459 // data has been found. 460 if (data == null) { 461 throw new CertificateException("No certificate data found"); 462 } 463 464 try { 465 PKCS7 pkcs7 = new PKCS7(data); 466 X509Certificate[] certs = pkcs7.getCertificates(); 467 // certs are optional in PKCS #7 468 if (certs != null) { 469 return Arrays.asList(certs); 470 } else { 471 // no certificates provided 472 return new ArrayList<>(0); 473 } 474 } catch (ParsingException e) { 475 while (data != null) { 476 coll.add(new X509CertImpl(data)); 477 data = readOneBlock(pbis); 478 } 479 } 480 return coll; 481 } 482 483 /* 484 * Parses the data in the given input stream as a sequence of DER encoded 485 * X.509 CRLs (in binary or base 64 encoded format) OR as a single PKCS#7 486 * encoded blob (in binary or base 64 encoded format). 487 */ 488 private Collection<? extends java.security.cert.CRL> parseX509orPKCS7CRL(InputStream is)489 parseX509orPKCS7CRL(InputStream is) 490 throws CRLException, IOException 491 { 492 int peekByte; 493 byte[] data; 494 PushbackInputStream pbis = new PushbackInputStream(is); 495 Collection<X509CRLImpl> coll = new ArrayList<>(); 496 497 // Test the InputStream for end-of-stream. If the stream's 498 // initial state is already at end-of-stream then return 499 // an empty collection. Otherwise, push the byte back into the 500 // stream and let readOneBlock look for the first CRL. 501 peekByte = pbis.read(); 502 if (peekByte == -1) { 503 return new ArrayList<>(0); 504 } else { 505 pbis.unread(peekByte); 506 data = readOneBlock(pbis); 507 } 508 509 // If we end up with a null value after reading the first block 510 // then we know the end-of-stream has been reached and no CRL 511 // data has been found. 512 if (data == null) { 513 throw new CRLException("No CRL data found"); 514 } 515 516 try { 517 PKCS7 pkcs7 = new PKCS7(data); 518 X509CRL[] crls = pkcs7.getCRLs(); 519 // CRLs are optional in PKCS #7 520 if (crls != null) { 521 return Arrays.asList(crls); 522 } else { 523 // no crls provided 524 return new ArrayList<>(0); 525 } 526 } catch (ParsingException e) { 527 while (data != null) { 528 coll.add(new X509CRLImpl(data)); 529 data = readOneBlock(pbis); 530 } 531 } 532 return coll; 533 } 534 535 /** 536 * Returns an ASN.1 SEQUENCE from a stream, which might be a BER-encoded 537 * binary block or a PEM-style BASE64-encoded ASCII data. In the latter 538 * case, it's de-BASE64'ed before return. 539 * 540 * After the reading, the input stream pointer is after the BER block, or 541 * after the newline character after the -----END SOMETHING----- line. 542 * 543 * @param is the InputStream 544 * @return byte block or null if end of stream 545 * @throws IOException If any parsing error 546 */ readOneBlock(InputStream is)547 private static byte[] readOneBlock(InputStream is) throws IOException { 548 549 // The first character of a BLOCK. 550 int c = is.read(); 551 if (c == -1) { 552 return null; 553 } 554 if (c == DerValue.tag_Sequence) { 555 ByteArrayOutputStream bout = new ByteArrayOutputStream(2048); 556 bout.write(c); 557 readBERInternal(is, bout, c); 558 return bout.toByteArray(); 559 } else { 560 // Read BASE64 encoded data, might skip info at the beginning 561 ByteArrayOutputStream data = new ByteArrayOutputStream(); 562 563 // Step 1: Read until header is found 564 int hyphen = (c=='-') ? 1: 0; // count of consequent hyphens 565 int last = (c=='-') ? -1: c; // the char before hyphen 566 while (true) { 567 int next = is.read(); 568 if (next == -1) { 569 // We accept useless data after the last block, 570 // say, empty lines. 571 return null; 572 } 573 if (next == '-') { 574 hyphen++; 575 } else { 576 hyphen = 0; 577 last = next; 578 } 579 if (hyphen == 5 && (last == -1 || last == '\r' || last == '\n')) { 580 break; 581 } 582 } 583 584 // Step 2: Read the rest of header, determine the line end 585 int end; 586 StringBuilder header = new StringBuilder("-----"); 587 while (true) { 588 int next = is.read(); 589 if (next == -1) { 590 throw new IOException("Incomplete data"); 591 } 592 if (next == '\n') { 593 end = '\n'; 594 break; 595 } 596 if (next == '\r') { 597 next = is.read(); 598 if (next == -1) { 599 throw new IOException("Incomplete data"); 600 } 601 if (next == '\n') { 602 end = '\n'; 603 } else { 604 end = '\r'; 605 // Skip all white space chars 606 if (next != 9 && next != 10 && next != 13 && next != 32) { 607 data.write(next); 608 } 609 } 610 break; 611 } 612 header.append((char)next); 613 } 614 615 // Step 3: Read the data 616 while (true) { 617 int next = is.read(); 618 if (next == -1) { 619 throw new IOException("Incomplete data"); 620 } 621 if (next != '-') { 622 // Skip all white space chars 623 if (next != 9 && next != 10 && next != 13 && next != 32) { 624 data.write(next); 625 } 626 } else { 627 break; 628 } 629 } 630 631 // Step 4: Consume the footer 632 StringBuilder footer = new StringBuilder("-"); 633 while (true) { 634 int next = is.read(); 635 // Add next == '\n' for maximum safety, in case endline 636 // is not consistent. 637 if (next == -1 || next == end || next == '\n') { 638 break; 639 } 640 if (next != '\r') footer.append((char)next); 641 } 642 643 checkHeaderFooter(header.toString().stripTrailing(), 644 footer.toString().stripTrailing()); 645 646 try { 647 return Base64.getDecoder().decode(data.toByteArray()); 648 } catch (IllegalArgumentException e) { 649 throw new IOException(e); 650 } 651 } 652 } 653 checkHeaderFooter(String header, String footer)654 private static void checkHeaderFooter(String header, 655 String footer) throws IOException { 656 if (header.length() < 16 || !header.startsWith("-----BEGIN ") || 657 !header.endsWith("-----")) { 658 throw new IOException("Illegal header: " + header); 659 } 660 if (footer.length() < 14 || !footer.startsWith("-----END ") || 661 !footer.endsWith("-----")) { 662 throw new IOException("Illegal footer: " + footer); 663 } 664 String headerType = header.substring(11, header.length()-5); 665 String footerType = footer.substring(9, footer.length()-5); 666 if (!headerType.equals(footerType)) { 667 throw new IOException("Header and footer do not match: " + 668 header + " " + footer); 669 } 670 } 671 672 /** 673 * Read one BER data block. This method is aware of indefinite-length BER 674 * encoding and will read all of the sub-sections in a recursive way 675 * 676 * @param is Read from this InputStream 677 * @param bout Write into this OutputStream 678 * @param tag Tag already read (-1 mean not read) 679 * @return The current tag, used to check EOC in indefinite-length BER 680 * @throws IOException Any parsing error 681 */ readBERInternal(InputStream is, ByteArrayOutputStream bout, int tag)682 private static int readBERInternal(InputStream is, 683 ByteArrayOutputStream bout, int tag) throws IOException { 684 685 if (tag == -1) { // Not read before the call, read now 686 tag = is.read(); 687 if (tag == -1) { 688 throw new IOException("BER/DER tag info absent"); 689 } 690 if ((tag & 0x1f) == 0x1f) { 691 throw new IOException("Multi octets tag not supported"); 692 } 693 bout.write(tag); 694 } 695 696 int n = is.read(); 697 if (n == -1) { 698 throw new IOException("BER/DER length info absent"); 699 } 700 bout.write(n); 701 702 int length; 703 704 if (n == 0x80) { // Indefinite-length encoding 705 if ((tag & 0x20) != 0x20) { 706 throw new IOException( 707 "Non constructed encoding must have definite length"); 708 } 709 while (true) { 710 int subTag = readBERInternal(is, bout, -1); 711 if (subTag == 0) { // EOC, end of indefinite-length section 712 break; 713 } 714 } 715 } else { 716 if (n < 0x80) { 717 length = n; 718 } else if (n == 0x81) { 719 length = is.read(); 720 if (length == -1) { 721 throw new IOException("Incomplete BER/DER length info"); 722 } 723 bout.write(length); 724 } else if (n == 0x82) { 725 int highByte = is.read(); 726 int lowByte = is.read(); 727 if (lowByte == -1) { 728 throw new IOException("Incomplete BER/DER length info"); 729 } 730 bout.write(highByte); 731 bout.write(lowByte); 732 length = (highByte << 8) | lowByte; 733 } else if (n == 0x83) { 734 int highByte = is.read(); 735 int midByte = is.read(); 736 int lowByte = is.read(); 737 if (lowByte == -1) { 738 throw new IOException("Incomplete BER/DER length info"); 739 } 740 bout.write(highByte); 741 bout.write(midByte); 742 bout.write(lowByte); 743 length = (highByte << 16) | (midByte << 8) | lowByte; 744 } else if (n == 0x84) { 745 int highByte = is.read(); 746 int nextByte = is.read(); 747 int midByte = is.read(); 748 int lowByte = is.read(); 749 if (lowByte == -1) { 750 throw new IOException("Incomplete BER/DER length info"); 751 } 752 if (highByte > 127) { 753 throw new IOException("Invalid BER/DER data (a little huge?)"); 754 } 755 bout.write(highByte); 756 bout.write(nextByte); 757 bout.write(midByte); 758 bout.write(lowByte); 759 length = (highByte << 24 ) | (nextByte << 16) | 760 (midByte << 8) | lowByte; 761 } else { // ignore longer length forms 762 throw new IOException("Invalid BER/DER data (too huge?)"); 763 } 764 if (readFully(is, bout, length) != length) { 765 throw new IOException("Incomplete BER/DER data"); 766 } 767 } 768 return tag; 769 } 770 commitEvent(X509CertImpl info)771 private void commitEvent(X509CertImpl info) { 772 X509CertificateEvent xce = new X509CertificateEvent(); 773 if (xce.shouldCommit() || EventHelper.isLoggingSecurity()) { 774 PublicKey pKey = info.getPublicKey(); 775 String algId = info.getSigAlgName(); 776 String serNum = info.getSerialNumber().toString(16); 777 String subject = info.getSubjectDN().getName(); 778 String issuer = info.getIssuerDN().getName(); 779 String keyType = pKey.getAlgorithm(); 780 int length = KeyUtil.getKeySize(pKey); 781 int hashCode = info.hashCode(); 782 long beginDate = info.getNotBefore().getTime(); 783 long endDate = info.getNotAfter().getTime(); 784 if (xce.shouldCommit()) { 785 xce.algorithm = algId; 786 xce.serialNumber = serNum; 787 xce.subject = subject; 788 xce.issuer = issuer; 789 xce.keyType = keyType; 790 xce.keyLength = length; 791 xce.certificateId = hashCode; 792 xce.validFrom = beginDate; 793 xce.validUntil = endDate; 794 xce.commit(); 795 } 796 if (EventHelper.isLoggingSecurity()) { 797 EventHelper.logX509CertificateEvent(algId, 798 serNum, 799 subject, 800 issuer, 801 keyType, 802 length, 803 hashCode, 804 beginDate, 805 endDate); 806 } 807 } 808 } 809 } 810