⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 standardsecurityhandler.java

📁 非常有用的操作pdf文件的java源码
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/**
 * Copyright (c) 2003-2006, www.pdfbox.org
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of pdfbox; nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * http://www.pdfbox.org
 *
 */
package org.pdfbox.pdmodel.encryption;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.pdfbox.cos.COSArray;
import org.pdfbox.cos.COSString;
import org.pdfbox.encryption.ARCFour;
import org.pdfbox.exceptions.CryptographyException;
import org.pdfbox.pdmodel.PDDocument;

/**
 * 
 * The class implements the standard security handler as decribed
 * in the PDF specifications. This security handler protects document
 * with password.
 * 
 * @see StandardProtectionPolicy to see how to protect document with this security handler.
 * 
 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
 * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr)
 *
 * @version $Revision: 1.3 $
 */

public class StandardSecurityHandler extends SecurityHandler 
{
    /**
     * Type of security handler.
     */
    public static final String FILTER = "Standard";
    
    private static final int DEFAULT_VERSION = 1;
    
    private static final int DEFAULT_REVISION = 3;
    
    private int revision = DEFAULT_REVISION;
    
    private StandardProtectionPolicy policy;
    
    private ARCFour rc4 = new ARCFour();
    
    /**
     * Protection policy class for this handler.
     */
    public static final Class PROTECTION_POLICY_CLASS = StandardProtectionPolicy.class;
    
    /**
     * Standard padding for encryption.
     */
    public static final byte[] ENCRYPT_PADDING =
    {
        (byte)0x28, (byte)0xBF, (byte)0x4E, (byte)0x5E, (byte)0x4E,
        (byte)0x75, (byte)0x8A, (byte)0x41, (byte)0x64, (byte)0x00,
        (byte)0x4E, (byte)0x56, (byte)0xFF, (byte)0xFA, (byte)0x01,
        (byte)0x08, (byte)0x2E, (byte)0x2E, (byte)0x00, (byte)0xB6,
        (byte)0xD0, (byte)0x68, (byte)0x3E, (byte)0x80, (byte)0x2F,
        (byte)0x0C, (byte)0xA9, (byte)0xFE, (byte)0x64, (byte)0x53,
        (byte)0x69, (byte)0x7A
    };
    
    /**
     * Constructor.
     */
    public StandardSecurityHandler()
    {        
    }
    
    /**
     * Constructor used for encryption.
     * 
     * @param p The protection policy.
     */    
    public StandardSecurityHandler(StandardProtectionPolicy p)
    {
        policy = p;
        keyLength = policy.getEncryptionKeyLength();
    }
    

    /**
     * Computes the version number of the StandardSecurityHandler 
     * regarding the encryption key length.
     * See PDF Spec 1.6 p 93
     *  
     * @return The computed cersion number.
     */    
    private int computeVersionNumber()
    {
        if(keyLength == 40)
        {
            return DEFAULT_VERSION;
        }
        return 2;        
    }
    
    /**
     * Computes the revision version of the StandardSecurityHandler to
     * use regarding the version number and the permissions bits set.
     * See PDF Spec 1.6 p98
     * 
     * @return The computed revision number.
     */
    private int computeRevisionNumber()
    {        
        if(version == 2 
            && !policy.getPermissions().canFillInForm()
            && !policy.getPermissions().canExtractForAccessibility() 
            && !policy.getPermissions().canPrintDegraded() )
        {
            return 2;
        }
        return 3;
    }
    
    /**
     * Decrypt the document.
     * 
     * @param doc The document to be decrypted.
     * @param decryptionMaterial Information used to decrypt the document.
     * 
     * @throws IOException If there is an error accessing data.
     * @throws CryptographyException If there is an error with decryption.
     */
    public void decryptDocument(PDDocument doc, DecryptionMaterial decryptionMaterial)
        throws CryptographyException, IOException
    {
        document = doc;
        
        PDEncryptionDictionary dictionary = document.getEncryptionDictionary();
        if(!(decryptionMaterial instanceof StandardDecryptionMaterial))
        {
            throw new CryptographyException("Provided decryption material is not compatible with the document");
        }
        
        StandardDecryptionMaterial material = (StandardDecryptionMaterial)decryptionMaterial;
        
        String password = material.getPassword();
        if(password == null)
        {
            password = "";
        }
        
        int dicPermissions = dictionary.getPermissions();
        int dicRevision = dictionary.getRevision();
        int dicLength = dictionary.getLength()/8;

        COSString id = (COSString)document.getDocument().getDocumentID().getObject( 0 );
        byte[] u = dictionary.getUserKey();
        byte[] o = dictionary.getOwnerKey();

        boolean isUserPassword = 
            isUserPassword( 
                password.getBytes(), 
                u, 
                o, 
                dicPermissions, 
                id.getBytes(), 
                dicRevision, 
                dicLength );
        boolean isOwnerPassword = 
            isOwnerPassword( 
                password.getBytes(), 
                u, 
                o, 
                dicPermissions, 
                id.getBytes(), 
                dicRevision, 
                dicLength );

        if( isUserPassword )
        {
            encryptionKey = 
                computeEncryptedKey(
                    password.getBytes(), 
                    o, 
                    dicPermissions, 
                    id.getBytes(), 
                    dicRevision, 
                    dicLength );
        }
        else if( isOwnerPassword )
        {
            byte[] computedUserPassword = getUserPassword(password.getBytes(),o,dicRevision,dicLength );
            encryptionKey = 
                computeEncryptedKey(
                    computedUserPassword, 
                    o, 
                    dicPermissions, 
                    id.getBytes(), 
                    dicRevision, 
                    dicLength );
        }
        else
        {
            throw new CryptographyException( 
                "Error: The supplied password does not match either the owner or user password in the document." );
        }
        
        this.proceedDecryption();
    }
    
    /**
     * Prepare document for encryption.
     * 
     * @param doc The documeent to encrypt.
     * 
     * @throws IOException If there is an error accessing data.
     * @throws CryptographyException If there is an error with decryption.
     */
    public void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException 
    {        
        document = doc;
        PDEncryptionDictionary encryptionDictionary = document.getEncryptionDictionary();
        if(encryptionDictionary == null)
        {
            encryptionDictionary = new PDEncryptionDictionary();    
        }
        version = computeVersionNumber();
        revision = computeRevisionNumber();
        encryptionDictionary.setFilter(FILTER);
        encryptionDictionary.setVersion(version);
        encryptionDictionary.setRevision(revision);
        encryptionDictionary.setLength(keyLength);
        
        String ownerPassword = policy.getOwnerPassword();
        String userPassword = policy.getUserPassword();
        if( ownerPassword == null )
        {
            ownerPassword = "";
        }
        if( userPassword == null )
        {
            userPassword = "";
        }
        
        int permissionInt = policy.getPermissions().getPermissionBytes();
        
        encryptionDictionary.setPermissions(permissionInt);
        
        int length = keyLength/8;
        
        COSArray idArray = document.getDocument().getDocumentID();

        //check if the document has an id yet.  If it does not then
        //generate one
        if( idArray == null || idArray.size() < 2 )
        {
            idArray = new COSArray();
            try
            {
                MessageDigest md = MessageDigest.getInstance( "MD5" );
                BigInteger time = BigInteger.valueOf( System.currentTimeMillis() );
                md.update( time.toByteArray() );
                md.update( ownerPassword.getBytes() );
                md.update( userPassword.getBytes() );
                md.update( document.getDocument().toString().getBytes() );
                byte[] id = md.digest( this.toString().getBytes() );
                COSString idString = new COSString();
                idString.append( id );
                idArray.add( idString );
                idArray.add( idString );
                document.getDocument().setDocumentID( idArray );
            }
            catch( NoSuchAlgorithmException e )
            {
                throw new CryptographyException( e );
            }
            catch(IOException e )
            {
                throw new CryptographyException( e );
            }
        }
        
        COSString id = (COSString)idArray.getObject( 0 );        
        
        byte[] o = computeOwnerPassword(
            ownerPassword.getBytes("ISO-8859-1"),
            userPassword.getBytes("ISO-8859-1"), revision, length);

        byte[] u = computeUserPassword(
            userPassword.getBytes("ISO-8859-1"),
            o, permissionInt, id.getBytes(), revision, length);

        encryptionKey = computeEncryptedKey(
            userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length);

        encryptionDictionary.setOwnerKey(o);
        encryptionDictionary.setUserKey(u);        
        
        document.setEncryptionDictionary( encryptionDictionary );
        document.getDocument().setEncryptionDictionary(encryptionDictionary.getCOSDictionary());
        
    }

    /**
     * Check for owner password.
     * 
     * @param ownerPassword The owner password.
     * @param u The u entry of the encryption dictionary.
     * @param o The o entry of the encryption dictionary.
     * @param permissions The set of permissions on the document.
     * @param id The document id.
     * @param encRevision The encryption algorithm revision.
     * @param length The encryption key length.
     * 
     * @return True If the ownerPassword param is the owner password.
     * 
     * @throws CryptographyException If there is an error during encryption.
     * @throws IOException If there is an error accessing data.
     */
    public final boolean isOwnerPassword(
            byte[] ownerPassword,
            byte[] u,
            byte[] o,
            int permissions,
            byte[] id,
            int encRevision,
            int length)
            throws CryptographyException, IOException
    {
        byte[] userPassword = getUserPassword( ownerPassword, o, encRevision, length );            
        return isUserPassword( userPassword, u, o, permissions, id, encRevision, length );
    }
    
    /**
     * Get the user password based on the owner password.
     * 
     * @param ownerPassword The plaintext owner password.
     * @param o The o entry of the encryption dictionary.
     * @param encRevision The encryption revision number.
     * @param length The key length.
     * 
     * @return The u entry of the encryption dictionary.
     * 
     * @throws CryptographyException If there is an error generating the user password.
     * @throws IOException If there is an error accessing data while generating the user password.
     */
    public final byte[] getUserPassword(
            byte[] ownerPassword,
            byte[] o,
            int encRevision,
            long length )
            throws CryptographyException, IOException
    {
        try
        {
            ByteArrayOutputStream result = new ByteArrayOutputStream();

            //3.3 STEP 1
            byte[] ownerPadded = truncateOrPad( ownerPassword );

            //3.3 STEP 2
            MessageDigest md = MessageDigest.getInstance( "MD5" );
            md.update( ownerPadded );
            byte[] digest = md.digest();

            //3.3 STEP 3
            if( encRevision == 3 || encRevision == 4 )
            {
                for( int i=0; i<50; i++ )
                {
                    md.reset();
                    md.update( digest );
                    digest = md.digest();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -