001/** 002 * Copyright 2012 Emmanuel Bourg 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package net.jsign.pe; 018 019import java.io.Closeable; 020import java.io.File; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.io.PrintWriter; 024import java.nio.ByteBuffer; 025import java.nio.ByteOrder; 026import java.nio.channels.SeekableByteChannel; 027import java.nio.file.Files; 028import java.nio.file.StandardOpenOption; 029import java.security.MessageDigest; 030import java.util.ArrayList; 031import java.util.Date; 032import java.util.List; 033 034import net.jsign.DigestAlgorithm; 035import net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers; 036 037import org.bouncycastle.asn1.ASN1Encodable; 038import org.bouncycastle.asn1.cms.Attribute; 039import org.bouncycastle.asn1.cms.AttributeTable; 040import org.bouncycastle.asn1.cms.ContentInfo; 041import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 042import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; 043import org.bouncycastle.cert.X509CertificateHolder; 044import org.bouncycastle.cms.CMSProcessable; 045import org.bouncycastle.cms.CMSSignedData; 046import org.bouncycastle.cms.SignerInformation; 047 048/** 049 * Portable Executable File. 050 * 051 * This class is thread safe. 052 * 053 * @see <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx">Microsoft PE and COFF Specification </a> 054 * 055 * @author Emmanuel Bourg 056 * @since 1.0 057 */ 058public class PEFile implements Closeable { 059 060 /** The position of the PE header in the file */ 061 private final long peHeaderOffset; 062 063 private File file; 064 final SeekableByteChannel channel; 065 066 /** Reusable buffer for reading bytes, words, dwords and qwords from the file */ 067 private final ByteBuffer valueBuffer = ByteBuffer.allocate(8); 068 { 069 valueBuffer.order(ByteOrder.LITTLE_ENDIAN); 070 } 071 072 /** 073 * Create a PEFile from the specified file. 074 * 075 * @param file 076 * @throws IOException 077 */ 078 public PEFile(File file) throws IOException { 079 this(Files.newByteChannel(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)); 080 this.file = file; 081 } 082 083 /** 084 * Create a PEFile from the specified channel. 085 * 086 * @since 2.0 087 */ 088 public PEFile(SeekableByteChannel channel) throws IOException { 089 this.channel = channel; 090 091 try { 092 // DOS Header 093 read(0, 0, 2); 094 if (valueBuffer.get() != 'M' || valueBuffer.get() != 'Z') { 095 throw new IOException("DOS header signature not found"); 096 } 097 098 // PE Header 099 read(0x3C, 0, 4); 100 peHeaderOffset = valueBuffer.getInt() & 0xFFFFFFFFL; 101 read(peHeaderOffset, 0, 4); 102 if (valueBuffer.get() != 'P' || valueBuffer.get() != 'E' || valueBuffer.get() != 0 || valueBuffer.get() != 0) { 103 throw new IOException("PE signature not found as expected at offset 0x" + Long.toHexString(peHeaderOffset)); 104 } 105 106 } catch (IOException e) { 107 channel.close(); 108 throw e; 109 } 110 } 111 112 public synchronized void close() throws IOException { 113 channel.close(); 114 } 115 116 synchronized int read(byte[] buffer, long base, int offset) { 117 try { 118 channel.position(base + offset); 119 return channel.read(ByteBuffer.wrap(buffer)); 120 } catch (IOException e) { 121 throw new RuntimeException(e); 122 } 123 } 124 125 private void read(long base, int offset, int length) { 126 try { 127 valueBuffer.limit(length); 128 valueBuffer.clear(); 129 channel.position(base + offset); 130 channel.read(valueBuffer); 131 valueBuffer.rewind(); 132 } catch (IOException e) { 133 throw new RuntimeException(e); 134 } 135 } 136 137 synchronized int read(long base, int offset) { 138 read(base, offset, 1); 139 return valueBuffer.get(); 140 } 141 142 synchronized int readWord(long base, int offset) { 143 read(base, offset, 2); 144 return valueBuffer.getShort() & 0xFFFF; 145 } 146 147 synchronized long readDWord(long base, int offset) { 148 read(base, offset, 4); 149 return valueBuffer.getInt() & 0xFFFFFFFFL; 150 } 151 152 synchronized long readQWord(long base, int offset) { 153 read(base, offset, 8); 154 return valueBuffer.getLong(); 155 } 156 157 synchronized void write(long base, byte[] data) { 158 try { 159 channel.position(base); 160 channel.write(ByteBuffer.wrap(data)); 161 } catch (IOException e) { 162 throw new RuntimeException(e); 163 } 164 } 165 166 public MachineType getMachineType() { 167 return MachineType.valueOf(readWord(peHeaderOffset, 4)); 168 } 169 170 /** 171 * The number of sections. This indicates the size of the section table, 172 * which immediately follows the headers. 173 */ 174 public int getNumberOfSections() { 175 return readWord(peHeaderOffset, 6); 176 } 177 178 /** 179 * The low 32 bits of the number of seconds since 00:00 January 1, 1970 180 * (a C runtime time_t value), that indicates when the file was created. 181 */ 182 public Date getTimeDateStamp() { 183 return new Date(1000 * readDWord(peHeaderOffset, 8)); 184 } 185 186 /** 187 * The file offset of the COFF symbol table, or zero if no COFF symbol table 188 * is present. This value should be zero for an image because COFF debugging 189 * information is deprecated. 190 */ 191 public long getPointerToSymbolTable() { 192 return readDWord(peHeaderOffset, 12); 193 } 194 195 /** 196 * The number of entries in the symbol table. This data can be used to 197 * locate the string table, which immediately follows the symbol table. 198 * This value should be zero for an image because COFF debugging 199 * information is deprecated. 200 */ 201 public long getNumberOfSymbols() { 202 return readDWord(peHeaderOffset, 16); 203 } 204 205 /** 206 * The size of the optional header, which is required for executable files 207 * but not for object files. This value should be zero for an object file. 208 */ 209 public int getSizeOfOptionalHeader() { 210 return readWord(peHeaderOffset, 20); 211 } 212 213 /** 214 * The flags that indicate the attributes of the file. 215 */ 216 public int getCharacteristics() { 217 return readWord(peHeaderOffset, 22); 218 } 219 220 public PEFormat getFormat() { 221 return PEFormat.valueOf(readWord(peHeaderOffset, 24)); 222 } 223 224 /** 225 * The linker major version number. 226 */ 227 public int getMajorLinkerVersion() { 228 return read(peHeaderOffset, 26); 229 } 230 231 /** 232 * The linker minor version number. 233 */ 234 public int getMinorLinkerVersion() { 235 return read(peHeaderOffset, 27); 236 } 237 238 /** 239 * The size of the code (text) section, or the sum of all code sections 240 * if there are multiple sections. 241 */ 242 public long getSizeOfCode() { 243 return readDWord(peHeaderOffset, 28); 244 } 245 246 /** 247 * The size of the initialized data section, or the sum of all such 248 * sections if there are multiple data sections. 249 */ 250 public long getSizeOfInitializedData() { 251 return readDWord(peHeaderOffset, 32); 252 } 253 254 /** 255 * The size of the uninitialized data section (BSS), or the sum of all such 256 * sections if there are multiple BSS sections. 257 */ 258 public long getSizeOfUninitializedData() { 259 return readDWord(peHeaderOffset, 36); 260 } 261 262 /** 263 * The address of the entry point relative to the image base when the 264 * executable file is loaded into memory. For program images, this is the 265 * starting address. For device drivers, this is the address of the 266 * initialization function. An entry point is optional for DLLs. When no 267 * entry point is present, this field must be zero. 268 */ 269 public long getAddressOfEntryPoint() { 270 return readDWord(peHeaderOffset, 40); 271 } 272 273 /** 274 * The address that is relative to the image base of the beginning-of-code 275 * section when it is loaded into memory. 276 */ 277 public long getBaseOfCode() { 278 return readDWord(peHeaderOffset, 44); 279 } 280 281 /** 282 * The address that is relative to the image base of the beginning-of-data 283 * section when it is loaded into memory (PE32 only). 284 */ 285 public long getBaseOfData() { 286 if (PEFormat.PE32.equals(getFormat())) { 287 return readDWord(peHeaderOffset, 48); 288 } else { 289 return 0; 290 } 291 } 292 293 /** 294 * The preferred address of the first byte of image when loaded into memory; 295 * must be a multiple of 64 K. The default for DLLs is 0x10000000. The default 296 * for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 2000, 297 * Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000. 298 */ 299 public long getImageBase() { 300 if (PEFormat.PE32.equals(getFormat())) { 301 return readDWord(peHeaderOffset, 52); 302 } else { 303 return readQWord(peHeaderOffset, 48); 304 } 305 } 306 307 /** 308 * The alignment (in bytes) of sections when they are loaded into memory. 309 * It must be greater than or equal to FileAlignment. The default is the 310 * page size for the architecture. 311 */ 312 public long getSectionAlignment() { 313 return readDWord(peHeaderOffset, 56); 314 } 315 316 /** 317 * The alignment factor (in bytes) that is used to align the raw data of 318 * sections in the image file. The value should be a power of 2 between 319 * 512 and 64 K, inclusive. The default is 512. If the SectionAlignment 320 * is less than the architecture?s page size, then FileAlignment must 321 * match SectionAlignment. 322 */ 323 public long getFileAlignment() { 324 return readDWord(peHeaderOffset, 60); 325 } 326 327 /** 328 * The major version number of the required operating system. 329 */ 330 public int getMajorOperatingSystemVersion() { 331 return readWord(peHeaderOffset, 64); 332 } 333 334 /** 335 * The minor version number of the required operating system. 336 */ 337 public int getMinorOperatingSystemVersion() { 338 return readWord(peHeaderOffset, 66); 339 } 340 341 /** 342 * The major version number of the image. 343 */ 344 public int getMajorImageVersion() { 345 return readWord(peHeaderOffset, 68); 346 } 347 348 /** 349 * The minor version number of the image. 350 */ 351 public int getMinorImageVersion() { 352 return readWord(peHeaderOffset, 70); 353 } 354 355 /** 356 * The major version number of the subsystem. 357 */ 358 public int getMajorSubsystemVersion() { 359 return readWord(peHeaderOffset, 72); 360 } 361 362 /** 363 * The minor version number of the subsystem. 364 */ 365 public int getMinorSubsystemVersion() { 366 return readWord(peHeaderOffset, 74); 367 } 368 369 /** 370 * Reserved, must be zero. 371 */ 372 public long getWin32VersionValue() { 373 return readDWord(peHeaderOffset, 76); 374 } 375 376 /** 377 * The size (in bytes) of the image, including all headers, as the image 378 * is loaded in memory. It must be a multiple of SectionAlignment. 379 */ 380 public long getSizeOfImage() { 381 return readDWord(peHeaderOffset, 80); 382 } 383 384 /** 385 * The combined size of an MS DOS stub, PE header, and section headers 386 * rounded up to a multiple of FileAlignment. 387 */ 388 public long getSizeOfHeaders() { 389 return readDWord(peHeaderOffset, 84); 390 } 391 392 /** 393 * The image file checksum. 394 */ 395 public long getCheckSum() { 396 return readDWord(peHeaderOffset, 88); 397 } 398 399 /** 400 * Compute the checksum of the image file. The algorithm for computing 401 * the checksum is incorporated into IMAGHELP.DLL. 402 */ 403 public synchronized long computeChecksum() { 404 PEImageChecksum checksum = new PEImageChecksum(peHeaderOffset + 88); 405 406 ByteBuffer b = ByteBuffer.allocate(64 * 1024); 407 408 try { 409 channel.position(0); 410 411 int len; 412 while ((len = channel.read(b)) > 0) { 413 checksum.update(b.array(), 0, len); 414 } 415 } catch (IOException e) { 416 throw new RuntimeException(e); 417 } 418 419 return checksum.getValue(); 420 } 421 422 public synchronized void updateChecksum() { 423 ByteBuffer buffer = ByteBuffer.allocate(4); 424 buffer.order(ByteOrder.LITTLE_ENDIAN); 425 buffer.putInt((int) computeChecksum()); 426 427 try { 428 channel.position(peHeaderOffset + 88); 429 channel.write(buffer); 430 } catch (IOException e) { 431 throw new RuntimeException(e); 432 } 433 } 434 435 /** 436 * The subsystem that is required to run this image. 437 */ 438 public Subsystem getSubsystem() { 439 return Subsystem.valueOf(readWord(peHeaderOffset, 92)); 440 } 441 442 public int getDllCharacteristics() { 443 return readWord(peHeaderOffset, 94); 444 } 445 446 /** 447 * The size of the stack to reserve. Only SizeOfStackCommit is committed; 448 * the rest is made available one page at a time until the reserve size is reached. 449 */ 450 public long getSizeOfStackReserve() { 451 if (PEFormat.PE32.equals(getFormat())) { 452 return readDWord(peHeaderOffset, 96); 453 } else { 454 return readQWord(peHeaderOffset, 96); 455 } 456 } 457 458 /** 459 * The size of the stack to commit. 460 */ 461 public long getSizeOfStackCommit() { 462 if (PEFormat.PE32.equals(getFormat())) { 463 return readDWord(peHeaderOffset, 100); 464 } else { 465 return readQWord(peHeaderOffset, 104); 466 } 467 } 468 469 /** 470 * The size of the local heap space to reserve. Only SizeOfHeapCommit is 471 * committed; the rest is made available one page at a time until the 472 * reserve size is reached. 473 */ 474 public long getSizeOfHeapReserve() { 475 if (PEFormat.PE32.equals(getFormat())) { 476 return readDWord(peHeaderOffset, 104); 477 } else { 478 return readQWord(peHeaderOffset, 112); 479 } 480 } 481 482 /** 483 * The size of the local heap space to commit. 484 */ 485 public long getSizeOfHeapCommit() { 486 if (PEFormat.PE32.equals(getFormat())) { 487 return readDWord(peHeaderOffset, 108); 488 } else { 489 return readQWord(peHeaderOffset, 120); 490 } 491 } 492 493 /** 494 * Reserved, must be zero. 495 */ 496 public long getLoaderFlags() { 497 return readDWord(peHeaderOffset, PEFormat.PE32.equals(getFormat()) ? 112 : 128); 498 } 499 500 /** 501 * The number of data-directory entries in the remainder of the optional 502 * header. Each describes a location and size. 503 */ 504 public int getNumberOfRvaAndSizes() { 505 return (int) readDWord(peHeaderOffset, PEFormat.PE32.equals(getFormat()) ? 116 : 132); 506 } 507 508 int getDataDirectoryOffset() { 509 return (int) peHeaderOffset + (PEFormat.PE32.equals(getFormat()) ? 120 : 136); 510 } 511 512 /** 513 * Returns the data directory of the specified type. 514 */ 515 public DataDirectory getDataDirectory(DataDirectoryType type) { 516 if (type.ordinal() >= getNumberOfRvaAndSizes()) { 517 return null; 518 } else { 519 return new DataDirectory(this, type.ordinal()); 520 } 521 } 522 523 /** 524 * Writes the data directory of the specified type. The data is either appended 525 * at the end of the file or written over the previous data of the same type if 526 * there is enough space. 527 * 528 * @param type the type of the data directory 529 * @param data the content of the data directory 530 */ 531 public synchronized void writeDataDirectory(DataDirectoryType type, byte[] data) throws IOException { 532 DataDirectory directory = getDataDirectory(type); 533 534 if (!directory.exists()) { 535 // append the data directory at the end of the file 536 long offset = channel.size(); 537 538 channel.position(offset); 539 channel.write(ByteBuffer.wrap(data)); 540 541 // update the entry in the data directory table 542 directory.write(offset, data.length); 543 544 } else { 545 if (data.length == directory.getSize()) { 546 // same size as before, just overwrite 547 channel.position(directory.getVirtualAddress()); 548 channel.write(ByteBuffer.wrap(data)); 549 550 } else if (data.length < directory.getSize() && type != DataDirectoryType.CERTIFICATE_TABLE) { 551 // the new data is smaller, erase and rewrite in-place 552 // this doesn't work with the certificate table since it changes the file digest 553 directory.erase(); 554 channel.position(directory.getVirtualAddress()); 555 channel.write(ByteBuffer.wrap(data)); 556 557 // update the size in the data directory table 558 directory.write(directory.getVirtualAddress(), data.length); 559 560 } else if (directory.isTrailing()) { 561 // the data is at the end of the file, overwrite it 562 channel.position(directory.getVirtualAddress()); 563 channel.write(ByteBuffer.wrap(data)); 564 channel.truncate(directory.getVirtualAddress() + data.length); // trim the file if the data shrunk 565 566 // update the size in the data directory table 567 directory.write(directory.getVirtualAddress(), data.length); 568 569 } else { 570 if (type == DataDirectoryType.CERTIFICATE_TABLE) { 571 throw new IOException("The certificate table isn't at the end of the file and can't be moved without invalidating the signature"); 572 } 573 574 // the new data is larger, erase and relocate it at the end 575 directory.erase(); 576 577 long offset = channel.size(); 578 579 channel.position(offset); 580 channel.write(ByteBuffer.wrap(data)); 581 582 // update the entry in the data directory table 583 directory.write(offset, data.length); 584 } 585 } 586 587 updateChecksum(); 588 } 589 590 /** 591 * Returns the authenticode signatures on the file. 592 */ 593 public synchronized List<CMSSignedData> getSignatures() { 594 List<CMSSignedData> signatures = new ArrayList<>(); 595 596 for (CertificateTableEntry entry : getCertificateTable()) { 597 try { 598 CMSSignedData signedData = entry.getSignature(); 599 signatures.add(signedData); 600 601 // look for nested signatures 602 SignerInformation signerInformation = signedData.getSignerInfos().getSigners().iterator().next(); 603 AttributeTable unsignedAttributes = signerInformation.getUnsignedAttributes(); 604 if (unsignedAttributes != null) { 605 Attribute nestedSignatures = unsignedAttributes.get(AuthenticodeObjectIdentifiers.SPC_NESTED_SIGNATURE_OBJID); 606 if (nestedSignatures != null) { 607 for (ASN1Encodable nestedSignature : nestedSignatures.getAttrValues()) { 608 signatures.add(new CMSSignedData((CMSProcessable) null, ContentInfo.getInstance(nestedSignature))); 609 } 610 } 611 } 612 } catch (UnsupportedOperationException e) { 613 // unsupported type, just skip 614 } catch (Exception e) { 615 e.printStackTrace(); 616 } 617 } 618 619 return signatures; 620 } 621 622 private synchronized List<CertificateTableEntry> getCertificateTable() { 623 List<CertificateTableEntry> entries = new ArrayList<>(); 624 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 625 626 if (certificateTable != null && certificateTable.exists()) { 627 long position = certificateTable.getVirtualAddress(); 628 629 try { 630 entries.add(new CertificateTableEntry(this, position)); 631 632 // todo read the remaining entries (but Authenticode use only one, extra signatures are appended as a SPC_NESTED_SIGNATURE unauthenticated attribute) 633 } catch (Exception e) { 634 e.printStackTrace(); 635 } 636 } 637 638 return entries; 639 } 640 641 public synchronized List<Section> getSections() { 642 List<Section> sections = new ArrayList<>(); 643 int sectionTableOffset = getDataDirectoryOffset() + 8 * getNumberOfRvaAndSizes(); 644 645 for (int i = 0; i < getNumberOfSections(); i++) { 646 sections.add(new Section(this, sectionTableOffset + 40 * i)); 647 } 648 649 return sections; 650 } 651 652 /** 653 * Print detailed informations about the PE file. 654 */ 655 public void printInfo(OutputStream out) { 656 printInfo(new PrintWriter(out, true)); 657 } 658 659 /** 660 * Print detailed informations about the PE file. 661 */ 662 public void printInfo(PrintWriter out) { 663 if (file != null) { 664 out.println("PE File"); 665 out.println(" Name: " + file.getName()); 666 out.println(" Size: " + file.length()); 667 out.println(" Last Modified: " + new Date(file.lastModified())); 668 out.println(); 669 } 670 671 out.println("PE Header"); 672 out.println(" Machine: " + getMachineType()); 673 out.println(" Number of sections: " + getNumberOfSections()); 674 out.println(" Timestamp: " + getTimeDateStamp()); 675 out.println(" Pointer to symbol table: 0x" + Long.toHexString(getPointerToSymbolTable())); 676 out.println(" Number of symbols: " + getNumberOfSymbols()); 677 out.println(" Size of optional header: " + getSizeOfOptionalHeader()); 678 out.println(" Characteristics: 0x" + Long.toBinaryString(getCharacteristics())); 679 out.println(); 680 681 out.println("Optional Header"); 682 PEFormat format = getFormat(); 683 out.println(" PE Format: 0x" + Integer.toHexString(format.value) + " (" + format.label + ")"); 684 out.println(" Linker version: " + getMajorLinkerVersion() + "." + getMinorLinkerVersion()); 685 out.println(" Size of code: " + getSizeOfCode()); 686 out.println(" Size of initialized data: " + getSizeOfInitializedData()); 687 out.println(" Size of uninitialized data: " + getSizeOfUninitializedData()); 688 out.println(" Address of entry point: 0x" + Long.toHexString(getAddressOfEntryPoint())); 689 out.println(" Base of code: 0x" + Long.toHexString(getBaseOfCode())); 690 if (PEFormat.PE32.equals(getFormat())) { 691 out.println(" Base of data: 0x" + Long.toHexString(getBaseOfData())); 692 } 693 out.println(" Image base: 0x" + Long.toHexString(getImageBase())); 694 out.println(" Section alignment: " + getSectionAlignment()); 695 out.println(" File alignment: " + getFileAlignment()); 696 out.println(" Operating system version: " + getMajorOperatingSystemVersion() + "." + getMinorOperatingSystemVersion()); 697 out.println(" Image version: " + getMajorImageVersion() + "." + getMinorImageVersion()); 698 out.println(" Subsystem version: " + getMajorSubsystemVersion() + "." + getMinorSubsystemVersion()); 699 out.println(" Size of image: " + getSizeOfImage()); 700 out.println(" Size of headers: " + getSizeOfHeaders()); 701 out.println(" Checksum: 0x" + Long.toHexString(getCheckSum())); 702 out.println(" Checksum (computed): 0x" + Long.toHexString(computeChecksum())); 703 out.println(" Subsystem: " + getSubsystem()); 704 out.println(" DLL characteristics: 0x" + Long.toBinaryString(getDllCharacteristics())); 705 out.println(" Size of stack reserve: " + getSizeOfStackReserve()); 706 out.println(" Size of stack commit: " + getSizeOfStackCommit()); 707 out.println(" Size of heap reserve: " + getSizeOfHeapReserve()); 708 out.println(" Size of heap commit: " + getSizeOfHeapCommit()); 709 out.println(" Number of RVA and sizes: " + getNumberOfRvaAndSizes()); 710 out.println(); 711 712 out.println("Data Directory"); 713 for (DataDirectoryType type : DataDirectoryType.values()) { 714 DataDirectory entry = getDataDirectory(type); 715 if (entry != null && entry.exists()) { 716 out.printf(" %-30s 0x%08x %8d bytes\n", type, entry.getVirtualAddress(), entry.getSize()); 717 } 718 } 719 out.println(); 720 721 out.println("Sections"); 722 out.println(" Name Virtual Size Virtual Address Raw Data Size Raw Data Ptr Characteristics"); 723 List<Section> sections = getSections(); 724 for (int i = 0; i < sections.size(); i++) { 725 Section section = sections.get(i); 726 out.printf(" #%d %-8s %8d 0x%08x %8d 0x%08x %s\n", i + 1, section.getName(), section.getVirtualSize(), section.getVirtualAddress(), section.getSizeOfRawData(), section.getPointerToRawData(), section.getCharacteristics()); 727 } 728 out.println(); 729 730 List<CMSSignedData> signatures = getSignatures(); 731 if (!signatures.isEmpty()) { 732 out.println("Signatures"); 733 for (CMSSignedData signedData : signatures) { 734 SignerInformation signerInformation = signedData.getSignerInfos().getSigners().iterator().next(); 735 X509CertificateHolder certificate = (X509CertificateHolder) signedData.getCertificates().getMatches(signerInformation.getSID()).iterator().next(); 736 737 String commonName = certificate.getSubject().getRDNs(X509ObjectIdentifiers.commonName)[0].getFirst().getValue().toString(); 738 739 AttributeTable unsignedAttributes = signerInformation.getUnsignedAttributes(); 740 boolean timestamped = unsignedAttributes != null && 741 (unsignedAttributes.get(PKCSObjectIdentifiers.pkcs_9_at_counterSignature) != null 742 || unsignedAttributes.get(AuthenticodeObjectIdentifiers.SPC_RFC3161_OBJID) != null); 743 DigestAlgorithm algorithm = DigestAlgorithm.of(signerInformation.getDigestAlgorithmID().getAlgorithm()); 744 out.println(" " + commonName + " " + (algorithm != null ? "[" + algorithm.id + "] " : "") + (timestamped ? "(timestamped)" : "")); 745 } 746 } 747 } 748 749 /** 750 * Compute the digest of the file. The checksum field, the certificate 751 * directory table entry and the certificate table are excluded from 752 * the digest. 753 */ 754 private synchronized byte[] computeDigest(MessageDigest digest) throws IOException { 755 long checksumLocation = peHeaderOffset + 88; 756 757 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 758 759 // digest from the beginning to the checksum field (excluded) 760 updateDigest(digest, 0, checksumLocation); 761 762 // skip the checksum field 763 long position = checksumLocation + 4; 764 765 // digest from the end of the checksum field to the beginning of the certificate table entry 766 int certificateTableOffset = getDataDirectoryOffset() + 8 * DataDirectoryType.CERTIFICATE_TABLE.ordinal(); 767 updateDigest(digest, position, certificateTableOffset); 768 769 // skip the certificate entry 770 position = certificateTableOffset + 8; 771 772 // todo digest the sections in ascending address order 773 774 // digest from the end of the certificate table entry to the beginning of the certificate table 775 if (certificateTable != null && certificateTable.exists()) { 776 updateDigest(digest, position, certificateTable.getVirtualAddress()); 777 position = certificateTable.getVirtualAddress() + certificateTable.getSize(); 778 } 779 780 // digest from the end of the certificate table to the end of the file 781 updateDigest(digest, position, channel.size()); 782 783 return digest.digest(); 784 } 785 786 /** 787 * Update the specified digest by reading the underlying RandomAccessFile 788 * from the start offset included to the end offset excluded. 789 * 790 * @param digest 791 * @param startOffset 792 * @param endOffset 793 */ 794 private void updateDigest(MessageDigest digest, long startOffset, long endOffset) throws IOException { 795 channel.position(startOffset); 796 797 ByteBuffer buffer = ByteBuffer.allocate(8192); 798 799 long position = startOffset; 800 while (position < endOffset) { 801 buffer.clear(); 802 buffer.limit((int) Math.min(buffer.capacity(), endOffset - position)); 803 channel.read(buffer); 804 buffer.rewind(); 805 806 digest.update(buffer); 807 808 position += buffer.limit(); 809 } 810 } 811 812 /** 813 * Compute the checksum of the file using the specified digest algorithm. 814 * 815 * @param algorithm the digest algorithm, typically SHA1 816 */ 817 public byte[] computeDigest(DigestAlgorithm algorithm) throws IOException { 818 return computeDigest(algorithm.getMessageDigest()); 819 } 820 821 /** 822 * Increase the size of the file up to a size that is a multiple of the specified value. 823 * 824 * @param multiple 825 */ 826 public synchronized void pad(int multiple) throws IOException { 827 long padding = (multiple - channel.size() % multiple) % multiple; 828 channel.position(channel.size()); 829 channel.write(ByteBuffer.allocate((int) padding)); 830 } 831}