1 31 32 package org.pdfbox.pdmodel.encryption; 33 34 import java.io.ByteArrayInputStream ; 35 import java.io.ByteArrayOutputStream ; 36 import java.io.IOException ; 37 import java.io.InputStream ; 38 import java.io.OutputStream ; 39 import java.security.MessageDigest ; 40 import java.security.NoSuchAlgorithmException ; 41 import java.util.HashSet ; 42 import java.util.Iterator ; 43 import java.util.List ; 44 import java.util.Set ; 45 46 import org.pdfbox.cos.COSArray; 47 import org.pdfbox.cos.COSBase; 48 import org.pdfbox.cos.COSDictionary; 49 import org.pdfbox.cos.COSName; 50 import org.pdfbox.cos.COSObject; 51 import org.pdfbox.cos.COSStream; 52 import org.pdfbox.cos.COSString; 53 import org.pdfbox.encryption.ARCFour; 54 import org.pdfbox.exceptions.CryptographyException; 55 import org.pdfbox.pdmodel.PDDocument; 56 57 66 67 public abstract class SecurityHandler 68 { 69 70 73 74 private static final int DEFAULT_KEY_LENGTH = 40; 75 76 79 protected int version; 80 81 84 protected int keyLength = DEFAULT_KEY_LENGTH; 85 86 89 protected byte[] encryptionKey; 90 91 94 95 protected PDDocument document; 96 97 100 protected ARCFour rc4 = new ARCFour(); 101 102 private Set objects = new HashSet (); 103 104 private Set potentialSignatures = new HashSet (); 105 106 110 111 protected AccessPermission currentAccessPermission = null; 112 113 121 public abstract void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException ; 122 123 131 public abstract void decryptDocument(PDDocument doc, DecryptionMaterial mat) 132 throws CryptographyException, IOException ; 133 134 135 142 protected void proceedDecryption() throws IOException , CryptographyException 143 { 144 145 COSDictionary trailer = document.getDocument().getTrailer(); 146 COSArray fields = (COSArray)trailer.getObjectFromPath( "Root/AcroForm/Fields" ); 147 148 if( fields != null ) 151 { 152 for( int i=0; i<fields.size(); i++ ) 153 { 154 COSDictionary field = (COSDictionary)fields.getObject( i ); 155 addDictionaryAndSubDictionary( potentialSignatures, field ); 156 } 157 } 158 159 List allObjects = document.getDocument().getObjects(); 160 Iterator objectIter = allObjects.iterator(); 161 while( objectIter.hasNext() ) 162 { 163 decryptObject( (COSObject)objectIter.next() ); 164 } 165 document.setEncryptionDictionary( null ); 166 } 167 168 private void addDictionaryAndSubDictionary( Set set, COSDictionary dic ) 169 { 170 set.add( dic ); 171 COSArray kids = (COSArray)dic.getDictionaryObject( "Kids" ); 172 for( int i=0; kids != null && i<kids.size(); i++ ) 173 { 174 addDictionaryAndSubDictionary( set, (COSDictionary)kids.getObject( i ) ); 175 } 176 COSBase value = dic.getDictionaryObject( "V" ); 177 if( value instanceof COSDictionary ) 178 { 179 addDictionaryAndSubDictionary( set, (COSDictionary)value ); 180 } 181 } 182 183 184 195 public void encryptData(long objectNumber, long genNumber, InputStream data, OutputStream output) 196 throws CryptographyException, IOException 197 { 198 byte[] newKey = new byte[ encryptionKey.length + 5 ]; 199 System.arraycopy( encryptionKey, 0, newKey, 0, encryptionKey.length ); 200 204 newKey[newKey.length -5] = (byte)(objectNumber & 0xff); 206 newKey[newKey.length -4] = (byte)((objectNumber >> 8) & 0xff); 207 newKey[newKey.length -3] = (byte)((objectNumber >> 16) & 0xff); 208 newKey[newKey.length -2] = (byte)(genNumber & 0xff); 209 newKey[newKey.length -1] = (byte)((genNumber >> 8) & 0xff); 210 211 212 byte[] digestedKey = null; 214 try 215 { 216 MessageDigest md = MessageDigest.getInstance( "MD5" ); 217 digestedKey = md.digest( newKey ); 218 } 219 catch( NoSuchAlgorithmException e ) 220 { 221 throw new CryptographyException( e ); 222 } 223 224 int length = Math.min( newKey.length, 16 ); 226 byte[] finalKey = new byte[ length ]; 227 System.arraycopy( digestedKey, 0, finalKey, 0, length ); 228 229 rc4.setKey( finalKey ); 230 rc4.write( data, output ); 231 output.flush(); 232 233 } 234 235 236 244 private void decryptObject( COSObject object ) 245 throws CryptographyException, IOException 246 { 247 long objNum = object.getObjectNumber().intValue(); 248 long genNum = object.getGenerationNumber().intValue(); 249 COSBase base = object.getObject(); 250 decrypt( base, objNum, genNum ); 251 } 252 253 263 private void decrypt( Object obj, long objNum, long genNum ) 264 throws CryptographyException, IOException 265 { 266 if( !objects.contains( obj ) ) 267 { 268 objects.add( obj ); 269 270 if( obj instanceof COSString ) 271 { 272 decryptString( (COSString)obj, objNum, genNum ); 273 } 274 else if( obj instanceof COSStream ) 275 { 276 decryptStream( (COSStream)obj, objNum, genNum ); 277 } 278 else if( obj instanceof COSDictionary ) 279 { 280 decryptDictionary( (COSDictionary)obj, objNum, genNum ); 281 } 282 else if( obj instanceof COSArray ) 283 { 284 decryptArray( (COSArray)obj, objNum, genNum ); 285 } 286 } 287 } 288 289 299 public void decryptStream( COSStream stream, long objNum, long genNum ) 300 throws CryptographyException, IOException 301 { 302 decryptDictionary( stream, objNum, genNum ); 303 InputStream encryptedStream = stream.getFilteredStream(); 304 encryptData( objNum, 305 genNum, 306 encryptedStream, 307 stream.createFilteredStream() ); 308 } 309 310 320 private void decryptDictionary( COSDictionary dictionary, long objNum, long genNum ) 321 throws CryptographyException, IOException 322 { 323 Iterator keys = dictionary.keyList().iterator(); 324 while( keys.hasNext() ) 325 { 326 COSName key = (COSName)keys.next(); 327 Object value = dictionary.getItem( key ); 328 if( !(key.getName().equals( "Contents" ) && 331 value instanceof COSString && 332 potentialSignatures.contains( dictionary ))) 333 { 334 decrypt( value, objNum, genNum ); 335 } 336 } 337 } 338 339 349 public void decryptString( COSString string, long objNum, long genNum ) 350 throws CryptographyException, IOException 351 { 352 ByteArrayInputStream data = new ByteArrayInputStream ( string.getBytes() ); 353 ByteArrayOutputStream buffer = new ByteArrayOutputStream (); 354 encryptData( objNum, genNum, data, buffer ); 355 string.reset(); 356 string.append( buffer.toByteArray() ); 357 } 358 359 369 private void decryptArray( COSArray array, long objNum, long genNum ) 370 throws CryptographyException, IOException 371 { 372 for( int i=0; i<array.size(); i++ ) 373 { 374 decrypt( array.get( i ), objNum, genNum ); 375 } 376 } 377 378 383 public int getKeyLength() 384 { 385 return keyLength; 386 } 387 388 393 public void setKeyLength(int keyLen) 394 { 395 this.keyLength = keyLen; 396 } 397 398 404 public AccessPermission getCurrentAccessPermission() 405 { 406 return currentAccessPermission; 407 } 408 } | Popular Tags |