1 17 18 19 20 package org.apache.fop.pdf; 21 22 import java.io.InputStream ; 24 import java.io.OutputStream ; 25 import java.io.IOException ; 26 import java.security.MessageDigest ; 27 import java.security.NoSuchAlgorithmException ; 28 import java.security.InvalidKeyException ; 29 import javax.crypto.Cipher; 30 import javax.crypto.spec.SecretKeySpec; 31 import javax.crypto.CipherOutputStream; 32 import javax.crypto.IllegalBlockSizeException; 33 import javax.crypto.BadPaddingException; 34 import javax.crypto.NoSuchPaddingException; 35 36 import java.util.Random ; 37 38 42 public class PDFEncryptionJCE extends PDFObject implements PDFEncryption { 43 44 private class EncryptionFilter extends PDFFilter { 45 private PDFEncryptionJCE encryption; 46 private int number; 47 private int generation; 48 49 55 public EncryptionFilter(PDFEncryptionJCE encryption, 56 int number, int generation) { 57 super(); 58 this.encryption = encryption; 59 this.number = number; 60 this.generation = generation; 61 log.debug("new encryption filter for number " 62 + number + " and generation " + generation); 63 } 64 65 70 public String getName() { 71 return ""; 72 } 73 74 78 public String getDecodeParms() { 79 return null; 80 } 81 82 87 public byte[] encode(byte[] data) { 88 return encryption.encryptData(data, number, generation); 89 } 90 91 94 public void encode(InputStream in, OutputStream out, int length) 95 throws IOException { 96 byte[] buffer = new byte[length]; 97 in.read(buffer); 98 buffer = encode(buffer); 99 out.write(buffer); 100 } 101 102 105 public OutputStream applyFilter(OutputStream out) throws IOException { 106 return new CipherOutputStream(out, 107 encryption.initCipher(number, generation)); 108 } 109 110 } 111 112 private static final char [] PAD 113 = {0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 114 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 115 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 116 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A}; 117 118 119 public static final int PERMISSION_PRINT = 4; 120 121 public static final int PERMISSION_EDIT_CONTENT = 8; 122 123 public static final int PERMISSION_COPY_CONTENT = 16; 124 125 public static final int PERMISSION_EDIT_ANNOTATIONS = 32; 126 127 private MessageDigest digest = null; 129 private Random random = new Random (); 131 private PDFEncryptionParams params; 133 private byte[] fileID = null; 135 private byte[] encryptionKey = null; 136 private String dictionary = null; 137 138 143 public PDFEncryptionJCE(int objnum) { 144 145 super(); 146 setObjectNumber(objnum); 147 try { 148 digest = MessageDigest.getInstance("MD5"); 149 } catch (NoSuchAlgorithmException e) { 151 throw new UnsupportedOperationException (e.getMessage()); 152 154 } 155 } 156 157 163 public static PDFEncryption make(int objnum, PDFEncryptionParams params) { 164 PDFEncryptionJCE impl = new PDFEncryptionJCE(objnum); 165 impl.setParams(params); 166 impl.init(); 167 return impl; 168 } 169 170 171 175 public PDFEncryptionParams getParams() { 176 return this.params; 177 } 178 179 183 public void setParams(PDFEncryptionParams params) { 184 this.params = params; 185 } 186 187 189 private byte[] prepPassword(String password) { 190 byte[] obuffer = new byte[32]; 191 byte[] pbuffer = password.getBytes(); 192 193 int i = 0; 194 int j = 0; 195 196 while (i < obuffer.length && i < pbuffer.length) { 197 obuffer[i] = pbuffer[i]; 198 i++; 199 } 200 while (i < obuffer.length) { 201 obuffer[i++] = (byte) PAD[j++]; 202 } 203 204 return obuffer; 205 } 206 207 211 public byte[] getFileID() { 212 if (fileID == null) { 213 fileID = new byte[16]; 214 random.nextBytes(fileID); 215 } 216 217 return fileID; 218 } 219 220 225 public String getFileID(int index) { 226 if (index == 1) { 227 return PDFText.toHex(getFileID()); 228 } 229 230 byte[] id = new byte[16]; 231 random.nextBytes(id); 232 return PDFText.toHex(id); 233 } 234 235 private byte[] encryptWithKey(byte[] data, byte[] key) { 236 try { 237 final Cipher c = initCipher(key); 238 return c.doFinal(data); 239 } catch (IllegalBlockSizeException e) { 240 throw new IllegalStateException (e.getMessage()); 241 } catch (BadPaddingException e) { 242 throw new IllegalStateException (e.getMessage()); 243 } 244 } 245 246 private Cipher initCipher(byte[] key) { 247 try { 248 Cipher c = Cipher.getInstance("RC4"); 249 SecretKeySpec keyspec = new SecretKeySpec(key, "RC4"); 250 c.init(Cipher.ENCRYPT_MODE, keyspec); 251 return c; 252 } catch (InvalidKeyException e) { 253 throw new IllegalStateException (e.getMessage()); 254 } catch (NoSuchAlgorithmException e) { 255 throw new UnsupportedOperationException (e.getMessage()); 256 } catch (NoSuchPaddingException e) { 257 throw new UnsupportedOperationException (e.getMessage()); 258 } 259 } 260 261 private Cipher initCipher(int number, int generation) { 262 byte[] hash = calcHash(number, generation); 263 int size = hash.length; 264 hash = digest.digest(hash); 265 byte[] key = calcKey(hash, size); 266 return initCipher(key); 267 } 268 269 private byte[] encryptWithHash(byte[] data, byte[] hash, int size) { 270 hash = digest.digest(hash); 271 272 byte[] key = calcKey(hash, size); 273 274 return encryptWithKey(data, key); 275 } 276 277 private byte[] calcKey(byte[] hash, int size) { 278 byte[] key = new byte[size]; 279 280 for (int i = 0; i < size; i++) { 281 key[i] = hash[i]; 282 } 283 return key; 284 } 285 286 289 public void init() { 290 byte[] oValue; 292 if (params.getOwnerPassword().length() > 0) { 293 oValue = encryptWithHash( 294 prepPassword(params.getUserPassword()), 295 prepPassword(params.getOwnerPassword()), 5); 296 } else { 297 oValue = encryptWithHash( 298 prepPassword(params.getUserPassword()), 299 prepPassword(params.getUserPassword()), 5); 300 } 301 302 int permissions = -4; 304 305 if (!params.isAllowPrint()) { 306 permissions -= PERMISSION_PRINT; 307 } 308 if (!params.isAllowCopyContent()) { 309 permissions -= PERMISSION_COPY_CONTENT; 310 } 311 if (!params.isAllowEditContent()) { 312 permissions -= PERMISSION_EDIT_CONTENT; 313 } 314 if (!params.isAllowEditAnnotations()) { 315 permissions -= PERMISSION_EDIT_ANNOTATIONS; 316 } 317 318 digest.update(prepPassword(params.getUserPassword())); 320 digest.update(oValue); 321 digest.update((byte) (permissions >>> 0)); 322 digest.update((byte) (permissions >>> 8)); 323 digest.update((byte) (permissions >>> 16)); 324 digest.update((byte) (permissions >>> 24)); 325 digest.update(getFileID()); 326 327 byte [] hash = digest.digest(); 328 this.encryptionKey = new byte[5]; 329 330 for (int i = 0; i < 5; i++) { 331 this.encryptionKey[i] = hash[i]; 332 } 333 334 byte[] uValue = encryptWithKey(prepPassword(""), this.encryptionKey); 336 337 this.dictionary = getObjectID() 339 + "<< /Filter /Standard\n" 340 + "/V 1\n" 341 + "/R 2\n" 342 + "/Length 40\n" 343 + "/P " + permissions + "\n" 344 + "/O " + PDFText.toHex(oValue) + "\n" 345 + "/U " + PDFText.toHex(uValue) + "\n" 346 + ">>\n" 347 + "endobj\n"; 348 } 349 350 357 public byte[] encryptData(byte[] data, int number, int generation) { 358 if (this.encryptionKey == null) { 359 throw new IllegalStateException ("PDF Encryption has not been initialized"); 360 } 361 log.debug("encrypting with for " + number + " " + generation); 362 363 byte[] hash = calcHash(number, generation); 364 return encryptWithHash(data, hash, hash.length); 365 } 366 367 370 public byte[] encrypt(byte[] data, PDFObject refObj) { 371 return encryptData(data, refObj.getObjectNumber(), refObj.getGeneration()); 372 } 373 374 private byte[] calcHash(int number, int generation) { 375 byte[] hash = new byte[this.encryptionKey.length + 5]; 376 377 int i = 0; 378 while (i < this.encryptionKey.length) { 379 hash[i] = this.encryptionKey[i]; i++; 380 } 381 382 hash[i++] = (byte) (number >>> 0); 383 hash[i++] = (byte) (number >>> 8); 384 hash[i++] = (byte) (number >>> 16); 385 hash[i++] = (byte) (generation >>> 0); 386 hash[i++] = (byte) (generation >>> 8); 387 return hash; 388 } 389 390 396 public PDFFilter makeFilter(int number, int generation) { 397 return new EncryptionFilter(this, number, generation); 398 } 399 400 404 public void applyFilter(AbstractPDFStream stream) { 405 stream.getFilterList().addFilter( 406 this.makeFilter(stream.getObjectNumber(), stream.getGeneration())); 407 } 408 409 414 public byte[] toPDF() { 415 if (this.dictionary == null) { 416 throw new IllegalStateException ("PDF Encryption has not been initialized"); 417 } 418 419 return encode(this.dictionary); 420 } 421 422 425 public String getTrailerEntry() { 426 return "/Encrypt " + getObjectNumber() + " " 427 + getGeneration() + " R\n" 428 + "/ID[" + getFileID(1) + getFileID(2) + "]\n"; 429 } 430 } 431 | Popular Tags |