jeremias    2003/03/13 08:46:06

  Modified:    src/java/org/apache/fop/fo FOUserAgent.java
               src/java/org/apache/fop/pdf PDFDocument.java
               src/java/org/apache/fop/render/pdf PDFRenderer.java
  Added:       src/java/org/apache/fop/pdf PDFEncryptionParams.java
                        PDFEncryptionManager.java PDFEncryptionJCE.java
                        PDFEncryption.java
  Log:
  Added support for PDF encryption.
  Submitted by: Patrick C. Lankswert <[EMAIL PROTECTED]>
  
  Enhanced to be disabled automatically if JCE and/or necessary algorithms are 
unavailable.
  PDF encryption doesn't work, yet. If it's enabled Acrobat will show blank pages. 
Don't know why, yet. See separate mail.
  
  Revision  Changes    Path
  1.2       +21 -0     xml-fop/src/java/org/apache/fop/fo/FOUserAgent.java
  
  Index: FOUserAgent.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/fo/FOUserAgent.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FOUserAgent.java  11 Mar 2003 13:05:19 -0000      1.1
  +++ FOUserAgent.java  13 Mar 2003 16:46:02 -0000      1.2
  @@ -63,6 +63,7 @@
   import org.apache.avalon.framework.logger.Logger;
   
   // FOP
  +import org.apache.fop.pdf.PDFEncryptionParams;
   import org.apache.fop.render.XMLHandler;
   import org.apache.fop.render.RendererContext;
   
  @@ -92,6 +93,7 @@
       private Map defaults = new java.util.HashMap();
       private Map handlers = new java.util.HashMap();
       private String baseURL;
  +    private PDFEncryptionParams pdfEncryptionParams;
   
       /**
        * Sets the logger.
  @@ -132,6 +134,24 @@
       }
   
       /**
  +     * Returns the parameters for PDF encryption.
  +     * @return the PDF encryption parameters, null if not applicable
  +     */
  +    public PDFEncryptionParams getPDFEncryptionParams() {
  +        return pdfEncryptionParams;
  +    }
  +
  +    /**
  +     * Sets the parameters for PDF encryption.
  +     * @param pdfEncryptionParams the PDF encryption parameters, null to 
  +     * disable PDF encryption
  +     */
  +    public void setPDFEncryptionParams(PDFEncryptionParams pdfEncryptionParams) {
  +        this.pdfEncryptionParams = pdfEncryptionParams;
  +    }
  +    
  +    
  +    /**
        * Get an input stream for a reference.
        * Temporary solution until API better.
        * @param uri URI to access
  @@ -216,5 +236,6 @@
                       + "No handler defined for XML: " + namespace);
           }
       }
  +
   }
   
  
  
  
  1.2       +48 -11    xml-fop/src/java/org/apache/fop/pdf/PDFDocument.java
  
  Index: PDFDocument.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/pdf/PDFDocument.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- PDFDocument.java  11 Mar 2003 13:05:09 -0000      1.1
  +++ PDFDocument.java  13 Mar 2003 16:46:05 -0000      1.2
  @@ -159,6 +159,11 @@
       protected PDFResources resources;
   
       /**
  +     * the documents encryption, if exists
  +     */
  +    protected PDFEncryption encryption;
  +
  +    /**
        * the colorspace (0=RGB, 1=CMYK)
        */
       protected PDFColorSpace colorspace = new 
PDFColorSpace(PDFColorSpace.DEVICE_RGB);
  @@ -299,6 +304,30 @@
       }
   
       /**
  +     * Enables PDF encryption.
  +     * @param params The encryption parameters for the pdf file
  +     */
  +    public void setEncryption(PDFEncryptionParams params) {
  +        this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, 
params);
  +        if (encryption != null) {
  +            /[EMAIL PROTECTED] this cast is ugly. PDFObject should be transformed 
to an interface. */
  +            addTrailerObject((PDFObject)this.encryption);
  +        } else {
  +            System.out.println("PDF encryption is unavailable. PDF will be "
  +                + "generated without encryption.");
  +        }
  +    }
  +    
  +    
  +    /**
  +     * Indicates whether encryption is active for this PDF or not.
  +     * @return boolean True if encryption is active
  +     */
  +    public boolean isEncryptionActive() {
  +        return this.encryption != null;
  +    }
  +    
  +    /**
        * Make a /Catalog (Root) object. This object is written in
        * the trailer.
        *
  @@ -1696,7 +1725,7 @@
       }
   
       /**
  -     * make a stream object
  +     * Make a stream object
        *
        * @param type the type of stream to be created
        * @param add if true then the stream will be added immediately
  @@ -1704,13 +1733,13 @@
        */
       public PDFStream makeStream(String type, boolean add) {
   
  -        /*
  -         * create a PDFStream with the next object number and add it
  -         *
  -         * to the list of objects
  -         */
  +        // create a PDFStream with the next object number 
  +        // and add it to the list of objects
           PDFStream obj = new PDFStream(++this.objectcount);
           obj.addDefaultFilters(filterMap, type);
  +        if (isEncryptionActive()) {
  +            this.encryption.applyFilter(obj);
  +        }
   
           if (add) {
               this.objects.add(obj);
  @@ -1841,7 +1870,7 @@
        * @throws IOException if there is an exception writing to the output stream
        */
       public void outputHeader(OutputStream stream)
  -    throws IOException {
  +                throws IOException {
           this.position = 0;
   
           byte[] pdf = ("%PDF-" + PDF_VERSION + "\n").getBytes();
  @@ -1864,7 +1893,7 @@
        * @throws IOException if there is an exception writing to the output stream
        */
       public void outputTrailer(OutputStream stream)
  -    throws IOException {
  +                throws IOException {
           output(stream);
           for (int count = 0; count < trailerObjects.size(); count++) {
               PDFObject o = (PDFObject) trailerObjects.get(count);
  @@ -1876,13 +1905,21 @@
             by the table's length */
           this.position += outputXref(stream);
   
  +        // Determine existance of encryption dictionary
  +        String encryptEntry = "";
  +        if (this.encryption != null) {
  +            encryptEntry = this.encryption.getTrailerEntry();
  +        }
  +
           /* construct the trailer */
           String pdf = "trailer\n" + "<<\n"
                        + "/Size " + (this.objectcount + 1) + "\n"
                        + "/Root " + this.root.number + " "
  -                     + this.root.generation + " R\n" + "/Info "
  -                     + this.info.number + " " + this.info.generation
  -                     + " R\n" + ">>\n" + "startxref\n" + this.xref
  +                     + this.root.generation + " R\n" 
  +                     + "/Info " + this.info.number + " " 
  +                     + this.info.generation + " R\n" 
  +                     + encryptEntry
  +                     + ">>\n" + "startxref\n" + this.xref
                        + "\n" + "%%EOF\n";
   
           /* write the trailer */
  
  
  
  1.1                  xml-fop/src/java/org/apache/fop/pdf/PDFEncryptionParams.java
  
  Index: PDFEncryptionParams.java
  ===================================================================
  /*
   * $Id$
   * ============================================================================
   *                    The Apache Software License, Version 1.1
   * ============================================================================
   * 
   * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
   * 
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, 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. The end-user documentation included with the redistribution, if any, must
   *    include the following acknowledgment: "This product includes software
   *    developed by the Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself, if
   *    and wherever such third-party acknowledgments normally appear.
   * 
   * 4. The names "FOP" and "Apache Software Foundation" must not be used to
   *    endorse or promote products derived from this software without prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   * 
   * 5. Products derived from this software may not be called "Apache", nor may
   *    "Apache" appear in their name, without prior written permission of the
   *    Apache Software Foundation.
   * 
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
   * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
   * DING, 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.
   * ============================================================================
   * 
   * This software consists of voluntary contributions made by many individuals
   * on behalf of the Apache Software Foundation and was originally created by
   * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
   * Software Foundation, please see <http://www.apache.org/>.
   */ 
  package org.apache.fop.pdf;
  
  /**
   * This class holds the parameters for PDF encryption.
   */
  public class PDFEncryptionParams {
  
      private String userPassword = ""; //May not be null
      private String ownerPassword = ""; //May not be null
      private boolean allowPrint = true;
      private boolean allowCopyContent = true;
      private boolean allowEditContent = true;
      private boolean allowEditAnnotations = true;
      
      /**
       * Creates a new instance.
       * @param userPassword the user password
       * @param ownerPassword the owner password
       * @param allowPrint true if printing is allowed
       * @param allowCopyContent true if copying content is allowed
       * @param allowEditContent true if editing content is allowed
       * @param allowEditAnnotations true if editing annotations is allowed
       */
      public PDFEncryptionParams(String userPassword, String ownerPassword,
                                 boolean allowPrint,
                                 boolean allowCopyContent,
                                 boolean allowEditContent,
                                 boolean allowEditAnnotations) {
          setUserPassword(userPassword);
          setOwnerPassword(ownerPassword);
          setAllowPrint(allowPrint);
          setAllowCopyContent(allowCopyContent);
          setAllowEditContent(allowEditContent);
          setAllowEditAnnotations(allowEditAnnotations);
      }
      
      /**
       * Default constructor initializing to default values.
       */
      public PDFEncryptionParams() {
          //nop
      }
       
      /**
       * Indicates whether copying content is allowed.
       * @return true if copying is allowed
       */
      public boolean isAllowCopyContent() {
          return allowCopyContent;
      }
  
      /**
       * Indicates whether editing annotations is allowed.
       * @return true is editing annotations is allowed
       */
      public boolean isAllowEditAnnotations() {
          return allowEditAnnotations;
      }
  
      /**
       * Indicates whether editing content is allowed.
       * @return true if editing content is allowed
       */
      public boolean isAllowEditContent() {
          return allowEditContent;
      }
  
      /**
       * Indicates whether printing is allowed.
       * @return true if printing is allowed
       */
      public boolean isAllowPrint() {
          return allowPrint;
      }
  
      /**
       * Returns the owner password.
       * @return the owner password, an empty string if no password applies
       */
      public String getOwnerPassword() {
          return ownerPassword;
      }
  
      /**
       * Returns the user password.
       * @return the user password, an empty string if no password applies
       */
      public String getUserPassword() {
          return userPassword;
      }
  
      /**
       * Sets the permission for copying content.
       * @param allowCopyContent true if copying content is allowed
       */
      public void setAllowCopyContent(boolean allowCopyContent) {
          this.allowCopyContent = allowCopyContent;
      }
  
      /**
       * Sets the permission for editing annotations.
       * @param allowEditAnnotations true if editing annotations is allowed
       */
      public void setAllowEditAnnotations(boolean allowEditAnnotations) {
          this.allowEditAnnotations = allowEditAnnotations;
      }
  
      /**
       * Sets the permission for editing content.
       * @param allowEditContent true if editing annotations is allowed
       */
      public void setAllowEditContent(boolean allowEditContent) {
          this.allowEditContent = allowEditContent;
      }
  
      /**
       * Sets the persmission for printing.
       * @param allowPrint true if printing is allowed
       */
      public void setAllowPrint(boolean allowPrint) {
          this.allowPrint = allowPrint;
      }
  
      /**
       * Sets the owner password.
       * @param ownerPassword The owner password to set, null or an empty String
       * if no password is applicable
       */
      public void setOwnerPassword(String ownerPassword) {
          if (ownerPassword == null) {
              this.ownerPassword = "";
          } else {
              this.ownerPassword = ownerPassword;
          }
      }
  
      /**
       * Sets the user password.
       * @param userPassword The user password to set, null or an empty String
       * if no password is applicable
       */
      public void setUserPassword(String userPassword) {
          if (userPassword == null) {
              this.userPassword = "";
          } else {
              this.userPassword = userPassword;
          }
      }
  
  }
  
  
  
  1.1                  xml-fop/src/java/org/apache/fop/pdf/PDFEncryptionManager.java
  
  Index: PDFEncryptionManager.java
  ===================================================================
  /*
   * $Id$
   * ============================================================================
   *                    The Apache Software License, Version 1.1
   * ============================================================================
   * 
   * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
   * 
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, 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. The end-user documentation included with the redistribution, if any, must
   *    include the following acknowledgment: "This product includes software
   *    developed by the Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself, if
   *    and wherever such third-party acknowledgments normally appear.
   * 
   * 4. The names "FOP" and "Apache Software Foundation" must not be used to
   *    endorse or promote products derived from this software without prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   * 
   * 5. Products derived from this software may not be called "Apache", nor may
   *    "Apache" appear in their name, without prior written permission of the
   *    Apache Software Foundation.
   * 
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
   * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
   * DING, 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.
   * ============================================================================
   * 
   * This software consists of voluntary contributions made by many individuals
   * on behalf of the Apache Software Foundation and was originally created by
   * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
   * Software Foundation, please see <http://www.apache.org/>.
   */ 
  package org.apache.fop.pdf;
  
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.security.Provider;
  import java.security.Security;
  
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.fop.fo.FOUserAgent;
  
  /**
   * This class acts as a factory for PDF encryption support. It enables the
   * feature to be optional to FOP depending on the availability of JCE.
   */
  public class PDFEncryptionManager {
  
      /**
       * Indicates whether JCE is available.
       * @return boolean true if JCE is present
       */
      public static boolean isJCEAvailable() {
          try {
              Class clazz = Class.forName("javax.crypto.Cipher");
              return true;
          } catch (ClassNotFoundException e) {
              return false;
          }
      }
      
      /**
       * Checks whether the necessary algorithms are available.
       * @return boolean True if all necessary algorithms are present
       */
      public static boolean checkAvailableAlgorithms() {
          if (!isJCEAvailable()) {
              return false;
          } else {
              Provider[] providers;
              providers = Security.getProviders("Cipher.RC4");
              if (providers == null) {
                  return false;
              }
              providers = Security.getProviders("MessageDigest.MD5");
              if (providers == null) {
                  return false;
              }
              return true;
          }
      }
      
  
      /**
       * Sets up PDF encryption if PDF encryption is requested by registering
       * a <code>PDFEncryptionParams</code> object with the user agent and if
       * the necessary cryptographic support is available.
       * @param userAgent the user agent
       * @param pdf the PDF document to setup encryption for
       * @param log the logger to send warnings to
       */
      public static void setupPDFEncryption(FOUserAgent userAgent, 
                                            PDFDocument pdf,
                                            Logger log) {
          if (userAgent == null) {
              throw new NullPointerException("User agent must not be null");
          }
          if (pdf == null) {
              throw new NullPointerException("PDF document must not be null");
          }
          if (userAgent.getPDFEncryptionParams() != null) {
              if (!checkAvailableAlgorithms()) {
                  if (isJCEAvailable()) {
                      log.warn("PDF encryption has been requested, JCE is "
                              + "available but there's no "
                              + "JCE provider available that provides the "
                              + "necessary algorithms. The PDF won't be "
                              + "encrypted.");
                  } else {
                      log.warn("PDF encryption has been requested but JCE is "
                              + "unavailable! The PDF won't be encrypted.");
                  }
              }
              pdf.setEncryption(userAgent.getPDFEncryptionParams());
          }
      }
      
      /**
       * Creates a new PDFEncryption instance if PDF encryption is available.
       * @param objnum PDF object number
       * @param params PDF encryption parameters
       * @return PDFEncryption the newly created instance, null if PDF encryption
       * is unavailable.
       */
      public static PDFEncryption newInstance(int objnum, PDFEncryptionParams params) {
          try {
              Class clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
              Method makeMethod = clazz.getMethod("make", 
                          new Class[] {int.class, PDFEncryptionParams.class});
              Object obj = makeMethod.invoke(null, 
                          new Object[] {new Integer(objnum), params});
              return (PDFEncryption)obj;
          } catch (ClassNotFoundException e) {
              if (checkAvailableAlgorithms()) {
                  System.out.println("JCE and algorithms available, but the "
                      + "implementation class unavailable. Please do a full "
                      + "rebuild.");
              }
              return null;
          } catch (NoSuchMethodException e) {
              e.printStackTrace();
              return null;
          } catch (IllegalAccessException e) {
              e.printStackTrace();
              return null;
          } catch (InvocationTargetException e) {
              e.printStackTrace();
              return null;
          }
      }
      
  }
  
  
  
  1.1                  xml-fop/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
  
  Index: PDFEncryptionJCE.java
  ===================================================================
  /*
   * $Id: PDFEncryptionJCE.java,v 1.1.2.1 2003/03/05 18:58:15 pietsch Exp $
   * ============================================================================
   *                    The Apache Software License, Version 1.1
   * ============================================================================
   * 
   * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
   * 
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, 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. The end-user documentation included with the redistribution, if any, must
   *    include the following acknowledgment: "This product includes software
   *    developed by the Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself, if
   *    and wherever such third-party acknowledgments normally appear.
   * 
   * 4. The names "FOP" and "Apache Software Foundation" must not be used to
   *    endorse or promote products derived from this software without prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   * 
   * 5. Products derived from this software may not be called "Apache", nor may
   *    "Apache" appear in their name, without prior written permission of the
   *    Apache Software Foundation.
   * 
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
   * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
   * DING, 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.
   * ============================================================================
   * 
   * This software consists of voluntary contributions made by many individuals
   * on behalf of the Apache Software Foundation and was originally created by
   * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
   * Software Foundation, please see <http://www.apache.org/>.
   */ 
  package org.apache.fop.pdf;
  
  // Java
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.security.InvalidKeyException;
  import javax.crypto.Cipher;
  import javax.crypto.spec.SecretKeySpec;
  import javax.crypto.IllegalBlockSizeException;
  import javax.crypto.BadPaddingException;
  import javax.crypto.NoSuchPaddingException;
  
  import java.util.Random;
  
  /**
   * class representing a /Filter /Standard object.
   *
   */
  public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
      
      private class EncryptionFilter extends PDFFilter {
          private PDFEncryptionJCE encryption;
          private int number;
          private int generation;
  
          /** 
           * The constructor for the internal PDFEncryptionJCE filter
           * @param encryption The encryption object to use
           * @param number The number of the object to be encrypted
           * @param generation The generation of the object to be encrypted
           */        
          public EncryptionFilter(PDFEncryptionJCE encryption,
                                  int number, int generation) {
              super();
              this.encryption = encryption;
              this.number  = number;
              this.generation = generation;
          }
  
          /** 
           * Return a PDF string representation of the filter. In this
           * case no filter name is passed.
           * @return The filter name, blank in this case
           */
          public String getName() {
              return "";
          }
  
          /** 
           * Return a parameter dictionary for this filter, or null
           * @return The parameter dictionary. In this case, null.
           */
          public String getDecodeParms() {
              return null;
          }
  
          /** 
           * Encode the given data with the filter
           * @param data The data to be encrypted
           * @return The encrypted data
           */
          public byte[] encode(byte[] data) {
              return encryption.encryptData(data, number, generation);
          }
          
          /**
           * @see org.apache.fop.pdf.PDFFilter#encode(InputStream, OutputStream, int)
           */
          public void encode(InputStream in, OutputStream out, int length) 
                                                          throws IOException {
              byte[] buffer = new byte[length];
              in.read(buffer);
              buffer = encode(buffer);
              out.write(buffer);
          }
          
      }
  
      private static final char [] PAD = 
                                 { 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
                                   0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 
                                   0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 
                                   0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A };
      private static final char[] DIGITS = 
                                   {'0', '1', '2', '3', '4', '5', '6', '7',
                                    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
                                    
      /** Value of PRINT permission */                                  
      public static final int PERMISSION_PRINT            =  4;
      /** Value of content editting permission */    
      public static final int PERMISSION_EDIT_CONTENT     =  8;
      /** Value of content extraction permission */    
      public static final int PERMISSION_COPY_CONTENT     = 16;
      /** Value of annotation editting permission */    
      public static final int PERMISSION_EDIT_ANNOTATIONS = 32;
      
      // Encryption tools
      private MessageDigest digest = null;
      private Cipher cipher = null;
      private Random random = new Random();
      // Control attributes
      private PDFEncryptionParams params;
      // Output attributes
      private byte[] fileID = null;
      private byte[] encryptionKey = null;
      private String dictionary = null;
  
      /**
       * create a /Filter /Standard object.
       *
       * @param number the object's number
       */
      public PDFEncryptionJCE(int number) {
          /* generic creation of object */
          super(number);
          try {
              digest = MessageDigest.getInstance("MD5");
              cipher = Cipher.getInstance("RC4");
          } catch (NoSuchAlgorithmException e) {
              throw new UnsupportedOperationException(e.getMessage());
          } catch (NoSuchPaddingException e) {
              throw new UnsupportedOperationException(e.getMessage());
          }
      }
  
      /**
       * Local factory method.
       * @param objnum PDF object number for the encryption object
       * @param params PDF encryption parameters
       * @return PDFEncryption the newly created PDFEncryption object
       */
      public static PDFEncryption make(int objnum, PDFEncryptionParams params) {
          PDFEncryptionJCE impl = new PDFEncryptionJCE(objnum);
          impl.setParams(params);
          impl.init();
          return impl;
      }
  
  
      /**
       * Returns the encryption parameters.
       * @return the encryption parameters
       */
      public PDFEncryptionParams getParams() {
          return this.params;
      }
  
      /**
       * Sets the encryption parameters.
       * @param params The parameterss to set
       */
      public void setParams(PDFEncryptionParams params) {
          this.params = params;
      }
  
      // Internal procedures
      
      private byte[] prepPassword(String password) {
          byte[] obuffer = new byte[32];
          byte[] pbuffer = password.getBytes();
  
          int i = 0;
          int j = 0;
          
          while (i < obuffer.length && i < pbuffer.length) {
              obuffer[i] = pbuffer[i];
              i++;
          }
          while (i < obuffer.length) {
              obuffer[i++] = (byte) PAD[j++];
          }
  
          return obuffer;
      }
  
      private String toHex(byte[] value) {
          StringBuffer buffer = new StringBuffer();
          
          for (int i = 0; i < value.length; i++) {
              buffer.append(DIGITS[(value[i] >>> 4) & 0x0F]);
              buffer.append(DIGITS[value[i] & 0x0F]);
          }
          
          return buffer.toString();
      }
      
      /** 
       * Returns the document file ID
       * @return The file ID
       */    
      public byte[] getFileID() {
          if (fileID == null) {
              fileID = new byte[16];
              random.nextBytes(fileID);
          }
          
          return fileID;
      }
      
      /** 
       * This method returns the indexed file ID
       * @param index The index to access the file ID
       * @return The file ID
       */    
      public String getFileID(int index) {
          if (index == 1) {
              return toHex(getFileID());
          }
          
          byte[] id = new byte[16];
          random.nextBytes(id);
          return toHex(id);
      }
          
      private byte[] encryptWithKey(byte[] data, byte[] key) {
          try {
              SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
              cipher.init(Cipher.ENCRYPT_MODE, keyspec);
              return cipher.doFinal(data);
          } catch (IllegalBlockSizeException e) {
              throw new IllegalStateException(e.getMessage());
          } catch (BadPaddingException e) {
              throw new IllegalStateException(e.getMessage());
          } catch (InvalidKeyException e) {
              throw new IllegalStateException(e.getMessage());
          }
      }
      
      private byte[] encryptWithHash(byte[] data, byte[] hash, int size) {
          hash = digest.digest(hash);
          
          byte[] key = new byte[size];
  
          for (int i = 0; i < size; i++) {
              key[i] = hash[i];
          }
          
          return encryptWithKey(data, key);
      }
  
      /** 
       * This method initializes the encryption algorithms and values
       */    
      public void init() {
          // Generate the owner value
          byte[] oValue;
          if (params.getOwnerPassword().length() > 0) {
              oValue = encryptWithHash(
                      prepPassword(params.getUserPassword()), 
                      prepPassword(params.getOwnerPassword()), 5);
          } else {
              oValue = encryptWithHash(
                      prepPassword(params.getUserPassword()), 
                      prepPassword(params.getUserPassword()), 5);
          }
  
          // Generate permissions value
          int permissions = -4;
  
          if (!params.isAllowPrint()) {
              permissions -= PERMISSION_PRINT;
          }
          if (!params.isAllowCopyContent()) {
              permissions -= PERMISSION_COPY_CONTENT;
          }
          if (!params.isAllowEditContent()) {
              permissions -= PERMISSION_EDIT_CONTENT;
          }
          if (!params.isAllowEditAnnotations()) {
              permissions -= PERMISSION_EDIT_ANNOTATIONS;
          }
  
          // Create the encrption key
          digest.update(prepPassword(params.getUserPassword()));
          digest.update(oValue);
          digest.update((byte) (permissions >>> 0));
          digest.update((byte) (permissions >>> 8));
          digest.update((byte) (permissions >>> 16));
          digest.update((byte) (permissions >>> 24));
          digest.update(getFileID());
  
          byte [] hash = digest.digest();
          this.encryptionKey = new byte[5];
  
          for (int i = 0; i < 5; i++) {
              this.encryptionKey[i] = hash[i];
          }
          
          // Create the user value
          byte[] uValue = encryptWithKey(prepPassword(""), this.encryptionKey);
          
          // Create the dictionary
          this.dictionary = this.number + " " + this.generation
                          + " obj\n<< /Filter /Standard\n"
                          + "/V 1\n"
                          + "/R 2\n"
                          + "/Length 40\n"
                          + "/P "  + permissions + "\n"
                          + "/O <" + toHex(oValue) + ">\n"
                          + "/U <" + toHex(uValue) + ">\n"
                          + ">>\n"
                          + "endobj\n";
      }
  
      /** 
       * This method encrypts the passed data using the generated keys.
       * @param data The data to be encrypted
       * @param number The block number
       * @param generation The block generation
       * @return The encrypted data
       */    
      public byte[] encryptData(byte[] data, int number, int generation) {
          if (this.encryptionKey == null) {
              throw new IllegalStateException("PDF Encryption has not been 
initialized");
          }
          
          byte[] hash = new byte[this.encryptionKey.length + 5];
              
          int i = 0;
              
          while (i < this.encryptionKey.length) {
              hash[i] = this.encryptionKey[i]; i++;
          }
              
          hash[i++] = (byte) (number >>> 0);
          hash[i++] = (byte) (number >>> 8);
          hash[i++] = (byte) (number >>> 16);
          hash[i++] = (byte) (generation >>> 0);
          hash[i++] = (byte) (generation >>> 8);;
          
          return encryptWithHash(data, hash, hash.length);
      }
  
      /** 
       * Creates PDFFilter for the encryption object
       * @param number The object number
       * @param generation The objects generation
       * @return The resulting filter
       */    
      public PDFFilter makeFilter(int number, int generation) {
          return new EncryptionFilter(this, number, generation);
      }
  
      /**
       * Adds a PDFFilter to the PDFStream object
       * @param stream the stream to add an encryption filter to
       */    
      public void applyFilter(PDFStream stream) {
          stream.addFilter(this.makeFilter(stream.number, stream.generation));
      }
      
      /**
       * Represent the object in PDF
       *
       * @return the PDF
       */
      public byte[] toPDF() {
          if (this.dictionary == null) {
              throw new IllegalStateException("PDF Encryption has not been 
initialized");
          }
          
          try {
              return this.dictionary.getBytes(PDFDocument.ENCODING);
          } catch (UnsupportedEncodingException ue) {
              return this.dictionary.getBytes();
          }       
      }
  
      /**
       * @see org.apache.fop.pdf.PDFEncryption#getTrailerEntry()
       */
      public String getTrailerEntry() {
          return "/Encrypt " + number + " " 
                      + generation + " R\n"
                      + "/ID[<" + getFileID(1) + "><"
                      + getFileID(2) + ">]\n";
      }
  }
  
  
  
  1.1                  xml-fop/src/java/org/apache/fop/pdf/PDFEncryption.java
  
  Index: PDFEncryption.java
  ===================================================================
  /*
   * $Id$
   * ============================================================================
   *                    The Apache Software License, Version 1.1
   * ============================================================================
   * 
   * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
   * 
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, 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. The end-user documentation included with the redistribution, if any, must
   *    include the following acknowledgment: "This product includes software
   *    developed by the Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself, if
   *    and wherever such third-party acknowledgments normally appear.
   * 
   * 4. The names "FOP" and "Apache Software Foundation" must not be used to
   *    endorse or promote products derived from this software without prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   * 
   * 5. Products derived from this software may not be called "Apache", nor may
   *    "Apache" appear in their name, without prior written permission of the
   *    Apache Software Foundation.
   * 
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
   * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
   * DING, 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.
   * ============================================================================
   * 
   * This software consists of voluntary contributions made by many individuals
   * on behalf of the Apache Software Foundation and was originally created by
   * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
   * Software Foundation, please see <http://www.apache.org/>.
   */ 
  package org.apache.fop.pdf;
  
  /**
   * This interface defines the contract for classes implementing PDF encryption.
   */
  public interface PDFEncryption {
  
      /**
       * Returns the encryption parameters.
       * @return the encryption parameters
       */
      PDFEncryptionParams getParams();
  
      /**
       * Sets the encryption parameters.
       * @param params The parameterss to set
       */
      void setParams(PDFEncryptionParams params);
  
      /**
       * Adds a PDFFilter to the PDFStream object
       * @param stream the stream to add an encryption filter to
       */    
      void applyFilter(PDFStream stream);
   
      /**
       * Returns the trailer entry for encryption.
       * @return the trailer entry
       */
      String getTrailerEntry();
  }
  
  
  
  1.2       +8 -1      xml-fop/src/java/org/apache/fop/render/pdf/PDFRenderer.java
  
  Index: PDFRenderer.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/render/pdf/PDFRenderer.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- PDFRenderer.java  11 Mar 2003 13:05:13 -0000      1.1
  +++ PDFRenderer.java  13 Mar 2003 16:46:05 -0000      1.2
  @@ -79,6 +79,7 @@
   import org.apache.fop.fo.properties.BackgroundRepeat;
   import org.apache.fop.fonts.Font;
   import org.apache.fop.fonts.FontMetrics;
  +import org.apache.fop.pdf.PDFEncryptionManager;
   import org.apache.fop.pdf.PDFStream;
   import org.apache.fop.pdf.PDFDocument;
   import org.apache.fop.pdf.PDFInfo;
  @@ -240,6 +241,7 @@
        * @see 
org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
        */
       public void configure(Configuration conf) throws ConfigurationException {
  +        //PDF filters
           Configuration filters = conf.getChild("filterList");
           Configuration[] filt = filters.getChildren("value");
           List filterList = new java.util.ArrayList();
  @@ -250,6 +252,7 @@
   
           filterMap.put(PDFStream.DEFAULT_FILTER, filterList);
   
  +        //Font configuration
           Configuration[] font = conf.getChildren("font");
           for (int i = 0; i < font.length; i++) {
               Configuration[] triple = font[i].getChildren("font-triplet");
  @@ -271,6 +274,7 @@
               fontList.add(efi);
           }
   
  +
       }
   
       /**
  @@ -311,7 +315,10 @@
           this.pdfDoc = new PDFDocument(producer);
           this.pdfDoc.setCreator(creator);
           this.pdfDoc.setFilterMap(filterMap);
  -        pdfDoc.outputHeader(stream);
  +        this.pdfDoc.outputHeader(stream);
  +        
  +        //Setup encryption if necessary
  +        PDFEncryptionManager.setupPDFEncryption(userAgent, this.pdfDoc, 
getLogger());
       }
   
       /**
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to