DecryptSymmetric
Decrypts a Base64 ciphertext produced by EncryptSymmetric. Runtime-proven on a live Marketing Cloud Engagement CloudPage — including which cipher names work and why an arity mistake cannot be hidden behind a condition.
Syntax
DecryptSymmetric(encryptedValue, algorithm, passwordExternalKey, password, saltExternalKey, salt, ivExternalKey, iv) → string
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
encryptedValue |
string | Yes | The Base64 ciphertext to decrypt |
algorithm |
string | Yes | Cipher name, optionally followed by semicolon-separated mode and padding settings; must match what was used to encrypt |
passwordExternalKey |
string | Yes | External key of a Key Management customer key holding the passphrase, or empty when the passphrase is supplied inline |
password |
string | Yes | The passphrase itself, or empty when an external key is used |
saltExternalKey |
string | Yes | External key holding the salt, or empty when the salt is supplied inline |
salt |
string | Yes | The salt as a hex string, or empty when an external key is used |
ivExternalKey |
string | Yes | External key holding the initialization vector, or empty when the IV is supplied inline |
iv |
string | Yes | The initialization vector as a hex string, or empty when an external key is used |
All eight arguments must be present, and all four settings — algorithm, passphrase, salt and IV — must match the ones used to encrypt.
Example
%%[
VAR @plain
SET @plain = DecryptSymmetric(
"Ig8oL30Et8hO0sELCyVakw==", "aes",
@noKey, "zzThrowawayPhrase2026",
@noKey, "0011223344556677",
@noKey, "000102030405060708090a0b0c0d0e0f")
]%%
%%=v(@plain)=%%
Renders SFMC probe 2026. The ciphertext is a plain transportable string, so it can be stored, passed through a link, or hard-coded like this — it is not an in-render handle.
The usual shape is decrypting something that arrived from outside the page:
%%[
VAR @token, @plain
SET @token = RequestParameter("t")
SET @plain = DecryptSymmetric(@token, "aes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
]%%
@noKey is simply never assigned — an undeclared variable is how you say “no external key” inline, and an empty string literal does the same thing.
Return value
string — the decrypted plain text.
There is no sentinel value to test for. A branch that renders nothing at all is the failure signal.
Behaviour
The round trip is exact, including non-ASCII. A fixed ASCII string and a string containing é and € each came back byte for byte identical to the original when encrypted and decrypted in the same render with the same arguments.
The cipher names accepted match the encrypt side, and the name is case-insensitive. aes, AES, des and tripledes each returned the original plaintext, every one exercised through an actual DecryptSymmetric call rather than assumed from its inverse.
The compound form works, and the padding scheme is visible in the result. des;mode=ecb;padding=zeros decrypted successfully, but the returned string carried trailing padding characters instead of ending at the original plaintext — measure the length rather than trusting that the value ends where you expect.
An empty string and an omitted external key are interchangeable. Passing "" in the three external-key positions returned the same plaintext as passing an undeclared variable.
All eight arguments are required, and a wrong count is a compile-time error. Seven and nine arguments each abort the page, and — unlike a bad argument value — a wrong argument count takes down every branch of the page, including ones that are never selected. An arity mistake cannot be hidden behind a condition.
What happens on bad input is not established here. Decrypting with a wrong passphrase, a wrong salt, or a malformed or truncated ciphertext could not be pinned down: those cases sat in a page that was already aborting for an unrelated compile-time reason, so their failures say nothing about the function. Treat the outcome as unknown and validate the input before you rely on it.
The named-key form is not covered here. Supplying a Key Management customer key in the external-key positions requires a key configured in Setup, which was not available on the business unit used for these checks. Everything above was proven with inline values only.
Show test script
%%[
VAR @b, @asc, @non, @pw, @salt, @iv
VAR @e1, @d1, @e2, @d2
SET @b = RequestParameter("b")
SET @asc = "SFMC probe 2026"
SET @non = Concat("caf", Char(233), Char(8364))
SET @pw = "zzThrowawayPhrase2026"
SET @salt = "0011223344556677"
SET @iv = "000102030405060708090a0b0c0d0e0f"
/* known-good control: renders on every request, so a run of HTTP 422s
can be told apart from a broken deploy */
OutputLine(Concat("CTRL=[", Base64Encode("Man"), "]"))
/* round trip for ASCII and non-ASCII in the same render */
IF @b == "safe" THEN
OutputLine(Concat("--- safe start ---"))
SET @e1 = EncryptSymmetric(@asc, "aes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @d1 = DecryptSymmetric(@e1, "aes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @e2 = EncryptSymmetric(@non, "aes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @d2 = DecryptSymmetric(@e2, "aes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
OutputLine(Concat("D1=[", @d1, "]"))
OutputLine(Concat("ROUNDTRIP_ASCII=[", IIF(@d1 == @asc, "yes", "no"), "]"))
OutputLine(Concat("ROUNDTRIP_NONASCII=[", IIF(@d2 == @non, "yes", "no"), "]"))
OutputLine(Concat("--- safe done ---"))
ENDIF
/* the ciphertext is a plain transportable string, not an in-render handle:
this is the value the safe branch printed, hard-coded */
IF @b == "literal" THEN
OutputLine(Concat("--- literal start ---"))
SET @d1 = DecryptSymmetric("Ig8oL30Et8hO0sELCyVakw==", "aes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
OutputLine(Concat("LITERAL=[", @d1, "]"))
OutputLine(Concat("--- literal done ---"))
ENDIF
/* the cipher name is case-insensitive on the decrypt side too */
IF @b == "aesu" THEN
OutputLine(Concat("--- aesu start ---"))
SET @e1 = EncryptSymmetric(@asc, "AES", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @d1 = DecryptSymmetric(@e1, "AES", @noKey, @pw, @noKey, @salt, @noKey, @iv)
OutputLine(Concat("AESU=[", @d1, "]"))
OutputLine(Concat("--- aesu done ---"))
ENDIF
/* other accepted cipher names */
IF @b == "des" THEN
OutputLine(Concat("--- des start ---"))
SET @e1 = EncryptSymmetric(@asc, "des", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @d1 = DecryptSymmetric(@e1, "des", @noKey, @pw, @noKey, @salt, @noKey, @iv)
OutputLine(Concat("DES_BACK=[", @d1, "]"))
OutputLine(Concat("--- des done ---"))
ENDIF
IF @b == "tdes" THEN
OutputLine(Concat("--- tdes start ---"))
SET @e1 = EncryptSymmetric(@asc, "tripledes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @d1 = DecryptSymmetric(@e1, "tripledes", @noKey, @pw, @noKey, @salt, @noKey, @iv)
OutputLine(Concat("TDES_BACK=[", @d1, "]"))
OutputLine(Concat("--- tdes done ---"))
ENDIF
/* the compound cipher;mode;padding form - the padding residue shows up in
the decrypted value, so compare BACKLEN against the plaintext length */
IF @b == "desmode" THEN
OutputLine(Concat("--- desmode start ---"))
SET @e1 = EncryptSymmetric(@asc, "des;mode=ecb;padding=zeros", @noKey, @pw, @noKey, @salt, @noKey, @iv)
SET @d1 = DecryptSymmetric(@e1, "des;mode=ecb;padding=zeros", @noKey, @pw, @noKey, @salt, @noKey, @iv)
OutputLine(Concat("DESMODE_BACK=[", @d1, "] BACKLEN=[", Length(@d1), "]"))
OutputLine(Concat("--- desmode done ---"))
ENDIF
/* empty strings in the external-key positions behave like omitting them */
IF @b == "emptyext" THEN
OutputLine(Concat("--- emptyext start ---"))
SET @e1 = EncryptSymmetric(@asc, "aes", "", @pw, "", @salt, "", @iv)
SET @d1 = DecryptSymmetric(@e1, "aes", "", @pw, "", @salt, "", @iv)
OutputLine(Concat("EMPTYEXT_BACK=[", @d1, "]"))
OutputLine(Concat("--- emptyext done ---"))
ENDIF
]%%
AMPscript has no block scope. Re-declaring a variable with VAR inside an IF branch aborts the whole page at compile time, even when that branch is never selected. Hoist every declaration into a single top-level block before the branches.
Availability
| Platform | Available |
|---|---|
| Marketing Cloud Engagement | Yes |
| Marketing Cloud Next | No |
See also
- EncryptSymmetric — the inverse; produces the Base64 ciphertext this function consumes
- Base64Decode — decoding, not decryption; needs no key at all
- Official reference · ampscript.guide