CEP: In-Circuit Debugging of a .zxp File

It’s not common knowledge, but you can package a CEP extension into a .zxp file and debug it ‘in-circuit’, without needing to set the CEP debug flag.

My trick is to not use the ZXPSignCmd to package up the .zxp file.

Last time I checked, as far as I could tell, the ZXPSignCmd app takes notice, and will omit the .debug file and will refuse to add it.

However, my new PluginInstaller is able to inject a .debug file into a packaged .zxp.

As it so turns out, that makes the installed extension debuggable, without setting the CEP debug flag.

You would not want to do this in a release version of your extension – but when it comes to diagnosing and debugging difficult-to-catch bugs on someone else’s workstation, this can be invaluable.

Below a little cookbook to demonstrate how you can go about this.

I’ve also made a ‘live video’ about it. The quality of the video is what it is, I think you can tell I am not interested in becoming a ‘Youtuber’. My main concern is the content, not the presentation.


First, download the CEPSparker project.


Click the green Code button and then click the option Download ZIP.

CEPSparker is a framework I made to help me generate ‘starter’ projects for CEP.

Important to note: CEPSparker is not a CEP extension project. It is a precursor to a CEP extension project.

`After downloading, if you’re on a Mac, you MUST right-click the initialSetupConfigApp.command file, select Open, then allow it to run by clicking the Open button.

This de-quarantines the other .command files so they can be double-clicked.

On Windows, expect some blue warning screens (click More Info and run anyway).

You will need to run the .bat files from the command line in an elevated Administrator CMD session; you can double-click sudo.bat to start such a session.

Next, double-click the SparkerConfig or SparkerConfig.exe application. For the demo, change the STARTERCODE dropdown menu and pick IFrameUIServer.

Click Generate.

This will generate a sample CEP project that has a ‘split personality’. The CEP panel is featureless, and only contains an <iframe>.

The content of this <iframe> is pulled from a web server.

The sample code demonstrates how such server-provided code can drive a user-interface inside a CEP panel, and interact with the local host app.

For the demo, we use a small local Node.js application to play the role of the ‘remote server’. The Generate will have created a lot of folders in the CEP project folder.

One of them is IFrameUIServer

Inside you find a little file server.js. You need to have Node.js installed; Google around if you don’t have it installed yet.

All you need to do is open a command line and navigate into this folder, then run

node server.js

and that will run a tiny web server on http://localhost:8001

Next, we will tweak the CRDT manifest for this CEP panel. The CRDT manifest (a file called CRDT_manifest.json) is used by PluginInstaller to determine what it needs to do to package the CEP panel.

Open CRDT_manifest.json in a text editor and change it so it reads:

"injectDebugFile": true,

You can leave the "productCode" entry set to CEPSparkerTest, for the sake of argument – we need this code for creating a product entry in PluginInstaller.

This tell the PluginInstaller to copy the debug file from the CEP Project into the ZXP and rename it to .debug.

If you inspect the debug file in a text editor, you see it configures the debugger to use port 8888:

<?xml version="1.0" encoding="UTF-8"?>
    <Extension Id="com.rorohiko.cepsparker.samplepanel">
            <Host Name="IDSN" Port="8888" /> 

Now run PluginInstaller and switch to developer mode (File – PluginInstaller ModePublisher/Developer). If you don’t have a registered account yet, you need to create one first: after switching to Developer mode, switch to the Accounts window.

Registration is free, but needs to be approved.

To register, you need to click New and fill in a terse bit of detail.

Once you click Register you’ll need to wait for a confirmation email, and after that, I will need to handle your registration request, and I will need to approve it first; if I don’t personally know you I’ll reach out via email.

Once you have a registered developer account, you can create a product entry in the Products window.

For this test, you can just fill in some random details. The only thing that needs to match is the product code which is CEPSparkerTest (unless you changed the "productCode" in CRDT_manifest.json – these names need to match for PluginInstaller to find the manifest).

Once you complete the new product entry, you can click Package… and navigate to the CEP project folder.

Once the packaging completes, you’ll find a .zxp file in the build subfolder.

For the demo, you can now install this .zxp file using a standard CEP installer (for example Anastasiy’s Extension Manager).

Make sure the Adobe CEP debug flag is turned off before trying this out if you want to verify that the debug flag has no influence on the debuggability of this .zxp

With the IFrameUIServer running, start InDesign and open the panel.

The panel should populate and show a UI that has a New Document button. This whole UI is served from the remote web server – if you look at your Node.js terminal window, you’ll see the http requests fly past.

Click New Document to create a new document.

Now start Chrome and access http://localhost:8888 – this will show you the familiar debug interface with two available sessions: one for the panel itself and a second one for the user interface inside the <iframe> on the panel.

Reach out to me if you find this kind of stuff helpful or interesting. I can help you kickstart your automation project and greatly reduce the time needed. In most of my projects, my role is to be a temporary member of the team, and help build the software, by managing, training, designing and coding, as needed.


Creative Developer Tools – status update

I’ve been working hard on Creative Developer Tools (CRDT).

My current focus is to implement a unified packager/installer/license management for third party Adobe scripts/extensions/plug-ins.

PluginInstaller handles the whole developer-to-user chain: packaging, downloading, installing, removing, updating…

I’ve just finished adding support for CEP. PluginInstaller now handles plain ExtendScript as well as CEP extensions. Next up is UXP.

PluginInstaller is part of the growing suite of Creative Developer Tools.

Unified Packager/Installer

PluginInstaller handles both packaging (developer side) and installing (user side) of third party enhancements to the Adobe Creative Cloud apps.

I’ve just released a new version of the PluginInstaller which combines support for plain ExtendScript (.jsx) as well as CEP panels (.zxp).

My plan is to further extend PluginInstaller so the same packager/installer will also handle UXP/UXPScript and C++ plug-ins.

PluginInstaller uses a unified package format (.tpkg) which combines a number of features:

  • Code signing
  • Code encryption
  • Licensing and activation
  • JSON meta-information in the .tpkg file header
  • Support multiple types of solutions (.jsx, .zxp…)

PluginInstaller can be downloaded here:


CEP: no more ZXPSignCmd

I’ve managed to fully integrate all the steps needed for .zxp code signing into CRDT/PluginInstaller.

As we all know, Adobe’s .zxp code signing is both required and ineffective – i.e. it’s just security theater. Just an additional hurdle that developers have to jump.

When packaging a CEP extension using PluginInstaller, it will automatically create and manage the necessary self-signed signing certificates during the packaging operation.

Separate of the Adobe code signing setup, CRDT/PluginInstaller implements a more generic code signing mechanism. This mechanism can handle all kind of project types, including plain ExtendScript script projects.

When PluginInstaller manages a CEP extension, it transparently manages two distinct ‘layers’ of code signing: one as imposed by Adobe, the second layer is handled by CRDT.

PluginInstaller will build both a standard .zxp as well as a .tpkg file when packaging a CEP extension.

The .zxp file is a courtesy file which is not needed nor used by CRDT. It allows the developer to opt for using existing installer tools (e.g. Anastasiy’s Extension Manager) instead of PluginInstaller.

Such .zxp is not protected by CRDT code signing, but it can still have access to the protective CRDT code encryption features as long as the CRDT_ES runtime is included into the package.

The PluginInstaller also handles downloading of .tpkg and handles management of activation files for commercial solutions that need licensing/demo configurations.

CRDT Encryption

CRDT uses two levels of encryption, for two different purposes: an outer level (public/private key) handles code signing, and an inner level (AES-256) handles code protection.

Code signing is there to protect the user: they can verify that the code they are about to run was created by someone they can trust.

Code encryption is there to protect the developer: it blocks malicious users from inspecting and copying the crucial code magic that makes a software solution work.

.tpkg files

.tpkg files are self-identifying. They are structured so they have a human/computer readable JSON header which contains useful meta-info about their content.

This also allows the use of partial downloads: we can download the first 1024 bytes of any .tpkg file and figure out what they are without having to download the whole file.

CRDT Code Signing

When a developer registers a developer account in PluginManager, it will use OpenSSL to automatically generate a public/private key pair and publish the public key in tgrg.net online registry. PluginInstaller handles key generation automatically and there is no cost to creating these keys.

The private key is only stored on the developer’s workstation and used to encrypt anything that gets packaged by the developer.

A package will only work when it is decrypted with the matching public developer key as retrieved from the registry.

The approach was chosen to avoid forcing developers to deal with expensive code-signing certificates.

When a developer creates a .tpkg, the file is encrypted using the developer’s private key.

Then, when the user downloads and installs the .tpkg, the file is decrypted using the developer’s public key as retrieved from the registry.

That way the user can make sure they know who the .tpkg was created by.

Code encryption

When a .tpkg file contains .jsx files, they are encrypted using AES-256.

CRDT_ES comes with a binary runtime in a .dylib/.dll. This runtime is loaded into the ExtendScript engine inside the host app, and it handles on-the-fly decryption and execution. This works for plain .jsx as well as for .jsx embedded inside a CEP solution/.zxp.

CRDT currently does not transparently handle encryption of other file types than .jsx (e.g. .js).

Encryption of .js files (as opposed to .jsx files) would currently not be a strong deterrent for hackers because the decrypted .js code could be inspected by way of a debugger, simply by putting a breakpoint after the decryption call.

.jsx is different: CRDT can execute the encrypted code straight from the CRDT runtime, which is binary C++ code, without making the code easily inspectable in an ExtendScript debugger. By judiciously using closures, the code can be made inaccessible to would-be snoopers that know how to use the ExtendScript debugger.

For .js I cannot do that (yet). I do plan to change this later, but I need to do some research and find Adobe documentation: I need to be able to embed and call a binary runtime from the embedded Node.js or UXP engines. I am eagerly awaiting the ability to call C++ code from a UXP script.

For the time being, if you have .js code that needs to be protected from prying eyes, stash it in a .jsx and call it from .js through CSInterface, or else use the ‘standard’ approach of uglifying the code.


CRDT encryption/decryption is also tied into the tracking/licensing/activation mechanisms. It allows developers to add code to decide whether their plugin/script is active/not active/in demo/paid for mode…

Depending on the situation, their extension can stop working, or be limited in features – whatever they want.

Payment Handling

Currently I don’t have payment handling integrated yet; but I plan to build sample integrations for PayPal and Stripe.

Developers can then decide to either use my integrations or create their own.

For the time being, when using CRDT for licensing, users need to pay the developer via a standard invoicing mechanism, after which can be emailed an activation file generated in PluginInstaller by the developer.

This is a manual process, which should be manageable for small developers.

Eventually this will all be fully automated.

CRDT also has support for sub-licensing, and can handle floating licenses to support licensing a developer’s solutions to larger companies.

Creative Developer Tools Progress

Creative Developer Tools (CRDT) is taking shape!

The ExtendScript version (CRDT_ES) is now quite functional and usable.

I’ve used one of my old ‘free’ ExtendScripts (SizeLabels) as a real-life test, and wrapped it up as a CRDT package, using a lot of the code-signing, encryption, licensing, trialware, installer… features of CRDT to make it into a ‘real’ project that could start generating some revenue.

A more in-depth developer perspective on how I used CRDT to repackage SizeLabels in this YouTube video:


CEP Extensions: Packaging/Code Signing/Encrypting in CRDT

The next subproject I’ll be tackling in CRDT are CEP extensions.

All the CRDT for ExtendScript features are already available in CEP extensions – so the monetization and other features offered by CRDT can already be integrated and used.

But there is more work to do.

The next goal is to reduce the friction related to code signing, packaging and encrypting CEP extensions

If all goes according to plan, PluginInstaller will handle both the packaging (by the developer) and installing (by the end-user) of CEP extensions.

It will transparently handle both the ‘mock’ code signing (CEP code signing) enforced by Adobe as part of its security theater, and will add a layer of ‘real’ code signing on top of that (CRDT code signing).

And of course, CRDT will provide a developer with the features they need to monetize their work.

CEP Code Signing – ucf.jar

PluginInstaller needs to handle the CEP code signing, so I’ve been doing some research, and I’ve discovered that the good old ucf.jar in the crusty old Adobe signingtoolkit can be made to work and can jpackage-d with a recent Java JDK.

I’ve got it working on my M2 Mac, and will now integrate it into PluginInstaller.

I am hopeful this will allow me to enhance PluginInstaller to transparently manage CEP code signing

PluginInstaller will then automatically create the self-signed key and sign your extension, making the creation of a package just a little bit easier.

CEP Extensions: Packaging/Code Signing/Encrypting

The aim is for PluginInstaller to transparently handle two types of code-signing, and a layer of encryption.

  • on one hand handle the Adobe-enforced CEP code signing
  • additionally, and independently, it will handle CRDT code signing, which depends on a registry of public keys to keep track of developers and their signatures.
  • it will also handle CRDT code encryption which is an AES-256-based encryption meant to replace JSXBIN.

CEP Code Signing

The whole bother with Adobe enforcing developers to sign CEP extensions is nothing but security theater. By making PluginInstaller handle this, I can remove another hurdle for CEP extension developers.

These signing keys are not verified in any which way by the Creative Cloud apps, so anyone can create their own key and sign anything and the Creative Cloud software will accept it, sight unseen.

Any hacker can claim to be anyone else, and create a self-signed key to ‘prove’ it to the Creative Cloud apps, and the Creative Cloud apps will happily take them at their word.

Nothing stops a malicious hacker from unzipping someone’s genuine .zxp, adding some malware to it, and re-package it with their own self-signed key.

CRDT Code Signing

CRDT code signing requires developers register their identity in a registry, so the PluginInstaller can verify their signature.

When a developer creates a developer account in PluginInstaller, a public/private key pair is automatically generated and the public key is published in the registry.

Developers retain their own private key locally, and PluginInstaller will use the private key to code-sign their scripts and data files when it packages the software.

CRDT Code Encryption

Additionally, CRDT also implements CRDT code encryption. This allows developers to protect their code from prying eyes. This is a replacement for JSXBIN as provided by ExtendScript.

What’s ahead

There is still a lot of work to be done on CRDT. Important things that are on my to-do list:

  • integrate payment processors like PayPal and Stripe. At the moment, payments are still handled manually.
  • handle packaging and installing of UXP plugins the same way ExtendScript and CEP Extensions are handled, so PluginInstaller can be a one-stop shop both for packaging (developerland) and installing (userland)