How to Decrypt OpenSSL AES-256-CBC Secrets in ServiceNow via PowerShell
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 hours ago - last edited 2 hours ago
Overview
When integrating ServiceNow with external security tools (like HashiCorp Vault, CyberArk, or custom DevOps pipelines), you often receive secrets encrypted via OpenSSL. Since ServiceNow's native JavaScript (Rhino) doesn't have a built-in library for OpenSSL's specific "Salted" key derivation, we have to bridge the gap using a PowerShell Step on a MID Server.
This article provides a reusable script to decrypt AES-256-CBC strings that use the EVP_BytesToKey (MD5) derivation.
The Challenge: The "Salted__" Format
Standard AES is just an algorithm, but OpenSSL adds a specific "envelope" to the data:
- It prefixes the data with the string Salted__.
- It follows that with 8 bytes of random Salt.
- It uses a specific loop (KDF) to turn your password into a 32-byte Key and a 16-byte IV.
The Solution: MID Server PowerShell Script
The following script can be used in a PowerShell Step within Flow Designer or as a MID Server Script File.
- The Script Logic
PowerShell
function Decrypt-AES {
param(
[string]$encryptedResponse, # The Base64 string from the external tool
[string]$secret # Your shared Master Key/Password
)
try {
# 1. Convert Base64 and extract the Salt (Bytes 8-15)
$encrypted = [Convert]::FromBase64String($encryptedResponse)
$salt = $encrypted[8..15]
$encryptedData = $encrypted[16..($encrypted.Length - 1)]
# 2. Replicate OpenSSL EVP_BytesToKey (MD5)
$secretBytes = [System.Text.Encoding]::UTF8.GetBytes($secret)
$keyIV = @()
$prev = @()
while ($keyIV.Count -lt 48) {
$md5 = [System.Security.Cryptography.MD5]::Create()
$input = $prev + $secretBytes + $salt
$prev = $md5.ComputeHash($input)
$keyIV += $prev
}
# 3. Assign Key (32 bytes) and IV (16 bytes)
$key = $keyIV[0..31]
$iv = $keyIV[32..47]
# 4. Decrypt using .NET AES libraries
$aes = [System.Security.Cryptography.Aes]::Create()
$aes.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$aes.Key = $key
$aes.IV = $iv
$decryptor = $aes.CreateDecryptor()
$decrypted = $decryptor.TransformFinalBlock($encryptedData, 0, $encryptedData.Length)
return [PSCustomObject]@{
Success = "True"
DecryptedContent = [System.Text.Encoding]::UTF8.GetString($decrypted)
}
}
catch {
return [PSCustomObject]@{
Success = "False"
Message = $_.Exception.Message
}
}
}
$myEncryptedString = "xxxxxx..." # The Base64 from your external tool
$mySecretPassword = "MySuperSecret" # The shared secret
# This line is where the function is called
$result = Decrypt-AES -encryptedResponse $myEncryptedString -secret $mySecretPassword
# 3. THE OUTPUT converting to json to know the status of powershell
$result | ConvertTo-Json -Depth 5
Implementation Best Practices
Use Flow Designer
Instead of hardcoding the password in the script, pass it as an Input Variable in your Flow Designer PowerShell step. You should retrieve the master secret from the Discovery Credentials (discovery_credentials) table using the "Get Credential" action or from the system proper you have already stored the secret value.
Why use .NET instead of third-party libraries?
By using System.Security.Cryptography, you don't have to install any modules (like ActiveDirectory or OpenSSL-Win64) on your MID Servers. This makes your ServiceNow integration "infrastructure-agnostic" as long as the MID Server is running on Windows.
Summary
This approach ensures your secret rotation flows are secure and compliant with standard encryption formats. It allows ServiceNow to act as a secure consumer of secrets without requiring the external team to change their security posture.
Note: This works on Windows Midserver only
Tags: #PowerShell #Security #Integration #MIDServer #AES256 #OpenSSL
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
an hour ago
Nice writeup on the OpenSSL decryption challenge. The EVP_BytesToKey implementation looks solid.
One thing to watch out for - you might want to add some validation on the encrypted input before processing:
```powershell
# Add this before the salt extraction
if ($encrypted.Length -lt 16 -or
[System.Text.Encoding]::ASCII.GetString($encrypted[0..7]) -ne "Salted__") {
throw "Invalid OpenSSL format - missing Salted__ header"
}
```
Also consider wrapping the AES object in a `using` block or explicit disposal to avoid memory leaks on high-volume flows:
```powershell
$aes = [System.Security.Cryptography.Aes]::Create()
try {
# your decryption logic
$decrypted = $decryptor.TransformFinalBlock($encryptedData, 0, $encryptedData.Length)
return [PSCustomObject]@{
Success = "True"
DecryptedContent = [System.Text.Encoding]::UTF8.GetString($decrypted)
}
} finally {
$aes.Dispose()
$decryptor.Dispose()
}
```
For the Flow Designer integration, you can make this even cleaner by creating a reusable subflow that handles the credential retrieval and decryption in one step. That way teams don't have to remember the two-step process each time.
The MD5-based key derivation is deprecated in newer OpenSSL versions, so if your external tools ever migrate to PBKDF2, you'll need to update the KDF logic. But for legacy compatibility this approach works well.
**If you find my answer useful, please mark it as Helpful and Correct. 😊**
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
32m ago
Yes those are valid points you mentioned
Thank you!

