← All posts
How to Verify JWT Licenses in Python Compiled Binaries with Py2Native Pro

How to Verify JWT Licenses in Python Compiled Binaries with Py2Native Pro

python compiler cython code protection open source

How to Verify JWT Licenses in Python Compiled Binaries with Py2Native Pro

Verifying a JWT license inside a Python application that has been compiled to native machine code is one of the most reliable ways to protect proprietary software from casual piracy and tampering. But doing it without exposing the verification logic—or the public key—requires more than just a JWT library. Py2Native Pro bakes the verification code and the public key directly into your compiled binary, so license checks happen in native code that is significantly harder to inspect or modify than Python bytecode.

In this guide, you’ll learn exactly how to generate an EC P-256 keypair, sign a JWT license, declare the verification function with a .pxd file, and build your application with Py2Native Pro. We’ll also cover how to interpret verification results and handle edge cases.

Why JWT License Verification Matters for Compiled Python

Shipping plain .py files means anyone with access to the source can read, copy, or modify your code. Even if you compile your code with something like Cython, the license verification logic often remains in Python and can be patched, bypassed, or reverse-engineered relatively easily.

A JWT (JSON Web Token) license is a signed, tamper-evident document. The token contains a header, a payload with claims, and a signature. If an attacker changes even one character, the signature no longer matches and verification fails. This makes JWT a strong format for license enforcement—provided the verification code itself is not easy to modify.

The challenge: if the public key or the verification function stays in Python source or bytecode, an attacker might swap in a different public key or neuter the check. Py2Native Pro solves this by compiling your custom Python code into native machine code and embedding both the verification logic and the public key inside the executable. The private key never leaves your build machine.

What a JWT License Proof Looks Like

A JWT license generated by Py2Native Pro is a standard JWT with three Base64URL-encoded parts separated by dots:

  1. Header – typically {"alg":"ES256","typ":"JWT"}.
  2. Payload – contains vendor-defined claims, for example:
    • exp – expiration timestamp
    • sub – customer identifier or product name
    • features – allowed feature flags
  3. Signature – computed over the first two parts using your EC P-256 private key.

The signing process is handled by the Pro plugin:

uv run py2native keygen private.pem public.pem

This generates an EC P-256 keypair. Keep private.pem secure on your build machine; you’ll use it to sign licenses. The public.pem file is later embedded in the compiled binary for verification.

To sign a license, create a JSON payload and run:

uv run py2native sign --private private.pem payload.json license.dat

The resulting license.dat file contains the signed JWT. At build time, you pass both the license and the public key to Py2Native:

uv run py2native build --license license.dat --public public.pem main.py

The Pro plugin bakes the elliptic-key verification code and the public key into the executable. Note that only the public key is stored in the binary; the private key is never distributed. Moreover, the elliptic verification is implemented in compiled code, avoiding third-party libraries that could be patched or replaced.

Step-by-Step: Verifying a JWT License with Py2Native Pro

Here’s the complete workflow from key generation to runtime license check.

Prerequisites

Step 1: Generate an EC P-256 keypair

uv run py2native keygen private.pem public.pem

This creates private.pem (vendor-only) and public.pem (embedded in the binary). Store the private key in an offline, access-controlled location.

Step 2: Sign a JWT payload

Create a payload file, for example payload.json:

{
  "sub": "customer-123",
  "exp": 1787222400,
  "features": ["pro", "reports"]
}

Sign it:

uv run py2native sign --private private.pem payload.json license.dat

You can inspect the resulting token without exposing the private key:

uv run py2native show --public public.pem license.dat

This displays the JWT claims and verifies the signature against the public key.

Step 3: Declare the verification function with a .pxd file

For normal code compilation, Py2Native accepts plain Python with no Cython syntax. For Pro license verification, you include a single .pxd declaration file that tells the compiler how to call the built-in verification routine. The Pro plugin provides the native function; your .pxd file just declares it.

Create a file named license_verify.pxd with a declaration similar to:

cdef extern from "py2native_pro.h":
    int py2native_verify_license(const char* license_path, const char* public_key_path)

The exact function name and signature are supplied by the Pro plugin; consult its generated header after installation. This .pxd file is not a Cython module you compile manually—it only declares the external symbol for the build orchestrator.

Step 4: Call the verification function from your Python code

In your main.py (or any module that runs at startup), import the declared function and call it:

from license_verify cimport py2native_verify_license

def check_license():
    # license.dat and public.pem are embedded or expected at a known path
    result = py2native_verify_license(b"license.dat", b"public.pem")
    if result != 1:
        print("License invalid or expired.")
        exit(1)
    return True

The Pro plugin handles linking the native verification function into your executable, so you don’t write any C or Cython logic yourself.

Step 5: Build the application

Run the build command, passing the license and public key:

uv run py2native build --license license.dat --public public.pem main.py

The Pro plugin verifies that you have a valid license file, then bakes the verification code and public key into the final native executable (or shared library, if you also use --library). At runtime, your code calls the compiled verification function.

Interpreting Verification Results and Handling Edge Cases

The compiled verification function returns a boolean-like integer (usually 1 for valid, 0 for invalid). Here’s what to expect:

Clock skew can be an issue if the customer’s system clock is ahead of the expiration time. To mitigate, consider issuing licenses with a short grace period or using a relative expiration based on the first run time.

For products that have multiple editions or feature sets, include a features claim in the JWT payload and gate functionality accordingly in your Python code. The verification function only checks the signature and expiration; it’s up to your application to enforce feature-level permissions.

Finally, remember that third-party Python libraries are left unmodified and may still be monkey-patched. Py2Native protects your custom code, including the license verification logic, but it does not prevent an attacker from altering libraries that remain as Python source. Keep your core verification checks in compiled code.

Best Practices for JWT License Verification in Compiled Binaries

FAQ: JWT License Verification with Py2Native Pro

Q: Can I verify a JWT license in a Python compiled binary without Py2Native Pro?

A: Yes, you could manually implement JWT verification using a library like PyJWT and then compile your code with Cython or another tool. However, that requires writing Cython code, managing .pyx files, and ensuring the verification logic is not exposed. Py2Native Pro automates this by baking the verification code and public key into the executable, with no manual Cython steps.

Q: How does Py2Native Pro protect the JWT verification logic from reverse engineering?

A: The verification logic is compiled into native machine code, making it much harder to inspect or modify than Python bytecode. Additionally, the public key is embedded in the binary, and the elliptic key verification is handled with compiled code, avoiding third-party libraries that could be patched.

Q: What happens if the JWT license is expired or invalid?

A: Your Python code can call the verification function and receive a boolean result. You decide how to handle failure: show an error message, exit the application, or run in a limited mode. The compiled binary will not contain the private key, so an attacker cannot forge a valid license.

Q: Does Py2Native Pro support license verification on all platforms?

A: Yes, Py2Native Pro works on Windows, Linux, and macOS, and the verification code is compiled for the target platform. The same JWT license file can be verified across platforms as long as the public key matches.

Conclusion

Verifying JWT licenses inside compiled Python binaries is a powerful way to protect your revenue and intellectual property. Py2Native Pro removes the complexity of manual Cython configuration and native linking, giving you a zero-config path from plain Python to a protected executable with embedded JWT verification.

To get started, generate a keypair, sign a license, add the .pxd declaration and a single call to your code, then build with --license and --public. The result is a native binary that checks license validity without exposing the private key or the verification logic.

Ready to compile and protect your Python code? Explore Py2Native and start building secure, license-enforced binaries today.

Related posts

EU label: AI-generated content