Class StandardDecrypter

java.lang.Object
com.sun.pdfview.decrypt.StandardDecrypter
All Implemented Interfaces:
PDFDecrypter

public class StandardDecrypter extends Object implements PDFDecrypter
Standard simple decrypter for versions 1, 2 and 4 of the Standard password-based decryption mechanisms, as described in section 3.5 of the PDF Reference version 1.7.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    Describes an encryption algorithm to be used, declaring not only the cipher type, but also key generation techniques
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private static final byte[]
    Extra salt to add to AES-based decryption keys, as per PDF Reference 1.7
    private static final String
    The specification of the AES cipher for JCE interactions.
    private static final String
    The specification of the RC4 cipher for JCE interactions
    The encryption algorithm being employed
    private byte[]
    The general encryption key; may be mutated to form individual stream/string encryption keys
    private static final String
    The key type for AES keys
    private static final String
    The key type for RC4 keys
    private boolean
    Whether the owner password was specified
    private static final byte[]
    Padding used to bring passwords up to 32 bytes, as specified by the first step of Algorithm 3.2 in the PDF Reference version 1.7.
  • Constructor Summary

    Constructors
    Constructor
    Description
    StandardDecrypter(StandardDecrypter.EncryptionAlgorithm encryptionAlgorithm, PDFObject documentId, int keyBitLength, int revision, byte[] oValue, byte[] uValue, int pValue, boolean encryptMetadata, PDFPassword password)
    Class constructor
  • Method Summary

    Modifier and Type
    Method
    Description
    private byte[]
    calculateGeneralEncryptionKey(byte[] userPassword, byte[] firstDocIdValue, int keyBitLength, int revision, byte[] oValue, int pValue, boolean encryptMetadata)
    Determine what the general encryption key is, given a configuration.
    private byte[]
    calculateUValue(byte[] generalKey, byte[] firstDocIdValue, int revision)
    Calculate what the U value should consist of given a particular key and document configuration.
    private byte[]
    calculuateOValue(byte[] ownerPassword, byte[] userPassword, int keyBitLength, int revision)
    Calculate what the O value of the Encrypt dict should look like given a particular configuration.
    private void
    checkNums(int objNum, int objGen)
    Check that object number and object generations are well-formed.
    private byte[]
    checkOwnerPassword(byte[] ownerPassword, byte[] firstDocIdValue, int keyBitLength, int revision, byte[] oValue, byte[] uValue, int pValue, boolean encryptMetadata)
    Check to see whether a given password is the owner password.
    private byte[]
    checkUserPassword(byte[] userPassword, byte[] firstDocIdValue, int keyBitLength, int revision, byte[] oValue, byte[] uValue, int pValue, boolean encryptMetadata)
    Check to see whether a provided user password is correct with respect to an Encrypt dict configuration.
    private Cipher
    Create a new AES cipher.
    private Cipher
    createAndInitialiseContentCipher(ByteBuffer encrypted, byte[] decryptionKeyBytes)
    Setup the cipher for decryption
    Create an MD5 digest.
    private Cipher
    Create a new RC4 cipher.
    createRC4Key(byte[] keyBytes)
    Create an RC4 key
    private byte[]
    crypt(Cipher cipher, byte[] input)
    Encrypt some bytes
    private void
    cryptInPlace(Cipher rc4, byte[] buffer)
    Encrypt/decrypt something in place
    decryptBuffer(String cryptFilterName, PDFObject streamObj, ByteBuffer streamBuf)
    Decrypt a buffer of data
    private ByteBuffer
    decryptBuffer(ByteBuffer encrypted, byte[] decryptionKeyBytes)
    Decrypt a buffer
    decryptString(int objNum, int objGen, String inputBasicString)
    Decrypt a basic string.
    private void
    digestTo(MessageDigest md5, byte[] hash)
    Hash into an existing byte array
    private byte[]
    getInitialOwnerPasswordKeyBytes(byte[] ownerPassword, int keyBitLength, int revision)
    Establish the key to be used for the generation and validation of the user password via the O entry.
    private byte[]
    getObjectSaltedDecryptionKey(int objNum, int objGen)
    Get a decryption key salted with an object number and object generation, for use when decrypting a string or stream within an object numbered so
    private int
    Get the length of a salted key
    private int
    getSaltedContentKeyByteLength(int generalKeyByteLength)
    Get the length of salted keys, in bytes.
    private byte[]
    Get the unsalted content decryption key, used for streams with specific crypt filters, which aren't specific to particular objects
    private void
    initDecryption(Cipher cipher, Key aKey)
    Setup a cipher for decryption
    private void
    Initialise a cipher for encryption
    boolean
    Determine whether this actually applies a decryption other than identity decryption.
    boolean
    Determine whether the password known by the decrypter indicates that the user is the owner of the document.
    private byte[]
    padPassword(byte[] password)
    Pad a password as per step 1 of Algorithm 3.2 of the PDF Reference version 1.7
    private void
    rc4shuffle(byte[] shuffle, byte[] key, Cipher rc4)
    Shuffle some input using a series of RC4 encryptions with slight mutations of an given key per iteration.
    private void
    rc4unshuffle(Cipher rc4, byte[] shuffle, byte[] key)
    Reverse the rc4shuffle(byte[], byte[], javax.crypto.Cipher) operation, and the operation that invariable preceeds it, thereby obtaining an original message
    private void
    testJceAvailability(int keyBitLength)
    Test that the platform (i.e., the JCE) can offer us all of the ciphers at the key length we need for content decryption.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • AESV2_SALT

      private static final byte[] AESV2_SALT
      Extra salt to add to AES-based decryption keys, as per PDF Reference 1.7
    • PW_PADDING

      private static final byte[] PW_PADDING
      Padding used to bring passwords up to 32 bytes, as specified by the first step of Algorithm 3.2 in the PDF Reference version 1.7.
    • CIPHER_RC4

      private static final String CIPHER_RC4
      The specification of the RC4 cipher for JCE interactions
      See Also:
    • KEY_RC4

      private static final String KEY_RC4
      The key type for RC4 keys
      See Also:
    • CIPHER_AES

      private static final String CIPHER_AES
      The specification of the AES cipher for JCE interactions. As per the spec, cipher-block chanining (CBC) mode and PKCS5 padding are used
      See Also:
    • KEY_AES

      private static final String KEY_AES
      The key type for AES keys
      See Also:
    • ownerAuthorised

      private boolean ownerAuthorised
      Whether the owner password was specified
    • generalKeyBytes

      private byte[] generalKeyBytes
      The general encryption key; may be mutated to form individual stream/string encryption keys
    • encryptionAlgorithm

      private StandardDecrypter.EncryptionAlgorithm encryptionAlgorithm
      The encryption algorithm being employed
  • Constructor Details

    • StandardDecrypter

      public StandardDecrypter(StandardDecrypter.EncryptionAlgorithm encryptionAlgorithm, PDFObject documentId, int keyBitLength, int revision, byte[] oValue, byte[] uValue, int pValue, boolean encryptMetadata, PDFPassword password) throws IOException, EncryptionUnsupportedByProductException, EncryptionUnsupportedByPlatformException
      Class constructor
      Parameters:
      encryptionAlgorithm - the algorithm used for encryption
      documentId - the contents of the ID entry of the document's trailer dictionary; can be null, but according to the spec, shouldn't be. Is expected to be an array of two byte sequences.
      keyBitLength - the length of the key in bits; should be a multiple of 8 between 40 and 128
      revision - the revision of the Standard encryption security handler being employed. Should be 2, 3 or 4.
      oValue - the value of the O entry from the Encrypt dictionary
      uValue - the value of the U entry from the Encrypt dictionary
      pValue - the value of the P entry from the Encrypt dictionary
      encryptMetadata - whether metadata is being encrypted, as identified by the Encrypt dict (with default true if not explicitly identified)
      password - the password; not null
      Throws:
      IOException - if there's a problem reading the file
      EncryptionUnsupportedByPlatformException - if the encryption is not supported by the environment in which the code is executing
      EncryptionUnsupportedByProductException - if PDFRenderer does not currently support the specified encryption
  • Method Details

    • decryptBuffer

      public ByteBuffer decryptBuffer(String cryptFilterName, PDFObject streamObj, ByteBuffer streamBuf) throws PDFParseException
      Description copied from interface: PDFDecrypter
      Decrypt a buffer of data
      Specified by:
      decryptBuffer in interface PDFDecrypter
      Parameters:
      cryptFilterName - the name of the crypt filter, if V4 encryption is being used, where individual crypt filters may be specified for individual streams. If encryption is not using V4 encryption (indicated by V=4 in the Encrypt dictionary) then this must be null. Null may also be specified with V4 encryption to indicate that the default filter should be used.
      streamObj - the object whose stream is being decrypted. The containing object's number and generation contribute to the key used for stream encrypted with the document's default encryption, so this is typically required. Should be null only if a cryptFilterName is specified, as objects with specific stream filters use the general document key, rather than a stream-specific key.
      streamBuf - the buffer to decrypt
      Returns:
      a buffer containing the decrypted stream, positioned at its beginning; will only be the same buffer as streamBuf if the identity decrypter is being used
      Throws:
      PDFParseException - if the named crypt filter does not exist, or if a crypt filter is named when named crypt filters are not supported. Problems due to incorrect passwords are revealed prior to this point.
    • decryptString

      public String decryptString(int objNum, int objGen, String inputBasicString) throws PDFParseException
      Description copied from interface: PDFDecrypter
      Decrypt a basic string.
      Specified by:
      decryptString in interface PDFDecrypter
      Parameters:
      objNum - the object number of the containing object
      objGen - the generation number of the containing object
      inputBasicString - the string to be decrypted
      Returns:
      the decrypted string
      Throws:
      PDFParseException - if the named crypt filter does not exist, or if a crypt filter is named when named crypt filters are not supported. Problems due to incorrect passwords are revealed prior to this point.
    • isOwnerAuthorised

      public boolean isOwnerAuthorised()
      Description copied from interface: PDFDecrypter
      Determine whether the password known by the decrypter indicates that the user is the owner of the document. Can be used, in conjunction with PDFDecrypter.isEncryptionPresent() to determine whether any permissions apply.
      Specified by:
      isOwnerAuthorised in interface PDFDecrypter
      Returns:
      whether owner authentication is being used to decrypt the document
    • isEncryptionPresent

      public boolean isEncryptionPresent()
      Description copied from interface: PDFDecrypter
      Determine whether this actually applies a decryption other than identity decryption.
      Specified by:
      isEncryptionPresent in interface PDFDecrypter
      Returns:
      whether encryption is present
    • testJceAvailability

      private void testJceAvailability(int keyBitLength) throws EncryptionUnsupportedByPlatformException, PDFParseException
      Test that the platform (i.e., the JCE) can offer us all of the ciphers at the key length we need for content decryption. This shouldn't be a problem on the Java 5 platform unless a particularly restrictive policy file is in place. Calling this on construction should avoid problems like these being exposed as PDFParseExceptions as they're used during decryption and key establishment.
      Parameters:
      keyBitLength - the length of the content key, in bits
      Throws:
      EncryptionUnsupportedByPlatformException - if the platform does not support the required ciphers and key lengths
      PDFParseException - if there's an internal error while testing cipher availability
    • decryptBuffer

      private ByteBuffer decryptBuffer(ByteBuffer encrypted, byte[] decryptionKeyBytes) throws PDFParseException
      Decrypt a buffer
      Parameters:
      encrypted - the encrypted content
      decryptionKeyBytes - the key to use for decryption
      Returns:
      a freshly allocated buffer containing the decrypted content
      Throws:
      PDFParseException - if there's a problem decrypting the content
    • createAndInitialiseContentCipher

      private Cipher createAndInitialiseContentCipher(ByteBuffer encrypted, byte[] decryptionKeyBytes) throws PDFParseException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException
      Setup the cipher for decryption
      Parameters:
      encrypted - the encrypted content; required by AES encryption so that the initialisation vector can be established
      decryptionKeyBytes - the bytes for the decryption key
      Returns:
      a content decryption cypher, ready to accept input
      Throws:
      PDFParseException - if the encrypted buffer is malformed or on an internal error
      NoSuchAlgorithmException - if the cipher algorithm is not supported by the platform
      NoSuchPaddingException - if the cipher padding is not supported by the platform
      InvalidKeyException - if the key is invalid according to the cipher, or too long
      InvalidAlgorithmParameterException - if the cipher parameters are bad
    • getUnsaltedDecryptionKey

      private byte[] getUnsaltedDecryptionKey()
      Get the unsalted content decryption key, used for streams with specific crypt filters, which aren't specific to particular objects
      Returns:
      the general key
    • getObjectSaltedDecryptionKey

      private byte[] getObjectSaltedDecryptionKey(int objNum, int objGen) throws PDFParseException
      Get a decryption key salted with an object number and object generation, for use when decrypting a string or stream within an object numbered so
      Parameters:
      objNum - the object number
      objGen - the object generation
      Returns:
      the key to be used for decrypting data associated with the object numbered so
      Throws:
      PDFParseException - if the MD5 digest is not available
    • getSaltedContentKeyByteLength

      private int getSaltedContentKeyByteLength()
      Get the length of a salted key
      Returns:
      length in bytes
    • getSaltedContentKeyByteLength

      private int getSaltedContentKeyByteLength(int generalKeyByteLength)
      Get the length of salted keys, in bytes. Unsalted keys will be the same length as generalKeyBytes
      Parameters:
      generalKeyByteLength - the length of the general key, in bytes
      Returns:
      byte length of salted keys
    • checkNums

      private void checkNums(int objNum, int objGen) throws PDFParseException
      Check that object number and object generations are well-formed. It is possible for some PDFObjects to have uninitialised object numbers and generations, but such objects should not required decryption
      Parameters:
      objNum - the object number
      objGen - the object generation
      Throws:
      PDFParseException - if the object numbering indicates that they aren't true object numbers
    • calculateUValue

      private byte[] calculateUValue(byte[] generalKey, byte[] firstDocIdValue, int revision) throws GeneralSecurityException, EncryptionUnsupportedByProductException
      Calculate what the U value should consist of given a particular key and document configuration. Correponds to Algorithms 3.4 and 3.5 of the PDF Reference version 1.7
      Parameters:
      generalKey - the general encryption key
      firstDocIdValue - the value of the first element in the document's ID entry in the trailer dictionary
      revision - the revision of the security handler
      Returns:
      the U value for the given configuration
      Throws:
      GeneralSecurityException - if there's an error getting required ciphers, etc. (unexpected, since a check for algorithm availability is performed on construction)
      EncryptionUnsupportedByProductException - if the revision is not supported
    • calculuateOValue

      private byte[] calculuateOValue(byte[] ownerPassword, byte[] userPassword, int keyBitLength, int revision) throws GeneralSecurityException
      Calculate what the O value of the Encrypt dict should look like given a particular configuration. Not used, but useful for reference; this process is reversed to determine whether a given password is the owner password. Corresponds to Algorithm 3.3 of the PDF Reference version 1.7.
      Parameters:
      ownerPassword - the owner password
      userPassword - the user password
      keyBitLength - the key length in bits (40-128)
      revision - the security handler revision
      Returns:
      the O value entry
      Throws:
      GeneralSecurityException - if ciphers are unavailable or inappropriately used
      See Also:
    • checkOwnerPassword

      private byte[] checkOwnerPassword(byte[] ownerPassword, byte[] firstDocIdValue, int keyBitLength, int revision, byte[] oValue, byte[] uValue, int pValue, boolean encryptMetadata) throws GeneralSecurityException, EncryptionUnsupportedByProductException, PDFParseException
      Check to see whether a given password is the owner password. Corresponds to algorithm 3.6 of PDF Reference version 1.7.
      Parameters:
      ownerPassword - the suggested owner password (may be null or empty)
      firstDocIdValue - the byte stream from the first element of the value of the ID entry in the trailer dictionary
      keyBitLength - the key length in bits
      revision - the security handler revision
      oValue - the O value from the Encrypt dictionary
      uValue - the U value from the Encrypt dictionary
      pValue - the P value from the Encrypt dictionary
      encryptMetadata - the EncryptMetadata entry from the Encrypt dictionary (or false if not present or revision <= 3)
      Returns:
      the general/user key bytes if the owner password is currect, null otherwise
      Throws:
      GeneralSecurityException - if there's a problem with cipher or digest usage; unexpected
      EncryptionUnsupportedByProductException - if PDFRenderer doesn't support the security handler revision
      PDFParseException - if the document is malformed
    • getInitialOwnerPasswordKeyBytes

      private byte[] getInitialOwnerPasswordKeyBytes(byte[] ownerPassword, int keyBitLength, int revision) throws GeneralSecurityException
      Establish the key to be used for the generation and validation of the user password via the O entry. Corresponds to steps 1-4 in Algorithm 3.3 of the PDF Reference version 1.7.
      Parameters:
      ownerPassword - the owner password
      keyBitLength - the length of the key in bits
      revision - the security handler revision
      Returns:
      the key bytes to use for generation/validation of the O entry
      Throws:
      GeneralSecurityException - if there's a problem wranling ciphers
    • checkUserPassword

      private byte[] checkUserPassword(byte[] userPassword, byte[] firstDocIdValue, int keyBitLength, int revision, byte[] oValue, byte[] uValue, int pValue, boolean encryptMetadata) throws GeneralSecurityException, EncryptionUnsupportedByProductException, PDFParseException
      Check to see whether a provided user password is correct with respect to an Encrypt dict configuration. Corresponds to algorithm 3.6 of the PDF Reference version 1.7
      Parameters:
      userPassword - the user password to test; may be null or empty
      firstDocIdValue - the byte stream from the first element of the value of the ID entry in the trailer dictionary
      keyBitLength - the length of the key in bits
      revision - the security handler revision
      oValue - the O value from the Encrypt dictionary
      uValue - the U value from the Encrypt dictionary
      pValue - the P value from the Encrypt dictionary
      encryptMetadata - the EncryptMetadata entry from the Encrypt dictionary (or false if not present or revision <= 3)
      Returns:
      the general/user encryption key if the user password is correct, or null if incorrect
      Throws:
      GeneralSecurityException - if there's a problem with cipher or digest usage; unexpected
      EncryptionUnsupportedByProductException - if PDFRenderer doesn't support the security handler revision
      PDFParseException - if the document is improperly constructed
    • calculateGeneralEncryptionKey

      private byte[] calculateGeneralEncryptionKey(byte[] userPassword, byte[] firstDocIdValue, int keyBitLength, int revision, byte[] oValue, int pValue, boolean encryptMetadata) throws GeneralSecurityException
      Determine what the general encryption key is, given a configuration. This corresponds to Algorithm 3.2 of PDF Reference version 1.7.
      Parameters:
      userPassword - the desired user password; may be null or empty
      firstDocIdValue - the byte stream from the first element of the value of the ID entry in the trailer dictionary
      keyBitLength - the length of the key in bits
      revision - the security handler revision
      oValue - the O value from the Encrypt dictionary
      pValue - the P value from the Encrypt dictionary
      encryptMetadata - the EncryptMetadata entry from the Encrypt dictionary (or false if not present or revision <= 3)
      Returns:
      the general encryption key
      Throws:
      GeneralSecurityException - if an error occurs when obtaining and operating ciphers/digests
    • padPassword

      private byte[] padPassword(byte[] password)
      Pad a password as per step 1 of Algorithm 3.2 of the PDF Reference version 1.7
      Parameters:
      password - the password, may be null or empty
      Returns:
      the padded password, always 32 bytes long
    • crypt

      private byte[] crypt(Cipher cipher, byte[] input) throws IllegalBlockSizeException, BadPaddingException
      Encrypt some bytes
      Parameters:
      cipher - the cipher
      input - the plaintext
      Returns:
      the crypt text
      Throws:
      BadPaddingException - if there's bad padding
      IllegalBlockSizeException - if the block size is bad
    • initEncryption

      private void initEncryption(Cipher cipher, SecretKey key) throws InvalidKeyException
      Initialise a cipher for encryption
      Parameters:
      cipher - the cipher
      key - the encryption key
      Throws:
      InvalidKeyException - if the key is invalid for the cipher
    • rc4shuffle

      private void rc4shuffle(byte[] shuffle, byte[] key, Cipher rc4) throws GeneralSecurityException
      Shuffle some input using a series of RC4 encryptions with slight mutations of an given key per iteration. Shuffling happens in place. Refer to the documentation of the algorithm steps where this is called.
      Parameters:
      shuffle - the bytes to be shuffled
      key - the original key
      rc4 - the cipher to use
      Throws:
      GeneralSecurityException - if there's a problem with cipher operation
    • rc4unshuffle

      private void rc4unshuffle(Cipher rc4, byte[] shuffle, byte[] key) throws GeneralSecurityException
      Reverse the rc4shuffle(byte[], byte[], javax.crypto.Cipher) operation, and the operation that invariable preceeds it, thereby obtaining an original message
      Parameters:
      rc4 - the RC4 cipher to use
      shuffle - the bytes in which shuffling will take place; unshuffling happens in place
      key - the encryption key
      Throws:
      GeneralSecurityException - if there's a problem with cipher operation
    • cryptInPlace

      private void cryptInPlace(Cipher rc4, byte[] buffer) throws IllegalBlockSizeException, ShortBufferException, BadPaddingException
      Encrypt/decrypt something in place
      Parameters:
      rc4 - the cipher to use; must be a stream cipher producing identical output length to input (e.g., RC4)
      buffer - the buffer to read input from and write output to
      Throws:
      IllegalBlockSizeException - if an inappropriate cipher is used
      ShortBufferException - if an inappropriate cipher is used
      BadPaddingException - if an inappropriate cipher is used
    • initDecryption

      private void initDecryption(Cipher cipher, Key aKey) throws InvalidKeyException
      Setup a cipher for decryption
      Parameters:
      cipher - the cipher
      aKey - the cipher key
      Throws:
      InvalidKeyException - if the key is of an unacceptable size or doesn't belong to the cipher
    • createRC4Cipher

      private Cipher createRC4Cipher() throws NoSuchAlgorithmException, NoSuchPaddingException
      Create a new RC4 cipher. Should always be available for supported platforms.
      Returns:
      the cipher
      Throws:
      NoSuchAlgorithmException - if the RC4 cipher is unavailable
      NoSuchPaddingException - should not happen, as no padding is specified
    • createAESCipher

      private Cipher createAESCipher() throws NoSuchAlgorithmException, NoSuchPaddingException
      Create a new AES cipher. Should always be available for supported platforms.
      Returns:
      the new cipher
      Throws:
      NoSuchAlgorithmException - if the AES cipher is unavailable
      NoSuchPaddingException - if the required padding is unavailable
    • createMD5Digest

      private MessageDigest createMD5Digest() throws NoSuchAlgorithmException
      Create an MD5 digest. Should always be available for supported platforms.
      Returns:
      the MD5 digest
      Throws:
      NoSuchAlgorithmException - if the digest is not available
    • createRC4Key

      private SecretKeySpec createRC4Key(byte[] keyBytes)
      Create an RC4 key
      Parameters:
      keyBytes - the bytes for the key
      Returns:
      the key
    • digestTo

      private void digestTo(MessageDigest md5, byte[] hash) throws GeneralSecurityException
      Hash into an existing byte array
      Parameters:
      md5 - the MD5 digest
      hash - the hash destination
      Throws:
      GeneralSecurityException - if there's a problem hashing; e.g., if the buffer is too small