diff --git a/pywidevine/cdm.py b/pywidevine/cdm.py
index faacb1d..e3212cf 100644
--- a/pywidevine/cdm.py
+++ b/pywidevine/cdm.py
@@ -235,7 +235,7 @@ class Cdm:
     def get_license_challenge(
         self,
         session_id: bytes,
-        init_data: Union[Container, bytes, str],
+        pssh: PSSH,
         type_: Union[int, str] = LicenseType.STREAMING,
         privacy_mode: bool = True
     ) -> bytes:
@@ -244,8 +244,7 @@ class Cdm:
 
         Parameters:
             session_id: Session identifier.
-            init_data: Widevine Cenc Header (Init Data) or a Protection System Specific
-                Header Box to take the init data from.
+            pssh: PSSH Object to get the init data from.
             type_: Type of License you wish to exchange, often `STREAMING`. The `OFFLINE`
                 Licenses are for Offline licensing of Downloaded content.
             privacy_mode: Encrypt the Client ID using the Privacy Certificate. If the
@@ -265,12 +264,10 @@ class Cdm:
         if not session:
             raise InvalidSession(f"Session identifier {session_id!r} is invalid.")
 
-        if not init_data:
-            raise InvalidInitData("The init_data must not be empty.")
-        try:
-            init_data = PSSH(init_data).init_data
-        except (ValueError, binascii.Error, DecodeError) as e:
-            raise InvalidInitData(str(e))
+        if not pssh:
+            raise InvalidInitData("A pssh must be provided.")
+        if not isinstance(pssh, PSSH):
+            raise InvalidInitData(f"Expected pssh to be a {PSSH}, not {pssh!r}")
 
         try:
             if isinstance(type_, int):
@@ -290,7 +287,9 @@ class Cdm:
         license_request.protocol_version = ProtocolVersion.Value("VERSION_2_1")
         license_request.key_control_nonce = random.randrange(1, 2 ** 31)
 
-        license_request.content_id.widevine_pssh_data.pssh_data.append(init_data)
+        # pssh_data may be either a WidevineCencHeader or custom data
+        # we have to assume the pssh.init_data value is valid, we cannot test
+        license_request.content_id.widevine_pssh_data.pssh_data.append(pssh.init_data)
         license_request.content_id.widevine_pssh_data.license_type = type_
         license_request.content_id.widevine_pssh_data.request_id = request_id
 
diff --git a/pywidevine/main.py b/pywidevine/main.py
index f52b3be..614d673 100644
--- a/pywidevine/main.py
+++ b/pywidevine/main.py
@@ -13,6 +13,7 @@ from pywidevine import __version__
 from pywidevine.cdm import Cdm
 from pywidevine.device import Device
 from pywidevine.license_protocol_pb2 import LicenseType, FileHashes
+from pywidevine.pssh import PSSH
 
 
 @click.group(invoke_without_command=True)
@@ -62,6 +63,9 @@ def license_(device: Path, pssh: str, server: str, type_: str, privacy: bool):
     """
     log = logging.getLogger("license")
 
+    # prepare pssh
+    pssh = PSSH(pssh)
+
     # load device
     device = Device.load(device)
     log.info(f"[+] Loaded Device ({device.system_id} L{device.security_level})")
diff --git a/pywidevine/remotecdm.py b/pywidevine/remotecdm.py
index 3842902..f2df64a 100644
--- a/pywidevine/remotecdm.py
+++ b/pywidevine/remotecdm.py
@@ -153,7 +153,7 @@ class RemoteCdm(Cdm):
     def get_license_challenge(
         self,
         session_id: bytes,
-        init_data: Union[Container, bytes, str],
+        pssh: PSSH,
         type_: Union[int, str] = LicenseType.STREAMING,
         privacy_mode: bool = True
     ) -> bytes:
@@ -161,12 +161,10 @@ class RemoteCdm(Cdm):
         if not session:
             raise InvalidSession(f"Session identifier {session_id!r} is invalid.")
 
-        if not init_data:
-            raise InvalidInitData("The init_data must not be empty.")
-        try:
-            init_data = PSSH(init_data).init_data
-        except (ValueError, binascii.Error, DecodeError) as e:
-            raise InvalidInitData(str(e))
+        if not pssh:
+            raise InvalidInitData("A pssh must be provided.")
+        if not isinstance(pssh, PSSH):
+            raise InvalidInitData(f"Expected pssh to be a {PSSH}, not {pssh!r}")
 
         try:
             if isinstance(type_, int):
@@ -184,7 +182,7 @@ class RemoteCdm(Cdm):
             url=f"{self.host}/{self.device_name}/challenge/{type_}",
             json={
                 "session_id": session_id.hex(),
-                "init_data": base64.b64encode(init_data).decode()
+                "init_data": pssh.dumps()
             }
         )
         if r.status_code != 200:
diff --git a/pywidevine/serve.py b/pywidevine/serve.py
index 6d47791..148011b 100644
--- a/pywidevine/serve.py
+++ b/pywidevine/serve.py
@@ -3,6 +3,8 @@ import sys
 from pathlib import Path
 from typing import Optional, Union
 
+from pywidevine.pssh import PSSH
+
 try:
     from aiohttp import web
 except ImportError:
@@ -210,10 +212,13 @@ async def challenge(request: web.Request) -> web.Response:
             "message": "No Service Certificate set but Privacy Mode is Enforced."
         }, status=403)
 
+    # get init data
+    init_data = PSSH(body["init_data"])
+
     # get challenge
     license_request = cdm.get_license_challenge(
         session_id=session_id,
-        init_data=body["init_data"],
+        pssh=init_data,
         type_=LicenseType.Value(request.match_info["license_type"]),
         privacy_mode=True
     )