1 31 package org.pdfbox.encryption; 32 33 import java.io.ByteArrayInputStream ; 34 import java.io.ByteArrayOutputStream ; 35 import java.io.IOException ; 36 import java.io.InputStream ; 37 import java.math.BigInteger ; 38 import java.security.MessageDigest ; 39 import java.security.NoSuchAlgorithmException ; 40 import java.util.HashSet ; 41 import java.util.Iterator ; 42 import java.util.List ; 43 import java.util.Set ; 44 45 import org.pdfbox.cos.COSArray; 46 import org.pdfbox.cos.COSBase; 47 import org.pdfbox.cos.COSDictionary; 48 import org.pdfbox.cos.COSDocument; 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.exceptions.CryptographyException; 54 import org.pdfbox.exceptions.InvalidPasswordException; 55 import org.pdfbox.pdmodel.PDDocument; 56 import org.pdfbox.pdmodel.encryption.PDStandardEncryption; 57 58 68 public class DocumentEncryption 69 { 70 private PDDocument pdDocument = null; 71 private COSDocument document = null; 72 73 private byte[] encryptionKey = null; 74 private PDFEncryption encryption = new PDFEncryption(); 75 76 private Set objects = new HashSet (); 77 78 82 private Set potentialSignatures = new HashSet (); 83 84 89 public DocumentEncryption( PDDocument doc ) 90 { 91 pdDocument = doc; 92 document = doc.getDocument(); 93 } 94 95 100 public DocumentEncryption( COSDocument doc ) 101 { 102 pdDocument = new PDDocument( doc ); 103 document = doc; 104 } 105 106 113 public void initForEncryption() 114 throws CryptographyException, IOException 115 { 116 String ownerPassword = pdDocument.getOwnerPasswordForEncryption(); 117 String userPassword = pdDocument.getUserPasswordForEncryption(); 118 if( ownerPassword == null ) 119 { 120 ownerPassword = ""; 121 } 122 if( userPassword == null ) 123 { 124 userPassword = ""; 125 } 126 PDStandardEncryption encParameters = (PDStandardEncryption)pdDocument.getEncryptionDictionary(); 127 int permissionInt = encParameters.getPermissions(); 128 int revision = encParameters.getRevision(); 129 int length = encParameters.getLength()/8; 130 COSArray idArray = document.getDocumentID(); 131 132 if( idArray == null || idArray.size() < 2 ) 135 { 136 idArray = new COSArray(); 137 try 138 { 139 MessageDigest md = MessageDigest.getInstance( "MD5" ); 140 BigInteger time = BigInteger.valueOf( System.currentTimeMillis() ); 141 md.update( time.toByteArray() ); 142 md.update( ownerPassword.getBytes() ); 143 md.update( userPassword.getBytes() ); 144 md.update( document.toString().getBytes() ); 145 byte[] id = md.digest( this.toString().getBytes() ); 146 COSString idString = new COSString(); 147 idString.append( id ); 148 idArray.add( idString ); 149 idArray.add( idString ); 150 document.setDocumentID( idArray ); 151 } 152 catch( NoSuchAlgorithmException e ) 153 { 154 throw new CryptographyException( e ); 155 } 156 157 } 158 COSString id = (COSString)idArray.getObject( 0 ); 159 encryption = new PDFEncryption(); 160 161 byte[] o = encryption.computeOwnerPassword( 162 ownerPassword.getBytes("ISO-8859-1"), 163 userPassword.getBytes("ISO-8859-1"), revision, length); 164 165 byte[] u = encryption.computeUserPassword( 166 userPassword.getBytes("ISO-8859-1"), 167 o, permissionInt, id.getBytes(), revision, length); 168 169 encryptionKey = encryption.computeEncryptedKey( 170 userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length); 171 172 encParameters.setOwnerKey( o ); 173 encParameters.setUserKey( u ); 174 175 document.setEncryptionDictionary( encParameters.getCOSDictionary() ); 176 } 177 178 179 180 189 public void decryptDocument( String password ) 190 throws CryptographyException, IOException , InvalidPasswordException 191 { 192 if( password == null ) 193 { 194 password = ""; 195 } 196 197 PDStandardEncryption encParameters = (PDStandardEncryption)pdDocument.getEncryptionDictionary(); 198 199 200 int permissions = encParameters.getPermissions(); 201 int revision = encParameters.getRevision(); 202 int length = encParameters.getLength()/8; 203 204 COSString id = (COSString)document.getDocumentID().getObject( 0 ); 205 byte[] u = encParameters.getUserKey(); 206 byte[] o = encParameters.getOwnerKey(); 207 208 boolean isUserPassword = 209 encryption.isUserPassword( password.getBytes(), u, 210 o, permissions, id.getBytes(), revision, length ); 211 boolean isOwnerPassword = 212 encryption.isOwnerPassword( password.getBytes(), u, 213 o, permissions, id.getBytes(), revision, length ); 214 215 if( isUserPassword ) 216 { 217 encryptionKey = 218 encryption.computeEncryptedKey( 219 password.getBytes(), o, 220 permissions, id.getBytes(), revision, length ); 221 } 222 else if( isOwnerPassword ) 223 { 224 byte[] computedUserPassword = 225 encryption.getUserPassword( 226 password.getBytes(), 227 o, 228 revision, 229 length ); 230 encryptionKey = 231 encryption.computeEncryptedKey( 232 computedUserPassword, o, 233 permissions, id.getBytes(), revision, length ); 234 } 235 else 236 { 237 throw new InvalidPasswordException( "Error: The supplied password does not match " + 238 "either the owner or user password in the document." ); 239 } 240 241 COSDictionary trailer = document.getTrailer(); 242 COSArray fields = (COSArray)trailer.getObjectFromPath( "Root/AcroForm/Fields" ); 243 244 if( fields != null ) 247 { 248 for( int i=0; i<fields.size(); i++ ) 249 { 250 COSDictionary field = (COSDictionary)fields.getObject( i ); 251 addDictionaryAndSubDictionary( potentialSignatures, field ); 252 } 253 } 254 255 List allObjects = document.getObjects(); 256 Iterator objectIter = allObjects.iterator(); 257 while( objectIter.hasNext() ) 258 { 259 decryptObject( (COSObject)objectIter.next() ); 260 } 261 document.setEncryptionDictionary( null ); 262 } 263 264 private void addDictionaryAndSubDictionary( Set set, COSDictionary dic ) 265 { 266 set.add( dic ); 267 COSArray kids = (COSArray)dic.getDictionaryObject( "Kids" ); 268 for( int i=0; kids != null && i<kids.size(); i++ ) 269 { 270 addDictionaryAndSubDictionary( set, (COSDictionary)kids.getObject( i ) ); 271 } 272 COSBase value = dic.getDictionaryObject( "V" ); 273 if( value instanceof COSDictionary ) 274 { 275 addDictionaryAndSubDictionary( set, (COSDictionary)value ); 276 } 277 } 278 279 287 private void decryptObject( COSObject object ) 288 throws CryptographyException, IOException 289 { 290 long objNum = object.getObjectNumber().intValue(); 291 long genNum = object.getGenerationNumber().intValue(); 292 COSBase base = object.getObject(); 293 decrypt( base, objNum, genNum ); 294 } 295 296 306 public void decrypt( Object obj, long objNum, long genNum ) 307 throws CryptographyException, IOException 308 { 309 if( !objects.contains( obj ) ) 310 { 311 objects.add( obj ); 312 313 if( obj instanceof COSString ) 314 { 315 decryptString( (COSString)obj, objNum, genNum ); 316 } 317 else if( obj instanceof COSStream ) 318 { 319 decryptStream( (COSStream)obj, objNum, genNum ); 320 } 321 else if( obj instanceof COSDictionary ) 322 { 323 decryptDictionary( (COSDictionary)obj, objNum, genNum ); 324 } 325 else if( obj instanceof COSArray ) 326 { 327 decryptArray( (COSArray)obj, objNum, genNum ); 328 } 329 } 330 } 331 332 342 private void decryptStream( COSStream stream, long objNum, long genNum ) 343 throws CryptographyException, IOException 344 { 345 decryptDictionary( stream, objNum, genNum ); 346 InputStream encryptedStream = stream.getFilteredStream(); 347 encryption.encryptData( objNum, 348 genNum, 349 encryptionKey, 350 encryptedStream, 351 stream.createFilteredStream() ); 352 } 353 354 364 private void decryptDictionary( COSDictionary dictionary, long objNum, long genNum ) 365 throws CryptographyException, IOException 366 { 367 Iterator keys = dictionary.keyList().iterator(); 368 while( keys.hasNext() ) 369 { 370 COSName key = (COSName)keys.next(); 371 Object value = dictionary.getItem( key ); 372 if( !(key.getName().equals( "Contents" ) && 375 value instanceof COSString && 376 potentialSignatures.contains( dictionary ))) 377 { 378 decrypt( value, objNum, genNum ); 379 } 380 } 381 } 382 383 393 private void decryptString( COSString string, long objNum, long genNum ) 394 throws CryptographyException, IOException 395 { 396 ByteArrayInputStream data = new ByteArrayInputStream ( string.getBytes() ); 397 ByteArrayOutputStream buffer = new ByteArrayOutputStream (); 398 encryption.encryptData( objNum, 399 genNum, 400 encryptionKey, 401 data, 402 buffer ); 403 string.reset(); 404 string.append( buffer.toByteArray() ); 405 } 406 407 417 private void decryptArray( COSArray array, long objNum, long genNum ) 418 throws CryptographyException, IOException 419 { 420 for( int i=0; i<array.size(); i++ ) 421 { 422 decrypt( array.get( i ), objNum, genNum ); 423 } 424 } 425 } | Popular Tags |