Skip to content

Instantly share code, notes, and snippets.

@Ap0dexMe0
Created June 18, 2025 00:01
Show Gist options
  • Select an option

  • Save Ap0dexMe0/705aab66e0184835d9842b82ec081093 to your computer and use it in GitHub Desktop.

Select an option

Save Ap0dexMe0/705aab66e0184835d9842b82ec081093 to your computer and use it in GitHub Desktop.
Widevine Recover Client ID
import argparse
import base64
from pathlib import Path
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Util import Padding
from pywidevine.device import Device
from pywidevine.pssh import PSSH
from pywidevine.cdm import Cdm
from pywidevine.license_protocol_pb2 import (
SignedMessage, SignedDrmCertificate, DrmCertificate,
LicenseRequest, ClientIdentification
)
def main():
parser = argparse.ArgumentParser(description="Decrypt Widevine Client ID from a WVD file")
parser.add_argument("-f", "--file", required=True, help="Path to the Widevine Device (.wvd) file")
args = parser.parse_args()
device_wvd_path = Path(args.file)
if not device_wvd_path.exists():
raise FileNotFoundError(f"WVD file not found: {device_wvd_path}")
output_dir = Path("output") / device_wvd_path.stem
output_dir.mkdir(parents=True, exist_ok=True)
mitm_key = RSA.generate(2048)
service_cert_b64 = """CAUSwwUKvQIIAxIQ5US6QAvBDzfTtjb4tU/7QxiH8c+TBSKOAjCCAQoCggEBAObzvlu2hZRsapAPx4Aa4GUZj4/GjxgXUtBH4THSkM40x63wQeyVxlEEo1D/T1FkVM/S+tiKbJiIGaT0Yb5LTAHcJEhODB40TXlwPfcxBjJLfOkF3jP6wIlqbb6OPVkDi6KMTZ3EYL6BEFGfD1ag/LDsPxG6EZIn3k4S3ODcej6YSzG4TnGD0szj5m6uj/2azPZsWAlSNBRUejmP6Tiona7g5u6AWZz0MsgCiEvnxRHmTRee+LO6U4dswzF3Odr2XBPD/hIAtp0RX8JlcGazBS0GABMMo2qNfCiSiGdyl2xZJq4fq99LoVfCLNChkn1N2NIYLrStQHa35pgObvhwi7ECAwEAAToQdGVzdC5uZXRmbGl4LmNvbRKAA4TTLzJbDZaKfozb9vDv5qpW5A/DNL9gbnJJi/AIZB3QOW2veGmKT3xaKNQ4NSvo/EyfVlhc4ujd4QPrFgYztGLNrxeyRF0J8XzGOPsvv9Mc9uLHKfiZQuy21KZYWF7HNedJ4qpAe6gqZ6uq7Se7f2JbelzENX8rsTpppKvkgPRIKLspFwv0EJQLPWD1zjew2PjoGEwJYlKbSbHVcUNygplaGmPkUCBThDh7p/5Lx5ff2d/oPpIlFvhqntmfOfumt4i+ZL3fFaObvkjpQFVAajqmfipY0KAtiUYYJAJSbm2DnrqP7+DmO9hmRMm9uJkXC2MxbmeNtJHAHdbgKsqjLHDiqwk1JplFMoC9KNMp2pUNdX9TkcrtJoEDqIn3zX9p+itdt3a9mVFc7/ZL4xpraYdQvOwP5LmXj9galK3s+eQJ7bkX6cCi+2X+iBmCMx4R0XJ3/1gxiM5LiStibCnfInub1nNgJDojxFA3jH/IuUcblEf/5Y0s1SzokBnR8V0KbA=="""
service_cert_bytes = base64.b64decode(service_cert_b64)
signed_msg = SignedMessage()
signed_msg.ParseFromString(service_cert_bytes)
signed_drm = SignedDrmCertificate()
signed_drm.ParseFromString(signed_msg.msg)
drm_cert = DrmCertificate()
drm_cert.ParseFromString(signed_drm.drm_certificate)
drm_cert.public_key = mitm_key.public_key().export_key("DER")
signed_drm.drm_certificate = drm_cert.SerializeToString()
signed_msg.msg = signed_drm.SerializeToString()
patched_cert = signed_msg.SerializeToString()
device = Device.load(str(device_wvd_path))
cdm = Cdm.from_device(device)
session_id = cdm.open()
cdm.set_service_certificate(session_id, patched_cert)
pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")
challenge = cdm.get_license_challenge(session_id, pssh)
signed_message = SignedMessage()
signed_message.ParseFromString(challenge)
license_request = LicenseRequest()
license_request.ParseFromString(signed_message.msg)
enc_client_id = license_request.encrypted_client_id
print(f"πŸ” Encrypted Privacy Key Length: {len(enc_client_id.encrypted_privacy_key)} bytes")
expected_len = mitm_key.size_in_bytes()
if len(enc_client_id.encrypted_privacy_key) != expected_len:
raise ValueError(f"Encrypted Privacy Key mismatch: got {len(enc_client_id.encrypted_privacy_key)}, expected {expected_len}")
dec_privacy_key = PKCS1_OAEP.new(mitm_key).decrypt(enc_client_id.encrypted_privacy_key)
cipher = AES.new(dec_privacy_key, AES.MODE_CBC, enc_client_id.encrypted_client_id_iv)
dec_client_id_data = Padding.unpad(cipher.decrypt(enc_client_id.encrypted_client_id), 16)
client_id = ClientIdentification()
client_id.ParseFromString(dec_client_id_data)
print("\nβœ… Decrypted Client ID:")
print(client_id)
with open(output_dir / "client_id.bin", "wb") as f:
f.write(client_id.SerializeToString())
print(f"πŸ’Ύ Saved client_id.bin β†’ {output_dir / 'client_id.bin'}")
with open(output_dir / "mitm_private_key.pem", "wb") as f:
f.write(mitm_key.export_key("PEM"))
print(f"πŸ” Saved mitm_private_key.pem β†’ {output_dir / 'mitm_private_key.pem'}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment