Monday, July 21, 2025

Decrypting and Viewing AES-Encrypted PDFs in PeopleSoft

 If you’ve encrypted PDF files using AES-256 outside of PeopleSoft (e.g., via OpenSSL), and now need to decrypt and view them inside PeopleSoft—this PeopleCode-based solution lets you do that securely and efficiently.

This is especially useful when you want to keep files encrypted at rest but still allow authorized users to view them through the PeopleSoft UI, without manual decryption.


Encryption Setup Outside PeopleSoft

You encrypt the PDF like this using OpenSSL:


openssl enc -aes-256-ecb -in input.pdf -out output.pdf.aes -nosalt -K <64-char hex key>

To decrypt it manually for testing:


openssl enc -d -aes-256-ecb -in output.pdf.aes -out decrypted.pdf -nosalt -K <64-char hex key>

This assumes:

  • AES-256 in ECB mode

  • No salt

  • PKCS5 padding

  • A 32-byte key represented as 64 hex characters


The PeopleCode Solution

Here’s the full PeopleCode to decrypt the file, display it using ViewAttachment(), and then clean it up:


/* Decrypt and view AES-encrypted PDF inside PeopleSoft */ Local string &key = "Your32ByteAESKey1234567890123456"; /* Must be 32 chars */ Local string &fileName = "decrypted_a.pdf"; Local string &encryptedPath = "/tmp/secured_file.pdf.aes"; Local string &decryptedPath = "/tmp/" | &fileName; /* Step 1: Validate key length */ If Len(&key) <> 32 Then MessageBox(0, "", 0, 0, "Key must be exactly 32 characters for AES-256"); Return; End-If; /* Step 2: Initialize AES cipher using Java */ Local JavaObject &keyBytes = CreateJavaObject("java.lang.String", &key).getBytes(); Local JavaObject &secretKey = CreateJavaObject("javax.crypto.spec.SecretKeySpec", &keyBytes, "AES"); Local JavaObject &cipher = GetJavaClass("javax.crypto.Cipher").getInstance("AES/ECB/PKCS5Padding"); &cipher.init(GetJavaClass("javax.crypto.Cipher").DECRYPT_MODE, &secretKey); /* Step 3: Read encrypted file and write decrypted output */ Local JavaObject &fis = CreateJavaObject("java.io.FileInputStream", &encryptedPath); Local JavaObject &cis = CreateJavaObject("javax.crypto.CipherInputStream", &fis, &cipher); Local JavaObject &fos = CreateJavaObject("java.io.FileOutputStream", &decryptedPath); Local JavaObject &buffer = CreateJavaArray("byte[]", 1024); Local number &len; &total = 0; While True &len = &cis.read(&buffer); If &len = -1 Then Break; End-If; &fos.write(&buffer, 0, &len); &total = &total + &len; End-While; /* Step 4: Close all streams */ &cis.close(); &fos.close(); &fis.close(); /* Step 5: Show the decrypted file to the user */ &RET = ViewAttachment(URL.TEST, &fileName, &fileName); /* Step 6: Delete the decrypted file after viewing */ Local string &filePath = "/tmp/" | &fileName; Local File &f = GetFile(&filePath, "W", "A", %FilePath_Absolute); If &f.IsOpen Then &f.Delete(); End-If;

Key Considerations

  • The key must be exactly 32 characters long for AES-256.

  • The encryption must match the decryption logic: ECB mode, no salt, and PKCS5 padding.

  • This script decrypts the file to /tmp, shows it using ViewAttachment(), and then deletes the decrypted file to maintain security.



Handling Non-200 HTTP Responses in PeopleSoft Integration Broker

 In PeopleSoft, working with Integration Broker (IB) to make REST or SOAP service calls is pretty standard. But here's the thing—you can't just wrap everything in a simple Try-Catch and call it a day.

Why? Because the moment the target system returns a non-200 HTTP status—say a 400 Bad RequestPeopleSoft doesn’t raise an exception you can catch. Instead, the system treats it as a failure at the ResponseStatus level, but still gives you a chance to inspect the response.

So if you don’t handle that explicitly, you’re flying blind.


 Problem Statement

Let’s say you make a REST call to an external service that returns a 400 due to some client-side validation issue (e.g., missing field, invalid format).

In Postman, you'd see a well-structured JSON or XML body explaining the error.

But in PeopleCode?

If you don’t handle the response carefully, you'll just see that the SyncRequest() failed and your code crashes or logs a vague failure.


Solution: Don’t Just Use Try-Catch. Check ResponseStatus.

Make sure your routing tab User exception checkbox is checked.  ****IMPORTANT****



Here’s a reliable pattern:


Local Message &Response_Msg = %IntBroker.SyncRequest(&Request_Msg); If &Response_Msg.ResponseStatus = %IB_Status_Success Then /* Success - parse the content */ Local string &content = &Response_Msg.GetContentString(); MessageBox(0, "", 0, 0, "Raw: " | &content); Else /* Handle structured error */ Local number &MsgSet = &Response_Msg.IBException.MessageSetNumber; Local number &MsgNum = &Response_Msg.IBException.MessageNumber; Local string &MsgEx = &Response_Msg.IBException.ToString(); /* Extract raw fault response (JSON, XML, etc.) */ Local string &rawFault = &Response_Msg.GetContentString(); /* Log or display */ MessageBox(0, "", 0, 0, "Error " | &MsgSet | "-" | &MsgNum | ": " | &MsgEx); MessageBox(0, "", 0, 0, "Raw Fault: " | &rawFault); End-If;

Why GetContentString() Matters Even on Failure

The key insight: even when PeopleSoft sees the status as a failure, the response content (body) is still available.

This is critical when calling modern REST APIs. Most services return detailed JSON error bodies like:


{ "error": { "code": "InvalidRequest", "message": "The field 'email' is required." } }

Without grabbing GetContentString(), you lose all of this.


But Wait—Why Not Just Use Try-Catch?

Here’s the catch (no pun intended): Try-Catch in PeopleCode will not trap the HTTP 400-style errors unless the system throws an internal PeopleSoft exception (like a message catalog error, null pointer, etc.)

In the case of a REST response with a non-2xx status:

  • SyncRequest() still returns a valid Message object

  • You must inspect ResponseStatus manually

  • Try-Catch won’t help unless something else breaks


 Suggested Practice

Always wrap your IB call like this:

  1. Make the request

  2. Check ResponseStatus

  3. Use GetContentString() even on failure

  4. Log or parse the error for better diagnostics

  5. Use IBException to catch IB-side errors


 Bonus Tip: Log to a Custom Table

Instead of just using MessageBox, you can log everything to a custom error log table, like so:

SQLExec("INSERT INTO PS_MYLOG_TBL (OPRID, LOGTIME, MSG, RAWFAULT)
VALUES (:1, %CurrentDateTimeIn, :2, :3)", %OperatorId, &MsgEx, &rawFault);

Final Thoughts

If you're calling external APIs from PeopleSoft, expect failure. Not because your code is bad—but because that’s how real-world APIs work.

So don’t treat 400, 404, 500, etc. as fatal errors. Treat them as data. Read the response, log it, act on it.

And always remember: GetContentString() is your friend—even when the server isn't.


Let me know if you want to expand this with JSON parsing, XML fault extraction, or a reusable logging function.

Friday, June 20, 2025

🔐 How to Decode a JWT Token in PeopleCode Using Java (No Extra Libraries)

Use Case: Securely decode and inspect a JWT (JSON Web Token) in PeopleSoft using native Java classes, without relying on external libraries or PeopleCode JSON parsers.

 Why Decode a JWT in PeopleCode?

In modern PeopleSoft integrations — especially with OAuth 2.0, SSO (Single Sign-On), and Microsoft Azure AD authentication — JWTs (JSON Web Tokens) are used to pass identity and authorization information. For example:

  • A REST API request might carry a JWT in the Authorization header.

  • You might need to extract user identity (upn, email, roles) from the token.

  • You want to verify what claims (data) were issued in the JWT — without calling external tools.


PeopleCode doesn't have built-in JWT libraries or even a native Base64 URL decoder. But with a little help from standard Java classes (available in all PeopleTools environments), we can manually decode and read the JWT payload.


Use Case

Let’s say:

  • You have an OAuth-secured API call hitting PeopleSoft.

  • You’re intercepting the JWT from the %Request.GetHeader("Authorization") PeopleCode method.

  • You want to read the user identity inside the token for auditing or mapping purposes.


 Sample PeopleCode: Decode JWT Payload (Pure Java, No Parser Yet)


Local string &jwt, &payload, &decodedPayload; Local array of string &parts; Local number &mod4; Local JavaObject &decoder, &payloadBytes, &decodedBytes, &decodedString; /* Step 1: Build the JWT from pieces (used here for testing only) */ &part1 = ".."; &part2 = ".."; &part3 = ".."; &part4 = ".."; &part5 = ".."; &jwt = &part1 | &part2 | &part3 | &part4 | &part5; /* Step 2: Split the JWT */ &parts = Split(&jwt, "."); If &parts.Len >= 2 Then /* Step 3: Get payload part */ &payload = &parts[2]; /* Step 4: Normalize Base64URL */ &payload = Substitute(&payload, "-", "+"); &payload = Substitute(&payload, "_", "/"); /* Step 5: Add padding */ &mod4 = Mod(Len(&payload), 4); If &mod4 = 2 Then &payload = &payload | "=="; Else If &mod4 = 3 Then &payload = &payload | "="; End-If; End-If; /* Step 6: Base64 decode using Java */ &decoder = GetJavaClass("java.util.Base64").getDecoder(); &payloadBytes = CreateJavaObject("java.lang.String", &payload).getBytes(); &decodedBytes = &decoder.decode(&payloadBytes); &decodedString = CreateJavaObject("java.lang.String", &decodedBytes); &decodedPayload = &decodedString.toString(); /* Step 7: Display decoded JSON payload */ WinMessage("Decoded JWT Payload: " | &decodedPayload); Else WinMessage("Invalid JWT format."); End-If;

Friday, January 17, 2025

How to Convert CSV to XLSX in PeopleSoft Using the PSSpreadsheet Class

 Working with large datasets often requires transitioning data between formats. In PeopleSoft, you can efficiently convert a CSV file to an XLSX file using the PSSpreadsheet class. This guide will walk you through how to automate this conversion with a simple PeopleCode program.

Why Convert CSV to XLSX?

  • Better Data Handling: Excel handles large datasets more effectively than CSV, especially when working with formulas, charts, and formatting.

  • Improved Readability: XLSX allows for better organization with multiple sheets and styling options.

  • Integration with PeopleSoft: The PSSpreadsheet class makes it easy to programmatically generate Excel files.

Prerequisites

  • Access to PeopleSoft Application Designer

  • Basic knowledge of PeopleCode

  • Required permissions to run App Engine programs

Step-by-Step Implementation

1. Create an App Engine Program

Open PeopleSoft Application Designer and create a new App Engine program. Add a PeopleCode action where the conversion logic will be placed.

2. PeopleCode to Convert CSV to XLSX

 Run the App Engine Program

  1. Save and register the App Engine.

  2. Run it through the Process Scheduler or manually in PeopleSoft. Do not run in two tier app designer.

  3. The Excel file (Output.xlsx) will be generated in the defined directory.


/* Set the file paths */
/* Hardcoded Input CSV File Path /
&csvFileName = "<Path_to_dir>test_csv.csv"; /
Replace with actual CSV path */ /* Set Output Excel File Name */
&outFileName = "<Path_to_dir>Output.xlsx";/ *Replace with actual xlsx path */ /* Create Spreadsheet using PSSpreadsheet class */
Local object &ss;
&ss = CreateObject("PSSpreadsheet");
&ss.Open(&outFileName, True); /* Open the CSV File for Reading */
Local File &file;
&file = GetFile(&csvFileName, "R", "A", %FilePath_Absolute); If &file.IsOpen Then Local string &line; Local number &row = 1; Local array of string &columns; /* Read each line from the CSV */ While &file.ReadLine(&line) /* Split the line by comma (CSV) */ &columns = Split(&line, ","); /* Write data to Excel */ Local number &col; For &col = 1 To &columns.Len &ss.SetCellString(&row, &col, &columns [&col]); End-For; &row = &row + 1; End-While; /* Close the file */ &file.Close(); End-If; /* Save the Excel file */ &ss.Save();

Decrypting and Viewing AES-Encrypted PDFs in PeopleSoft

 If you’ve encrypted PDF files using AES-256 outside of PeopleSoft (e.g., via OpenSSL), and now need to decrypt and view them inside PeopleS...