private byte[] ComputeUserPasswordValue(int keyLength, byte[] documentId, byte[] encryptionKey) { // Algorithm 3.5, Computing the encryption dictionary's U (user password) value // (2, 3) Join the fixed padding and the document identifier byte[] hash = new byte[PADDING_32.Length + documentId.Length]; Array.Copy(PADDING_32, 0, hash, 0, PADDING_32.Length); Array.Copy(documentId, 0, hash, PADDING_32.Length, documentId.Length); // (3) Hash using MD5 hash = _md5.ComputeHash(hash); // (4, 5) Iterate 20 times byte[] rc4Key = new byte[encryptionKey.Length]; for (int i = 0; i < 20; i++) { for (int j = 0; j < encryptionKey.Length; ++j) { rc4Key[j] = (byte)(encryptionKey[j] ^ i); } hash = RC4.Transform(rc4Key, hash); } // (6) First 16 bytes is the hash result and the remainder is zero's byte[] userKey = new byte[32]; Array.Copy(hash, 0, userKey, 0, 16); return(userKey); }
private byte[] ComputeOwnerPasswordValue(int keyLength, byte[] ownerKey) { // Algorithm 3.3, Computing the encryption dictionary's O (owner password) value // (1) Pad the owner password (use the pad because we do not care about a users password) byte[] ownerPasword = new byte[32]; Array.Copy(PADDING_32, ownerPasword, 32); // (2) Initialize the MD5 ownerPasword = _md5.ComputeHash(ownerPasword); // (3) Rehash 50 times int blockLength = keyLength / 8; byte[] block = new byte[blockLength]; Array.Copy(ownerPasword, 0, block, 0, block.Length); for (int i = 0; i < 50; i++) { Array.Copy(_md5.ComputeHash(block), 0, block, 0, block.Length); } // (4) RC4 key byte[] rc4Key = new byte[blockLength]; // (5) Pad the user password byte[] userPassword = new byte[32]; Array.Copy(PADDING_32, userPassword, 32); // (6, 7) Iterate 20 times for (int i = 0; i < 20; i++) { for (int j = 0; j < blockLength; ++j) { rc4Key[j] = (byte)(block[j] ^ i); } ownerKey = RC4.Transform(rc4Key, ownerKey); } return(ownerKey); }
private byte[] DecodeBytes(PdfObject obj, byte[] bytes) { PdfIndirectObject indirectObject = obj.TypedParent <PdfIndirectObject>(); if (indirectObject == null) { throw new ApplicationException($"Cannot decrypt a string that is not inside an indirect object."); } // Create bytes that need hashing by combining the encryption key with the indirect object numbers byte[] key = new byte[_encryptionKey.Length + 5]; Array.Copy(_encryptionKey, 0, key, 0, _encryptionKey.Length); int index = _encryptionKey.Length; int id = indirectObject.Id; key[index++] = (byte)(id >> 0); key[index++] = (byte)(id >> 8); key[index++] = (byte)(id >> 16); int gen = indirectObject.Gen; key[index++] = (byte)(gen >> 0); key[index++] = (byte)(gen >> 8); // MD5 hash the bytes to get raw decrypt key key = _md5.ComputeHash(key); // Limit check the decrypt key length int keyLength = _encryptionKey.Length + 5; if (keyLength > 16) { keyLength = 16; } // Create the RC4 key byte[] rc4Key = new Byte[keyLength]; Array.Copy(key, 0, rc4Key, 0, keyLength); return(RC4.Transform(rc4Key, bytes)); }