How to encrypt and decrypt text in NetSuite using API secrets

March 14, 2024 | 7 minute read
Wilman Arambillete
Senior Manager for SDN Solutions Engineering at Oracle NetSuite
Text Size 100%:

Introduction

There are occasions where sharing text between two parties, such as between different NetSuite accounts or from NetSuite to an external server, becomes necessary. While it's important to acknowledge the role of secure connection protocols like oAuth1 or oAuth2 for safely connecting to NetSuite, this discussion will set aside those topics to concentrate on encryption. Our primary focus will be on identifying the most effective method for encrypting text, which involves using a shared "secret" for both the encryption and decryption processes. This approach ensures that sensitive information remains protected as it is exchanged between involved parties.

API Secrets

NetSuite offers a secure vault for storing sensitive data, accommodating keys, certificates, or plain text. It's crucial to prioritize these dedicated features for safeguarding sensitive information over alternative methods, such as custom records or storing files in the File Cabinet. This ensures the highest level of security within the NetSuite environment.

Furthermore, as we've discussed in previous posts, API secrets enable our partners to share credentials securely with their customers, fostering a safe exchange of information.

To begin, it's essential to create a secret key that will be used for encrypting and decrypting text shared with external services. To do this in NetSuite, navigate to Setup -> Company -> API Secrets. Here, you'll create a new secret key by completing the necessary fields:

  • NAME: Assign a name to your secret key for easy identification.
  • ID: Provide a unique identifier for the key, preferring a custom ID to the system-generated one for better reference.
  • PASSWORD (and confirm it): Set a secure password for your secret key, ensuring it's robust to enhance security.

By following these steps, you can effectively establish a secure method for encrypting and decrypting data, leveraging NetSuite's built-in features for optimal data protection.
 

api-secret1

 

api-secret-2

When setting up a new API secret, consider the following guidelines:

  • Opt for a custom internal ID for the API Secret within your code, rather than relying on the system-generated one. This approach enhances clarity and ease of reference.
  • The encryption process will utilize Cipher, which necessitates specifying the encryption algorithm. In this instance, AES is our choice, requiring the secret key for encryption to be either 16, 24, or 32 bytes in length.
  • Lastly, pay attention to the Restrictions section. Here, you can define the roles, scripts, and domains permitted to use this secret. While it's acceptable to forego restrictions for testing, it's imperative to meticulously configure these permissions for deployment in a production environment

Encrypting data

Having clarified the appropriate storage location for the "secret" key to be used in both encryption and decryption, let's explore which of the numerous modules provided by NetSuite would be best suited for our needs. For this task, we will utilize the createCipher function from the N/crypto module. 
Next, we will demonstrate how to create a function that encrypts text using the API secret we've just established.

const encrypt = (text, secret) => {    
    const sKey = crypto.createSecretKey({
      secret: secret,
      encoding: encode.Encoding.UTF_8,
    });

    const myCipher = crypto.createCipher({
      algorithm: crypto.EncryptionAlg.AES,
      key: sKey,
      padding: crypto.Padding.PKCS5Padding,
    });

    const encoded = myCipher.update({    
      input: text,
      inputEncoding: encode.Encoding.UTF_8,
      outputEncoding: encode.Encoding.BASE_64,
    });

    const cipherPayload = myCipher.final({
      outputEncoding: encode.Encoding.BASE_64,
    });
    return cipherPayload;
  };

This function takes two parameters: the text to be encrypted and the secret, which is the internal ID of the API secret we recently created (e.g., custsecret_myprivatekey). Let's delve into our code:
First, we must generate a SecretKey using the internal ID, enabling its use with the createCipher function.

const sKey = crypto.createSecretKey({
      secret: secret,
      encoding: encode.Encoding.UTF_8,
    });

Next, we proceed to instantiate a Cipher, specifying the encryption algorithm we intend to use and, optionally, setting the padding.

    const myCipher = crypto.createCipher({
      algorithm: crypto.EncryptionAlg.AES,
      key: sKey,
      padding: crypto.Padding.PKCS5Padding,
    });

Following this setup, we're ready to encrypt the text. At this point, we specify the encoding for the input data—typically UTF_8—and select the output encoding. For this example, we opt to encode our text in BASE_64, facilitating easier sharing.

    const encoded = myCipher.update({    
      input: text,
      inputEncoding: encode.Encoding.UTF_8,
      outputEncoding: encode.Encoding.BASE_64,
    });

Finally, the encrypted text is exported in BASE_64 format. NetSuite encapsulates the result in a CipherPayload object, structured as follows:

{
    ciphertext: xxxxxxx,
    iv: xxxxx
}

The encrypted text, encoded in BASE_64, is stored in the ciphertext attribute of the resulting object. Additionally, the Initialization Vector (iv)—a crucial element in encryption processes that introduces randomness into data encryption—is stored in the iv attribute. 

To decrypt our text, we will require the secret key previously established in API secrets, the iv value, and, of course, the encrypted text.

Decrypting data

Let’s see now how our decryption function looks like then:

const decrypt = (cipherPayload, secret) => {
    const sKey = crypto.createSecretKey({
      secret: secret,
      encoding: encode.Encoding.UTF_8,
    });

    const myDecipher = crypto.createDecipher({
      algorithm: crypto.EncryptionAlg.AES,
      key: sKey,
      padding: crypto.Padding.PKCS5Padding,
      iv: cipherPayload.iv,
    });

    const decoded = myDecipher.update({
      input: cipherPayload.ciphertext,
      inputEncoding: encode.Encoding.BASE_64,
    });

    return myDecipher.final();
  };

Initially, we generate the SecretKey using the internal ID of the API secret as a reference. Next, we invoke the crypto.createDecipher function, supplying it with the key, padding (employing the same padding specification as before), and the iv (initialization vector) from the cipherPayload object we've received.

The update() method then processes the encrypted text encoded in BASE_64, as specified in the ciphertext attribute of the cipherPayload object. It's essential to accurately define the input text's encoding, which, in our scenario, is BASE_64.

Bonus track

The examples provided above are tailored for use within NetSuite. However, there might be scenarios where you need to encrypt or decrypt data outside of NetSuite before sharing it with the platform.

To address this need, we'll explore how Node.js can be utilized for such purposes. In the following example, we will employ the same technique, encryption algorithm, and data encoding standards for both encrypting and decrypting the data, ensuring seamless compatibility with NetSuite.

const crypto = require("crypto");

const encrypt = (text, secret) => {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(
    "aes-256-cbc",
    Buffer.from(secret, "utf8"),
    iv,
  );
  cipher.setAutoPadding(true);
  let encrypted = cipher.update(text, "utf8", "base64");
  encrypted += cipher.final("base64");
  return {
    iv: iv.toString("hex"),
    ciphertext: encrypted,
  };
};

Similar to our earlier example with NetSuite, we utilize Cipher, padding, and AES as the encryption algorithm. Within the update function, we specify that the input text is in UTF_8 format, while the encrypted output is encoded in BASE_64. 

Ultimately, we encrypt the text and generate an object containing the iv (initialization vector) and ciphertext, mirroring the approach used in NetSuite.

const decrypt = (cipherPayload, secret) => {
  const decipher = crypto.createDecipheriv(
    "aes-256-cbc",
    Buffer.from(secret, "utf8"),
    Buffer.from(cipherPayload.iv, "hex"),
  );
  decipher.setAutoPadding(true);
  let decrypted = decipher.update(cipherPayload.ciphertext, "base64", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
};

To decrypt the text, we follow the same logic in reverse.

Summary

In this blog post, our aim was to demonstrate a straightforward method for encrypting and decrypting text with a shared secret key. However, it's important to note that this is just one of several strategies to achieve similar security objectives.

Moreover, it's crucial to ensure a secure connection between systems involved and to adhere strictly to the security guidelines we've outlined for the safe exchange of sensitive information.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

Wilman Arambillete

Senior Manager for SDN Solutions Engineering at Oracle NetSuite

With a decade at the company and a cumulative three decades in the technology sector, my primary expertise lies in eCommerce and ERP solutions. 

 

 


Previous Post

Want to become an AI developer? Here’s how with MySQL HeatWave

Nipun Agarwal | 5 min read

Next Post


Introducing Zero to low-cost Autonomous Database for Developers

Simon Law | 3 min read