Friday, September 26, 2025

OIC - How to Encrypt and Decrypt Using AES Key and OCI Function in Oracle Integration Cloud (OIC)

 Working...

📌 Use Case

In real-world Oracle Integration Cloud (OIC) projects, sensitive data like passwords, API keys, or personal information must be transmitted securely. A common approach is to encrypt data before sending it to a target system and decrypt it upon retrieval.

By leveraging AES encryption/decryption inside an OCI Function and invoking it from OIC, we can:

  1. Securely encrypt payloads before sending to external applications.
  2. Decrypt incoming encrypted data from third-party systems.
  3. Ensure compliance with data security requirements (AES/CBC/PKCS5Padding or AES/ECB/PKCS5Padding).
This approach allows OIC to delegate cryptographic operations to OCI Functions, ensuring flexibility and security without exposing raw secrets in integration flows.

Solution Steps

1. Create an OCI Function for AES Encryption/Decryption

Develop a Java OCI Function (see full code below).

Function accepts input parameters:

  • message → Text to encrypt or decrypt.
  • secretKeyBase64 → AES key (Base64 encoded).
  • ivBase64 → Initialization Vector (required for CBC mode).
  • aesMode → Cipher mode (e.g., AES/CBC/PKCS5Padding or AES/ECB/PKCS5Padding).
  • actionType → Either ENCRYPT or DECRYPT.

2. Deploy the Function in OCI

Use Fn Project CLI to deploy.

Make sure the function has appropriate IAM permissions to be invoked from OIC.

3. Invoke OCI Function from OIC

  • In OIC, create a REST Adapter to call the OCI Function.
  • Pass input parameters (message, aesMode, etc.) in the request body.
  • Receive encrypted/decrypted message in the response payload.

4. Use in Integration Flows

Encrypt sensitive data (like API credentials) before storing or transmitting.

Decrypt incoming messages before business logic processing.

📝 Function Code

Here’s the Java OCI Function code you can deploy:

package com.clp.fn;

import javax.crypto.Cipher;

import javax.crypto.spec.IvParameterSpec;

import javax.crypto.spec.SecretKeySpec;

import java.util.Base64;

import java.util.logging.*;

import java.security.SecureRandom;


public class AESEncryptDecrypt {

    private static final Logger logger = Logger.getLogger(AESEncryptDecrypt.class.getName());


    public static class Input {

        public String message;

        public String secretKeyBase64;

        public String ivBase64;

        public String aesMode;     // AES/CBC/PKCS5Padding or AES/ECB/PKCS5Padding

        public String actionType;  // ENCRYPT or DECRYPT

    }


    public static class Result {

        public String message;

        public String salt;

        public String wechataeskey;

        public String executionInfo;

    }


    public Result handleRequest(Input input) {

        logger.log(Level.INFO, "OIC - message:", input.message);

        logger.log(Level.INFO, "OIC - secretKeyBase64:", input.secretKeyBase64);

        logger.log(Level.INFO, "OIC - ivBase64", input.ivBase64);

        logger.log(Level.INFO, "OIC - aesMode:", input.aesMode);

        logger.log(Level.INFO, "OIC - actionType:", input.actionType);


        Result result = null;

        if ("DECRYPT".equals(input.actionType)) {

            result = decryptMyMessage(input);

        } else if ("ENCRYPT".equals(input.actionType)) {

            result = encryptMyMessage(input);

        } else {

            result = new Result();

            result.executionInfo = "ERROR: No proper action found , " +

                    "possible value is ENCRYPT or DECRYPT , recieved value:" + input.actionType;

        }


        return result;

    }


    // Generate random 16-byte IV for AES/CBC

    public static IvParameterSpec generateIV() {

        byte[] iv = new byte[16]; // 128-bit IV

        new SecureRandom().nextBytes(iv);

        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

        return ivParameterSpec;

    }


    public Result encryptMyMessage(Input input) {

        Result result = new Result();

        try {

            byte[] decodedKey = null;

            if (input.secretKeyBase64 == null) {

                // Generate 16-digit random numeric string

                String keyString = generateRandomDigits(16);

                //System.out.println("Generated 16-digit AES key: " + keyString);

                // Convert to byte array (each digit becomes 1 byte, 16 bytes total = 128 bits)

                result.wechataeskey = keyString;

                decodedKey = keyString.getBytes("UTF-8");

            } else {

                // Decode the base64 encoded string

                decodedKey = Base64.getDecoder().decode(input.secretKeyBase64);

            }

byte[] messageBytes = input.message.getBytes("UTF-8");

        // Create a SecretKeySpec for the AES key

        SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, "AES");

        // Create a Cipher instance for AES

        Cipher cipher = Cipher.getInstance(input.aesMode);

        if (input.aesMode.contains("CBC")) {

            // AES/CBC/PKCS5Padding required IV

            IvParameterSpec ivParameterSpec = generateIV();

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

            result.salt = Base64.getEncoder().encodeToString(ivParameterSpec.getIV());

        } else {

            // AES/ECB/PKCS5Padding do not require IV

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

        }


        // Decrypt the message

        byte[] originalBytes = cipher.doFinal(messageBytes);

        String encodedString = Base64.getEncoder().encodeToString(originalBytes);

        result.message = encodedString;

        // String originalMessage = new String(originalBytes);

        result.executionInfo = "SUCCESS";

    } catch (Exception e) {

        result.executionInfo = e.getMessage();

        logger.log(Level.INFO, "Error Details:", e.getMessage());

    }


    return result;

}

public Result decryptMyMessage(Input input) {


    Result result = new Result();

    generateIV();


    try {

        // Decode the base64 encoded string

        byte[] decodedKey = Base64.getDecoder().decode(input.secretKeyBase64);

        byte[] encryptedBytes = Base64.getDecoder().decode(input.message);


        // Create a SecretKeySpec for the AES key

        SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, "AES");


        // Create a Cipher instance for AES

        Cipher cipher = Cipher.getInstance(input.aesMode);


        if (input.aesMode.contains("CBC")) {

            // AES/CBC/PKCS5Padding required IV

            byte[] decodedIV = Base64.getDecoder().decode(input.ivBase64);

            IvParameterSpec ivParameterSpec = new IvParameterSpec(decodedIV);

            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);

        } else {

            // AES/ECB/PKCS5Padding do not require IV

            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

        }


        // Decrypt the message

        byte[] originalBytes = cipher.doFinal(encryptedBytes);

        String encodedString = Base64.getEncoder().encodeToString(originalBytes);

        result.message = encodedString;

        // String originalMessage = new String(originalBytes);

        result.executionInfo = "SUCCESS";

    } catch (Exception e) {

        result.executionInfo = e.getMessage();

        logger.log(Level.INFO, "Error Details:", e.getMessage());

    }


    return result;

}

byte[] decodedIV = Base64.getDecoder().decode(input.ivBase64);

IvParameterSpec ivParameterSpec = new IvParameterSpec(decodedIV);

cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);

} else {

//AES/ECB/PKCS5Padding do not require IV

cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

}


// Decrypt the message

byte[] originalBytes = cipher.doFinal(encryptedBytes);

String encodedString = Base64.getEncoder().encodeToString(originalBytes);


result.message = encodedString;

//String originalMessage = new String(originalBytes);

result.executionInfo = "SUCCESS";

} catch (Exception e) {

    result.executionInfo = e.getMessage();

    logger.log(Level.INFO, "Error Details:", e.getMessage());

}


return result;

}


// WeChat, generate a random string of digits for AES Key

public static String generateRandomDigits(int length) {

    SecureRandom random = new SecureRandom();

    StringBuilder sb = new StringBuilder(length);

    for (int i = 0; i < length; i++) {

        sb.append(random.nextInt(10)); //0-9

    }

    return sb.toString();

}

}

Code link:

https://drive.google.com/file/d/1OQnaC9XaEqvIMrTkbi1o5wvJsa5i1LPL/view?usp=drivesdk

Key Benefits

  • Ensures data confidentiality across systems.
  • Supports multiple AES modes (CBC, ECB) with PKCS5Padding.
  • Can be reused across multiple OIC integrations via a single OCI Function.
  • No direct exposure of keys inside OIC flows

OIC - How to Use XSLT to Generate Namespaced Target Payload Blocks in OIC

Use Case

When working with Oracle Integration Cloud (OIC) SOAP-based integrations, we often need to map fault objects (like error details or reasons) into a target response payload that follows a specific namespace and schema.

In this example, the target system expects a payload with the following structure:

<ns2:AddMeterToInventoryResponse xmlns:ns2="turtletech.com/TS2/">
   <ns2:AddMeterToInventoryResult>
      <ns2:ErrorObj>
         <ns2:errorString>Meter Not Added</ns2:errorString>
      </ns2:ErrorObj>
   </ns2:AddMeterToInventoryResult>
</ns2:AddMeterToInventoryResponse>

The challenge is to generate this block dynamically from fault objects (like $GlobalFaultObject/nspr1:fault) while preserving the correct namespace (ns2) and nested structure.


Solution with XSLT Mapper

To achieve this in OIC mapper, we can leverage <xsl:apply-templates> and define templates that build the required XML block.


1. Declare the Namespace

In your stylesheet, ensure that the target namespace (ns2) is declared:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ns2="turtletech.com/TS2/"
                xmlns:nspr1="http://schemas.oracle.com/faults"
                exclude-result-prefixes="nspr1"
                version="1.0">

2. Entry Template

Start with a root template that applies templates for faults:

<xsl:template match="/">
   <nstrgmpr:InboundSOAPResponseDocument>
      <nstrgmpr:Body>
         <xsl:apply-templates select="$GlobalFaultObject/nspr1:fault"/>
      </nstrgmpr:Body>
   </nstrgmpr:InboundSOAPResponseDocument>
</xsl:template>

3. Fault Handling Template

Define how the fault is transformed into the required target structure:

<xsl:template match="nspr1:fault">
   <ns2:AddMeterToInventoryResponse>
      <ns2:AddMeterToInventoryResult>
         <xsl:choose>
            <xsl:when test="$GlobalFaultObject/nspr1:fault/nspr1:details = ''">
               <ns2:ErrorObj>
                  <ns2:errorString>
                     <xsl:value-of select="$GlobalFaultObject/nspr1:fault/nspr1:reason"/>
                  </ns2:errorString>
               </ns2:ErrorObj>
            </xsl:when>
            <xsl:otherwise>
               <ns2:ErrorObj>
                  <ns2:errorString>
                     <xsl:value-of select="$GlobalFaultObject/nspr1:fault/nspr1:details"/>
                  </ns2:errorString>
               </ns2:ErrorObj>
            </xsl:otherwise>
         </xsl:choose>
      </ns2:AddMeterToInventoryResult>
   </ns2:AddMeterToInventoryResponse>
</xsl:template>


4. Execution

With this template setup:

  • OIC picks up the fault object.
  • The <xsl:apply-templates> dispatches it to the fault template.
  • The correct namespace-prefixed block (ns2) is generated.

This ensures your output matches the required schema exactly and avoids namespace mismatches.


Key Takeaways

  • Always declare the target namespace (ns2) in the XSLT stylesheet.
  • Use <xsl:apply-templates> and <xsl:template match> to modularize transformation logic.
  • Use <xsl:choose> to conditionally pick reason or details from fault objects.
  • This approach guarantees a reusable and scalable mapping for multiple fault scenarios.

👉 This method is highly reusable for any SOAP service response transformation in OIC where a specific namespace-aligned block must be built dynamically.



Wednesday, September 24, 2025

OIC - How to Generate RSA Private Key for Oracle OIC Vault, Functions, and Connections Using ssh-keygen (Windows CMD)

📌 Use Case

When working with Oracle Integration Cloud (OIC), secure authentication often requires RSA private keys in PEM format. These keys are commonly used for:

  • Uploading secrets into OCI Vault.
  • Configuring OIC Functions with key-based authentication.
  • Creating SFTP, REST, or API Connections in OIC that use private keys instead of passwords.

If you are on Windows, you can use the built-in OpenSSH ssh-keygen tool (available in Windows 10/11 CMD or PowerShell) to generate or reformat RSA private keys.


⚙️ Solution Steps (Windows CMD)

Step 1: Ensure OpenSSH is Installed

  • Open Command Prompt.
  • Run:
    ssh -V
    
    If you see a version (e.g., OpenSSH_for_Windows_8.x), you’re good to go.
    If not, install OpenSSH from Windows Optional Features.

Step 2: Run the ssh-keygen Command in CMD

Use the following command to create or reformat a private key in PEM format:

ssh-keygen -p -f my_oic_key.pem -N "" -t rsa -m pem

🔎 Example:

ssh-keygen -p -f svc_oic_ccsvault_tst@clp.com.hk-2025-09-16T13_18_48.364Z.pem -N "" -t rsa -m pem


Parameters explained:

  • -p → Update or re-save the key file.
  • -f my_oic_key.pem → Path and name of your private key file.
  • -N "" → Empty passphrase (no password required).
  • -t rsa → Generate RSA type key.
  • -m pem → Export the key in PEM format (needed for OIC Vault, Functions, Connections).

Step 3: Verify the Key File

  • Only one private key file (.pem) will be created/updated.
  • It will be saved in PEM format.
  • No public key file (.pub) is generated in this mode.

Step 4: Use in OIC

  • Upload the .pem file into OCI Vault as a secret.
  • Configure OIC Functions to read this private key.
  • Use it in SFTP / REST / API Connections in OIC for key-based authentication.

Final Result: You now have an RSA private key in PEM format, created directly from Windows CMD, ready for use in Oracle Integration Cloud Vault, Functions, and Connections.


Tuesday, September 23, 2025

OIC - Monitoring Oracle CCS Batch Job Failures in Real-Time with OIC and Datadog

Use Case

In Oracle Integration Cloud (OIC), batch jobs are frequently triggered in Oracle Customer Cloud Service (CCS) to handle customer data processing. However, if these batch jobs fail, it becomes critical for business users or technical support teams to be notified quickly to take corrective action. Traditional monitoring through CCS dashboards may cause delays, as failures might go unnoticed until users manually check.

To improve observability and responsiveness, we can integrate OIC with Datadog. This integration ensures that whenever a batch job fails in CCS, OIC captures the failure details and logs them into Datadog. From there, alerts can be configured to notify support teams instantly via email, Slack, or other preferred channels.


Solution Steps


  1. OIC scheduler will run every 5 mins interval or as per business requirement.

  2. Trigger Health check Batch Job in Oracle CCS from OIC

    • Use the CCS Utility REST API or SOAP service to invoke a batch job from OIC.
    • Pass required parameters like job name, job ID, and input arguments.



Note: in our case, not passing any param to health check up job.

Send Failure Details to Datadog

    • Use OIC’s REST Adapter to call Datadog’s Log Intake API.
    • Send the failure payload in JSON format
    • Example payload to Datadog:
      {
        "F1-HealthCheck": {
          "healthOverallResponse": 200,
          "healthCheckResults": [
            {
              "healthCompFlg": "BLOS",
              "healthCompDetail": "Batch Control: BILLING - Create Bills for Open Bill Cycles",
              "healthCompStatusFlg": "DISA",
              "healthCompStatusDescr": "Disabled",
              "healthCompStatusReason": "Level of Service information is not available for this batch job.",
              "healthCompResponse": 200,
              "mo": "BATCH CNTL",
              "navOpt": "f1btctrlTabMenu",
              "pkVal1": "BILLING",
              "pkFld1": "BATCH_CD"
            }
          ]
        }
      }




Configure Alerts in Datadog
  • In Datadog, create a monitor on the CCS job failure logs.
  • Define alert conditions (e.g., “if error count > 0 in last 5 minutes”).
  • Set up notification channels such as Slack, PagerDuty, or email for business/tech support teams.
Action by Support Team
  • Support team receives alerts in real-time.
  • They analyze failure details in Datadog dashboards and take immediate corrective actions in CCS.

Outcome:

  • Proactive monitoring of CCS batch jobs.
  • Reduced downtime by instantly notifying stakeholders.
  • Enhanced collaboration between business and IT teams via Datadog’s unified monitoring platform.


OIC - Solving Cold Start Latency in OCI Functions (AES Encryption/Decryption via OIC)

Use Case

In an integration scenario, we have an OCI Function that performs AES encryption and decryption.
This function is invoked from Oracle Integration Cloud (OIC) as part of secure data exchange.

However, we observed a performance bottleneck:

  • When the function is invoked after being idle for a long time, the first request takes significantly longer to respond.
  • Subsequent requests perform normally.
  • This is the classic cold start issue with serverless functions.

For business-critical integrations where response time is crucial (e.g., encrypting/decrypting sensitive payloads on-the-fly), this latency is unacceptable.

Solution: Provisioned Concurrency in OCI Functions

To eliminate cold starts and improve response consistency, we configure Provisioned Concurrency for the function.
Provisioned concurrency ensures that a set number of function instances are kept warm and ready, drastically reducing the cold start delay.


Step-by-Step Solution

  1. Identify the Function

    • Go to OCI Console → Developer Services → Functions → Applications
    • Select the function used for AES encrypt/decrypt.
  2. Check Current Behavior

    • Invoke the function from OIC after idle time.
    • Observe latency in the first response.
  3. Enable Provisioned Concurrency

    • Navigate to the function details page.
    • Select Concurrency → Provisioned Concurrency.
    • Set the number of provisioned instances (e.g., 1 or 2, depending on expected workload).
  4. Save and Deploy

    • Apply the configuration.
    • OCI will now keep those function instances warm.
  5. Validate from OIC

    • Re-test the function invocation from OIC.
    • First call latency should now be drastically reduced.
  6. Optimize Costs

    • Start with minimal provisioned concurrency (e.g., 1)
    • Monitor usage and increase only if high parallelism is required.

Key Benefits

No cold start delays – consistent response time for AES operations
Improved user experience – faster integration performance
Scalable – adjust provisioned concurrency as per demand
Secure – AES encryption/decryption handled in isolated OCI Function environment


👉 This setup ensures reliable, low-latency cryptographic operations for sensitive data processing in OIC integrations.


Reference:

https://docs.oracle.com/en-us/iaas/Content/Functions/Tasks/functionsusingprovisionedconcurrency.htm

Wednesday, September 17, 2025

OIC - OIC Utility to Reprocess Failed Real-Time Integration JSON Payloads

📌 Use Case

In real-time OIC integrations, JSON payloads are exchanged with external systems via REST APIs. When such integrations fail (due to downstream errors, connectivity issues, or invalid data), the input payload is saved into an OIC SFTP error folder for recovery.

Manually retrieving, reviewing, and resubmitting these payloads is time-consuming and error-prone.

To simplify recovery, we build an OIC Utility Service that:

  • Takes Interface ID and Payload File Name as inputs
  • Fetches error folder path and target REST endpoint (relative path) from a Lookup
  • Reads the failed payload file from the error folder
  • Encodes and decodes the payload as Base64
  • Calls the dynamic downstream REST service
  • Resubmits the payload as binary JSON for seamless reprocessing

This ensures that failed real-time integrations can be reprocessed quickly and reliably, without manual intervention.


🛠️ Solution Steps

1. Create a Lookup for Metadata

Define a Lookup in OIC with mappings for each interface:

  • Interface ID → Error Folder Path → Relative REST Endpoint Path
    Example:
HCM_IFC    | /u01/error/hcm/json    | /hcm/v1/worker
Payroll_IFC| /u01/error/payroll/json| /payroll/v2/run

2. Design the Utility App-Driven Orchestration

Trigger the utility with a REST endpoint that accepts:

  • Interface ID
  • Payload File Name


3. Fetch Error Path and REST Endpoint from Lookup

  • Use Lookup functions to dynamically retrieve:
    • Error folder path
    • Relative endpoint URI

4. List Files in Error Folder

  • Use File Server (SFTP) action to list and fetch the payload file based on the provided name.
  • Capture the fileReference from the file action (pointer to the file inside OIC).

5. Read and Encode Payload

  • Use Read File (File Server Action) → Get the file content using fileReference.
  • Encode the payload into Base64, then decode back to binary JSON inside OIC.

6. Call Dynamic REST Service

  • Use HTTP Adapter with dynamic configuration:
    • Base URL → from OIC Connection
    • Relative Path → from Lookup
  • Pass the decoded JSON payload as the request body.





7. Handle Logging & Tracking

  • Log success/failure at each step (File found, Payload resubmitted, REST service status).
  • Update monitoring dashboard or custom tables for auditing.

Benefits

  • Automated reprocessing of failed JSON payloads
  • Dynamic & reusable across multiple interfaces via Lookup
  • Reduces manual errors in resubmission
  • Improves system reliability & recovery time for real-time integrations


Tuesday, September 16, 2025

OIC - Building a Utility Service in OIC to Reprocess Failed Files

📌 Use Case

In many OIC (Oracle Integration Cloud) projects, integrations involve file-based processing where files are picked from an inbound location and processed further.

However, some files may fail due to validation issues, network errors, or downstream service unavailability. Typically, failed files are moved into an error folder.

Instead of manually moving and reprocessing files, we can create a reusable utility App-Driven Orchestration in OIC that:

  • Takes input parameters: Interface ID, File Name, and File Processing Date
  • Identifies inbound and error folders from a lookup using the Interface ID
  • Lists all failed files in the error folder
  • Moves files back to the inbound folder
  • Calls the next integration service dynamically (via absolute endpoint URI)
  • Passes the required parameters to retry the file processing automatically

This makes reprocessing automated, consistent, and faster.


🛠️ Solution Steps

1. Create Lookup for Folder Paths

  • Define a Lookup in OIC with mappings:
    • Interface ID → Inbound Folder Path → Error Folder Path
  • Example:
    Payroll_IFC | /u01/inbound/payroll | /u01/error/payroll
    HCM_IFC     | /u01/inbound/hcm     | /u01/error/hcm
    

2. Design the App-Driven Orchestration (Utility Service)

  • Triggered by a REST endpoint that takes:
    • Interface ID
    • File Name
    • File Processing Date


3. Fetch Folder Paths from Lookup

  • Use OIC Lookup functions to fetch Error Folder and Inbound Folder based on the provided Interface ID.

4. List Files in Error Folder

  • Call FTP / File Adapter to list files from the error folder.
  • Apply filter by File Name + Date if provided.

5. Move Files from Error → Inbound

  • For each matching file:
    • Use File Adapter (Read/Write) or FTP Move operation
    • Move file from the error folder to the inbound folder

6. Call the Next Integration

  • Configure an HTTP Adapter to call the downstream OIC service (absolute endpoint).
  • Pass parameters like:
    • File Name
    • Processing Date
    • Interface ID
  • This re-triggers the main integration flow as if the file was newly dropped in inbound.


7. Handle Errors and Logging

  • Add tracking:
    • Success → File reprocessed successfully
    • Failure → Log reason (e.g., file not found, service unavailable)
  • Store logs in OIC Activity Stream or custom log file.

✅ Benefits

  • Fully automated reprocessing of failed files
  • No manual intervention needed
  • Reusable utility → works across multiple integrations
  • Lookup-driven → easy to extend for new interfaces


Featured Post

OIC - How to Encrypt and Decrypt Using AES Key and OCI Function in Oracle Integration Cloud (OIC)

 Working... 📌 Use Case In real-world Oracle Integration Cloud (OIC) projects, sensitive data like passwords, API keys, or personal informat...