JPM CSR Generation
Generate one or two CSR + private-key pairs (CAT, PROD, or both) for JPM Payments API onboarding. Follow the steps in order. Use AskUserQuestion for every multiple-choice question.
Step 1 — Pre-check
Run openssl version via Bash. If the command is not found, tell the user:
openssl is not on PATH. Install it and re-run this skill:
- Windows: use Git Bash (which bundles openssl) or install from https://slproweb.com/products/Win32OpenSSL.html
- macOS:
brew install openssl- Linux:
apt install openssl(or your distro's equivalent)
Then exit.
Step 2 — Which environment(s)?
Ask:
- Question: "Which environment(s) do you need a CSR for?"
- Header: "Environment"
- Options:
- "CAT (test) only"
- "PROD only"
- "Both — CAT and PROD"
JPM requires a separate CSR per environment with a different CN. If the user picks "Both", you'll run the generation twice with different CNs but otherwise identical subject fields.
Step 3 — Collect subject fields
For the first (or only) environment, prompt the user (free text), one at a time, for the six standard X.509 subject fields:
- C — Country code (exactly 2 letters, ISO 3166, e.g.
US) - ST — State/Province (e.g.
New York) - L — Locality/City (e.g.
New York) - O — Organization (e.g.
Acme Corp) - OU — Organizational Unit (e.g.
Payments) - CN — Common Name (must be unique per environment, e.g.
acme-corp-cat)
Validate before generating:
Cmust match^[A-Za-z]{2}$. If not, re-prompt. Uppercase it before use.- All six fields must be non-empty.
- Reject any field containing a newline or a
"character.
If the user chose "Both": after generating CAT, re-prompt for PROD using CAT's C/ST/L/O/OU as suggested defaults (the user can accept or change them) and require a new CN that differs from CAT's. If the user enters the same CN, re-prompt with a reminder that JPM requires distinct CNs.
Step 4 — Output directory
Ask for an output directory (free text). Suggest defaults:
- Single env:
./jpm-csr/<env>/(e.g../jpm-csr/cat/) - Both:
./jpm-csr/cat/and./jpm-csr/prod/
Validate the directory before using it — it is interpolated into shell commands in Step 5. Apply the same rules as the subject fields in Step 3, plus path-specific ones:
- Reject any value containing
",',`,$,;,&,|,<,>,\, a newline, or a tab. These break out of the quoting in Step 5 and execute arbitrary commands. - Reject a value starting with
-(it would be parsed as an openssl/mkdir flag). - Reject an empty value.
- On rejection, re-prompt with the reason. Do not sanitize silently and do not proceed with a value that failed — an unvalidated path here is a command-injection sink, not a cosmetic issue.
The directory is created in Step 5 if it doesn't exist.
Step 5 — Generate
Run these commands via Bash, once per environment. Substitute the collected values; uppercase C first.
5a. Create the output directory and an OpenSSL config file. Use a config file rather than -subj so values containing /, , or other special characters are handled safely.
mkdir -p "<out-dir>"
cat > "<out-dir>/csr.cnf" <<'EOF'
[req]
distinguished_name = dn
prompt = no
[dn]
C = <C>
ST = <ST>
L = <L>
O = <O>
OU = <OU>
CN = <CN>
EOF
Note: the <<'EOF' quoting prevents shell expansion inside the subject values. Write the literal values into the file — do not use shell variables.
5b. Generate the key and CSR.
openssl req -new \
-newkey rsa:2048 \
-nodes \
-keyout "<out-dir>/privateKey.key" \
-out "<out-dir>/certificateRequest.pem" \
-config "<out-dir>/csr.cnf"
5c. Lock down the key and remove the config file.
chmod 600 "<out-dir>/privateKey.key" 2>/dev/null || true
rm -f "<out-dir>/csr.cnf"
5d. Verify the subject is what the user asked for.
openssl req -in "<out-dir>/certificateRequest.pem" -noout -subject
Show that subject line to the user. If it doesn't match the collected fields, delete both output files and report the mismatch.
If any command fails, delete privateKey.key and certificateRequest.pem if they were created, show the OpenSSL error to the user, and exit. Do not retry automatically.
Step 6 — Protect the private key
After successful generation:
- Read
.gitignorein the project root. If a line equal to**/privateKey.keyis not present, append it (with a leading comment line# JPM private keys). If.gitignoredoesn't exist, create one with those two lines. - Show the user a summary per environment:
- CSR (send to RM):
<csr path> - Private key (keep secret, never commit, never email):
<key path> - Subject:
C=<C>, ST=<ST>, L=<L>, O=<O>, OU=<OU>, CN=<CN>
- CSR (send to RM):
Step 7 — Next steps
Tell the user:
Send the
certificateRequest.pemfile to your Relationship Manager. They will use it to create your Client ID and return a.pemcertificate plus yourclientId. Onboarding typically takes 24–48 hours.Keep
privateKey.keysafe. You'll reference it later in your OAuth config along with the.pemyour Relationship Manager sends back. Once you have all three (clientId, .pem, privateKey.key), run thejpm-integrations-get-startedskill to validate them.
If the user generated for both environments, remind them the two CSRs go in separate IDAHO requests (CAT and PROD).
Rules
- Never echo the contents of the generated private key — only its path.
- Never write subject fields, paths, or generated keys to a memory file.
- If openssl fails or validation rejects the inputs, exit cleanly — do not loop or auto-retry.
- The only files this skill creates are the CSR + key in the user's chosen output dir, and (if needed) a
.gitignoreline for**/privateKey.key. The temporarycsr.cnfmust be deleted in Step 5c. - Never pass user-supplied values through unquoted shell expansion; write them literally into the quoted here-doc.
- Validate the output directory (Step 4) with the same rigor as the subject fields before it reaches any Bash command. It is interpolated into
mkdir, the here-doc path, threeopensslflags, andrm -f.