Miva Merchant Development by Scot's Scripts

ENCRYPTION: 2026 Miva Encryption Standards - sans Blowfish

Miva Knowledge Base
ENCRYPTION: 2026 Miva Encryption Standards - sans Blowfish
Important Notice: This information is for internal reference only. Use at your own risk.
Does Google actually understand your Miva Merchant store? Our JSON-LD schema generator makes sure it does. Contact us to get started. (more info)

ENCRYPTION: 2026 Miva Encryption Standards - sans Blowfish

Scot Ranney • April 25, 2026


Blowfish encrypt/decrypt is no longer supported on miva servers. You can migrate if you ask them to turn on "legacy" settings at the server level. The problem in detail is:


This is almost certainly an OpenSSL 3 / Legacy Provider issue. Here's the breakdown:
 
What's happening:

Miva's bf_encrypt() is a MivaScript wrapper around OpenSSL's Blowfish implementation. In OpenSSL 3.0+, Blowfish was moved out of the default provider and into the legacy provider, which is not loaded automatically. When Blowfish is unavailable, the cipher initialization silently fails — the function returns 1 (as if it succeeded) but the encrypted output variable ends up empty with len = 0. This exactly matches the behavior you're describing.

Why it works in dev but not on the other site:
Your dev environment is almost certainly running an older OS (e.g., Ubuntu 20.04, CentOS 7, RHEL 7/8) with OpenSSL 1.1.x, where Blowfish is fully supported by default. The other site is likely on a newer server (Ubuntu 22.04+, RHEL 9, Debian 12, etc.) running OpenSSL 3.x, where Blowfish is legacy-only.

How to confirm: Check the OpenSSL version on each server:

openssl version

Dev will likely show 1.1.x, the broken site will show 3.x.

Your options:

  1. Switch to <strong>crypto_evp_encrypt()</strong> with AES — this is Miva's recommended modern path and works on all current servers:crypto_evp_encrypt( 'aes-256-cbc', key, iv, plaintext, l.encrypted )
    Note: key must be exactly the length required by the cipher (crypto_cipher_key_length()), same for the IV.
  2. Enable the OpenSSL legacy provider on the server — if you control the server and need a quick fix, you can enable it in openssl.cnf. But this is a workaround, not a long-term solution, and may not be available in managed/cloud hosting environments.
  3. Check with Miva support — if you're on Miva's managed hosting, they may have disabled the legacy provider as part of a security hardening update, and you'd need to request a migration path.

 The bottom line: bf_encrypt() is a legacy function that depends on OpenSSL's legacy Blowfish support, and that support is no longer active by default on modern OS/server stacks. Migrating to crypto_evp_encrypt() with AES is the right fix.


Example code logic: Random, based off a use case, but basic principle is here.

  • <strong>ciphername</strong> — just a string telling it which algorithm to use. Use 'aes-256-cbc'. It's the modern standard, universally supported.
  • <strong>key</strong> — a secret password, BUT it must be exactly the right length for the cipher. For aes-256-cbc, that's exactly 32 characters.
  • <strong>iv</strong> (initialization vector) — think of it as a "salt" that makes the same plaintext produce different encrypted output each time if you change it. For aes-256-cbc, it must be exactly 16 characters. For simple/static use like yours, you can just hardcode it. It doesn't need to be secret, just consistent between encrypt and decrypt.
<MvFUNCTION NAME="Encrypt_Token" PARAMETERS="plaintext" STANDARDOUTPUTLEVEL="">
	LOCAL l.cipher, l.key, l.iv, l.encrypted, l.success, l.link_param

	l.cipher	= 'aes-256-cbc'
	l.key	   = 'MySuperSecretKey1234567890123456'   | 32 chars exactly
	l.iv		= 'MyInitVector1234'				   | 16 chars exactly

	l.success   = crypto_evp_encrypt( l.cipher, l.key, l.iv, l.plaintext, l.encrypted )

	IF NOT l.success THEN
		RETURN ''
	ENDIF

	| Base64 encode so it's safe in a URL
	RETURN crypto_base64_encode( l.encrypted )
</MvFUNCTION>

Decrypt

<MvFUNCTION NAME="Decrypt_Token" PARAMETERS="token" STANDARDOUTPUTLEVEL="">
	LOCAL l.cipher, l.key, l.iv, l.decoded, l.decrypted, l.success

	l.cipher	= 'aes-256-cbc'
	l.key	   = 'MySuperSecretKey1234567890123456'   | 32 chars exactly
	l.iv		= 'MyInitVector1234'				   | 16 chars exactly

	| Base64 decode first before decrypting
	l.decoded   = crypto_base64_decode( l.token )

	l.success   = crypto_evp_decrypt( l.cipher, l.key, l.iv, l.decoded, l.decrypted )

	IF NOT l.success THEN
		RETURN ''
	ENDIF

	RETURN l.decrypted
</MvFUNCTION>

 
Parse the Decrypted Value

<MvFUNCTION NAME="Parse_Token" PARAMETERS="decrypted" STANDARDOUTPUTLEVEL="">
	LOCAL l.pos, l.id, l.email

	l.pos   = indexof( ':', l.decrypted, 1 )

	IF NOT l.pos THEN
		RETURN 0	| No colon found, bad token
	ENDIF

	l.id	= substring( l.decrypted, 1, l.pos - 1 )
	l.email = substring( l.decrypted, l.pos + 1, len( l.decrypted ) )

	| Do whatever you need with l.id and l.email here

	RETURN 1
</MvFUNCTION>

 
Sanity Check (length validation)

<MvFUNCTION NAME="Check_Cipher_Lengths" STANDARDOUTPUTLEVEL="">
	LOCAL l.cipher, l.key, l.iv, l.key_len, l.iv_len, l.success

	l.cipher	= 'aes-256-cbc'
	l.key	   = 'MySuperSecretKey1234567890123456'
	l.iv		= 'MyInitVector1234'

	l.success   = crypto_cipher_key_length( l.cipher, l.key_len )
	l.success   = crypto_cipher_iv_length(  l.cipher, l.iv_len  )

	IF len( l.key ) NE l.key_len THEN
		| Key length mismatch - will silently fail
		RETURN 0
	ENDIF

	IF len( l.iv ) NE l.iv_len THEN
		| IV length mismatch - will silently fail
		RETURN 0
	ENDIF

	RETURN 1
</MvFUNCTION>

 
The base64 encode/decode step is essential when putting encrypted data in a URL — raw encrypted bytes contain characters that will break URLs. Encode before putting in the link, decode before decrypting.
 
The key and IV must be identical on both the encrypt and decrypt side. Since you're doing this within the same Miva store, the easiest approach is to define them once in a global variable or store setting that both sides reference.


https://www.scotsscripts.com/mvblog/encryption-2026-miva-encryption-standards-sans-blowfish.html

mvkb_encrypt mvkb_mivascript