This tool removes PDF permission restrictions (the Owner password) entirely in your browser. Files are never uploaded to a server.
unlock_core.js.PDF security is implemented by the Standard Security Handler. All encryption information lives in the file's Encryption Dictionary, referenced by /Encrypt <obj> 0 R in the trailer.
Encryption only affects two kinds of content:
/StmF)/StrF)Decryption must solve two problems:
| PDF version (compat.) | Cipher | Key length | Encryption dictionary |
|---|---|---|---|
| PDF 1.1 / 1.2 / 1.3 | RC4 | 40-bit | /V 1 /R 2 /Length 40 (no /CF) |
| PDF 1.4 / 1.5 | RC4 | 128-bit | /V 2 /R 3 /Length 128; or /V 4 /CFM /V2 /Length 128 |
| PDF 1.6 / 1.7 | AES (AES-CBC) | 128-bit | /V 4 /CFM /AESV2 /Length 128 |
| PDF 1.7+ (low security) | AES | 256-bit | /V 5 /R 5 /CFM /AESV3 /Length 256 (single SHA-256) |
| PDF 1.7+ (high security) | AES (AES-256 + PBKDF2) | 256-bit | /V 5 /R 6 /CFM /AESV3 /Length 256 (iterated hash) |
/V (algorithm version) selects the key derivation and cipher family; /R (revision) selects the password-check details;Length 40); RC4-128 is 16 bytes;| Field | Meaning | Example |
|---|---|---|
/Filter /Standard | Standard security handler | /Filter /Standard |
/V | Cipher version (1–5) | /V 5 |
/R | Revision (2–6) | /R 6 |
/Length | Key length in bits (classic schemes) | /Length 256 |
/O | Owner-password data (32B when V<5; 48B when V=5) | /O <e67f...> |
/U | User-password data (32B when V<5; 48B when V=5) | /U <542c...> |
/OE / /UE | V5: AES-encrypted Owner/User file key (32B) | /UE <7d3a...> |
/P | Permission bits (32-bit mask, includes flags and three fixed bits) | /P -3904 |
/CF /StdCF /CFM | Crypt filter: /V2 (RC4), /AESV2 (AES-128), /AESV3 (AES-256) | /CFM /AESV3 |
/StmF / /StrF | Filter for streams / strings (default /Identity = no encryption) | /StmF /StdCF |
/EncryptMetadata | Whether metadata is encrypted (affects key derivation for AES-128) | /EncryptMetadata false |
/Encrypt and parse /V /R /Length /O /U /P /CFM /StmF /StrF /UEMD5(pw+O+P+ID0), iterated 50× when R≥3); V5: Algorithm 2.B (R5 = one SHA-256, R6 = iterated AES-CBC hashing)/U; V5: Algorithm 11 checks /U and decrypts /UE to obtain the 32-byte file keyMD5(fileKey[:n]+salt)[:n+5] (AES-128 appends sAlT); V=5 uses the 32-byte file key directly(...) or hex <...>/Encrypt, shift offsets by the new (decrypted) lengths and fix the xref → download an unrestricted PDFFind /Encrypt <N> 0 R in the trailer, read the dictionary at the object offset, and extract by regex:
/V, /R, /CFM (/V2 → RC4, /AESV2 → AES-128, /AESV3 → AES-256)/Length, /P, /O, /U, /UE (V5)/StrF (missing or /Identity means strings are not encrypted)/O, /U and /UE may be either hex strings (<...>) or literal strings ((...)); the latter require unescaping (\(, \), \\, octal \ddd, etc.).
V1–V4 (classic encryption, Algorithm 2)
key = MD5( password(empty) + O[0:32] + P(4-byte little-endian) + ID[0] ) if R >= 3: repeat 50 times: key = MD5(key + first 3 bytes) take the first n bytes (n = Length/8; n capped at 16)
Note: for AES-128 (V4) with /EncryptMetadata false, append 4 extra bytes FF FF FF FF to the input. ID[0] comes from the trailer's /ID and participates in key derivation, so changing any byte of the file changes the key.
V5 (Algorithm 2.B)
key = SHA-256( password + U[32:40] + U[40:48] ) (a single hash — fast but brute-forceable)hash = SHA-256( password + salt + udata )
count = 0
loop:
count++
k1 = password + hash + udata
e = AES-128-CBC encrypt( key=hash[0:16], IV=hash[16:32], data=k1 repeated 64 times, no padding )
sel = (e[0]+...+e[15]) % 3 # picks the hash for this round
hash = SHA-256 / SHA-384 / SHA-512 ( e )
stop when count ≥ 64 and e[last] ≤ count-32
key = hash[0:32]
R6's stop condition and rotating hashes mean each derivation performs thousands of AES block encryptions and hashes. This is exactly why it is "high security" and brute-force resistant — and a major source of the tool's processing time.
MD5(empty + padding + ID[0]), then run 20 RC4 iterations with an incrementing index byte, and compare with /U. A match means the file opens with an empty password (the range this tool handles).U[0:32] == SHA-256( empty + U[32:40] );tmpKey = SHA-256( empty + U[40:48] );This step also finalises the file key (cross-checked byte-for-byte against pypdf for R5/R6 in the implementation).
Each object builds a salt from object number (3 bytes LE) + generation number (2 bytes LE):
| Cipher type | Object key |
|---|---|
V=1 (RC4-40, no /CF) | Extended key = fileKey + salt (spec note 1: with V=1, MD5 is replaced by RC4, and the extended key is used directly as the RC4 key); generator-compatible writers (e.g. pypdf) use the fallback MD5(fileKey+salt)[:n+5] |
| V≥2 RC4 | MD5( fileKey[:n] + salt )[: min(n+5, 16)] |
| V=4 AES-128 | Same, with 4 extra bytes sAlT appended to the salt |
| V=5 AES-256 | Use the 32-byte file key directly (no MD5, no object salt — Algorithm 3.1a) |
RC4-40 key selection was a recent fix: a single wrong derivation breaks every stream. The implementation uses "content validation + primary/fallback adaptation": it decrypts with the spec key first, and if the content check fails (the result is not valid FlateDecode/image data) it retries with the MD5 fallback key, letting string decryption follow the same conclusion.
78 9C, etc.) or a valid image encoding (JPX must start with FF 4F FF 51); otherwise the stream is treated as unencrypted and left as-is.crypto.subtle). Decryption shortens data, so the tool decrypts in descending object order and rebuilds byte-by-byte, shifting following bytes and xref offsets accordingly.Strings are encrypted only under "classic encryption (V<4)" or when the dictionary explicitly sets /StrF to something other than /Identity (otherwise they are skipped — which is why some files keep intact bookmarks). Ciphertext comes in two forms:
(...): with AES, IV + ciphertext is concatenated; after decryption the result is re-escaped into a safe literal; with RC4, decryption is length-preserving;<...>: decrypted by the same rules and written back as hex.The decrypted result must keep byte/text length consistency, and scanning continues after each replacement (adapting to shifted offsets).
/Encrypt reference from the trailer;| Method | V/R | File key | Object key | Stream decryption | Password check |
|---|---|---|---|---|---|
| RC4-40 | 1/2 | Alg. 2, 5 bytes | Extended key (spec) / MD5 fallback | RC4 length-preserving + validation | Alg. 6 (20 RC4 iterations) |
| RC4-128 | 2/3 or 4/CFM V2 | Alg. 2, 16 bytes (50 iterations when R3) | MD5+salt[:n+5] | RC4 length-preserving + validation | Alg. 6 |
| AES-128 | 4/AESV2 | Alg. 2 + EncryptMetadata fix-up | MD5+salt+sAlT | AES-CBC PKCS7 (Web Crypto) | Alg. 6 |
| AES-256 low | 5/R5 | Alg. 2.B: single SHA-256 | File key (32B) | AES-256-CBC (Web Crypto) | Alg. 11 |
| AES-256 high | 5/R6 | Alg. 2.B: iterated hash (AES-CBC + rotating hashes) | File key (32B) | AES-256-CBC (Web Crypto) | Alg. 11 |
Note: R6 iteration and /UE decryption require AES-CBC without padding, which Web Crypto does not provide. The core therefore ships a pure-JS AES block implementation (SBOX / key schedule / CBC chaining, verified against NIST test vectors) — the key to being a single self-contained file.
SUPPORTED
NOT SUPPORTED / REJECTED
PRIVACY All logic runs in local browser JavaScript; files never leave your machine and are never uploaded.