Credit Card Encryption on the Native Webstore Template

Acctivate’s Native web store connector supports transferring encrypted credit card numbers from the web store to Acctivate. Credit card numbers should be encrypted using AES with a 128-bit block size, 256-bit key length, and CBC mode. Blocks should be padded with zeroes. It should be sent to Acctivate formatted as $AES256CBC$iv$encrypted_number

  • iv is the base64-encoded initialization vector used during encryption
  • encrypted_number is the base64-encoded encrypted credit card number.

The party responsible for passing the Encrypted card numbers to Acctivate via the API will enter the CC encryption key once into the webstore template options. Acctivate will then encrypt that key using a proprietary encryption in the company database.

To enter the key into the webstore template options, go to File > Import Sales Orders. Then find the corresponding template id and select Edit. You’ll select Next twice and then the template options page will show. You will enter the CC encryption key you have generated into the ‘Encryption key for decrypting CC numbers‘ field. Close the window and select to Save changes. The key will then be encrypted in the database and users will not be able to view it on the template options either. 

Examples

Note that since the IV should be randomly-generated, encrypting the same CC number with the same key should generate a different encrypted result each time. However, decrypting the encrypted number should result in the correct CC number.

Example 1:

Key 12B529889DF82A477A6CCAE8C837D043808E51974769ACF288B5BD2C9F1D8E0E
Encrypted $AES256CBC$JTn/8DC1Omd41flDNIK+yg==$XrnBo1Qe1+/hXYzCtVMdKA=
CC Number 5512056021930658

Example 2:

Key 94D58DB9446E4456BBE8CB81F42D1F32977F545A400B6A874E911396CAFF1118
Encrypted $AES256CBC$MsfdrOYbvT3Z85qOv3meMA==$vgNBGgZEtFcaigSdgK4HTQ=
CC Number 79521833446488

Example 3:

Key 0C90CEF3A9620D2224598CA10F06965DD8D7A368DFE3C1580CB6792F2374ECC7
Encrypted $AES256CBC$Ei7ENFvl+wJX7BO8+SZnVQ==$L/hCzUG0sp+SAn+9USj8KQ=
CC Number 8486690700182615

Sample Code

Visual Basic .NET

    ''' <summary>
    ''' Encrypt CC Number using strong encryption that is PCI compliant.
    ''' </summary>
    ''' <param name="Key">Key (256 bits) as a hexadecimal string</param>
    ''' <param name="Value">Credit card number</param>
    ''' <returns>$AES256CBC$IV$CipherText where IV and CipherText are in Base64 format, and IV is 128 bits</returns>
    Public Shared Function EncryptCCNumber(ByVal Key As String, ByVal Value As String) As String
        Dim KeyBytes As Byte()
        Dim IV(15) As Byte ' 128 bits
        Dim ClearText As Byte()
        Dim Rng As New Security.Cryptography.RNGCryptoServiceProvider()

        KeyBytes = HexToByteArray(Key)
        Rng.GetBytes(IV)
        ClearText = System.Text.Encoding.ASCII.GetBytes(Value)

        Using oAES As New System.Security.Cryptography.AesManaged()
            oAES.KeySize = 256
            oAES.Mode = Security.Cryptography.CipherMode.CBC
            oAES.Padding = Security.Cryptography.PaddingMode.None
            Using oEncrypt = oAES.CreateEncryptor(KeyBytes, IV)
                Dim BlockSize As Integer = oEncrypt.InputBlockSize

                ' Zero pad ClearText byte array on the right to multiples of the Block Size.
                If ClearText.Length Mod BlockSize <> 0 Then
                    ReDim Preserve ClearText(ClearText.Length + (BlockSize - (ClearText.Length Mod BlockSize)) - 1)
                End If

                ' Check to make sure ClearText byte array is now a multiple of the Block Size.
                Debug.Assert((ClearText.Length Mod BlockSize) = 0)

                Dim CipherText As Byte() = oEncrypt.TransformFinalBlock(ClearText, 0, ClearText.Length)
                Return "$AES256CBC$" & Convert.ToBase64String(IV) & "$" & Convert.ToBase64String(CipherText)
            End Using
        End Using
    End Function

    ''' <summary>
    ''' Converts hexadecimal number to a byte.
    ''' </summary>
    Public Shared Function HexToByte(ByVal Value As String) As Byte
        Return Convert.ToByte(Integer.Parse(Value, Globalization.NumberStyles.HexNumber))
    End Function

    ''' <summary>
    ''' Convert a string of an even number of hex digits into an array of bytes.
    ''' </summary>
    Public Shared Function HexToByteArray(ByVal Value As String) As Byte()
        Dim Result() As Byte

        ReDim Result(Len(Value) \ 2 - 1)
        For I As Integer = 1 To Len(Value) Step 2
            Result((I - 1) \ 2) = HexToByte(Mid(Value, I, 2))
        Next

        Return Result
    End Function

    ''' <summary>
    ''' Convert byte to two hexadecimal digits.
    ''' </summary>
    Public Shared Function ByteToHex(ByVal Value As Byte) As String
        Dim Result As String = Hex(Value)
        Return If(Len(Result) = 1, "0", "") & Result
    End Function

    ''' <summary>
    ''' Convert byte array to an even number of hexadecimal digits.
    ''' </summary>
    Public Shared Function ByteArrayToHex(ByVal Value As Byte()) As String
        Dim Result As New System.Text.StringBuilder()

        For I As Integer = 0 To UBound(Value)
            Result.Append(ByteToHex(Value(I)))
        Next

        Return Result.ToString()
    End Function

    ''' <summary>
    ''' Decrypt CC Number using strong encryption that is PCI compliant.
    ''' </summary>
    ''' <param name="Key">Decryption key (256 bits) as a hexadecimal string</param>
    ''' <param name="Value">Encrypted value in $AES256CBC$IV$CipherText format, where IV and CipherText are Base 64 encoded, and IV is 128 bits</param>
    Public Shared Function DecryptCCNumber(ByVal Key As String, ByVal Value As String) As String
        Dim Pieces As String()

        Value = Trim(Value)
        If Value = "" Then
            Return ""
        End If

        Pieces = Split(Value, "$")
        If Key = "" Then
            Return Value
        ElseIf UBound(Pieces) = 3 AndAlso Pieces(0) = "" AndAlso Pieces(1) = "AES256CBC" Then
            ' Four parts.
            Dim KeyBytes As Byte()
            Dim IV As Byte()
            Dim CipherText As Byte()

            KeyBytes = HexToByteArray(Key)
            IV = Convert.FromBase64String(Pieces(2))
            CipherText = Convert.FromBase64String(Pieces(3))

            Using oAES As New System.Security.Cryptography.AesManaged()
                oAES.KeySize = 256
                oAES.Mode = Security.Cryptography.CipherMode.CBC
                oAES.Padding = Security.Cryptography.PaddingMode.None
                Using oDecrypt = oAES.CreateDecryptor(KeyBytes, IV)
                    Return System.Text.Encoding.Default.GetString(oDecrypt.TransformFinalBlock(CipherText, 0, CipherText.Length)).TrimEnd(ControlChars.NullChar)
                End Using
            End Using
        Else
            Throw New Exception("The encrypted number is in an unknown format.")
        End If

        Return Value
    End Function

PHP

<?php
/**
 * Encrypt CC Number using strong encryption that is PCI compliant.
 * 
 * @param string $key Key (256 bits) as a hexadecimal string
 * @param string $value Credit card number
 *
 * @return string $AES256CBC$IV$CipherText where IV and CipherText are in Base64 format, and IV is 128 bits
 */
function encrypt_ccnumber($key, $value)
{
        $keyBytes = hex2bin($key);
        $iv = openssl_random_pseudo_bytes(16);

        $cipher = openssl_encrypt($value, "AES-256-CBC", $keyBytes,
            OPENSSL_ZERO_PADDING, $iv);
        return implode('$', array("", "AES256CBC", base64_encode($iv),
            $cipher));
}

/**
 * Decrypt CC Number using strong encryption that is PCI compliant.
 *
 * @param string $key Decryption key (256 bits) as a hexadecimal string
 * @param string $value Encrypted value in $AES256CBC$IV$CipherText format, where IV and CipherText are Base 64 encoded, and IV is 128 bits
 *
 * @return string Plaintext credit card number
 */
function decrypt_ccnumber($key, $value)
{
        $value = trim($value);
        if ($value == "")
                return "";
        $pieces = explode('$', $value);
        if ($key == "")
                return $value;
        else if (!((count($pieces) == 4) &&
            ($pieces[0] == "") &&
            ($pieces[1] == "AES256CBC")))
                throw new Exception("The encrypted number is in an unknown format.");

        $keyBytes = hex2bin($key);
        $iv = base64_decode($pieces[2]);
        $ciphertext = $pieces[3];

        return rtrim(openssl_decrypt($ciphertext, "AES-256-CBC", $keyBytes,
            OPENSSL_ZERO_PADDING, $iv), "\0");
}
?>

Python 3

from Crypto import Random
from Crypto.Cipher import AES
import base64

def encrypt_ccnumber(key, value):
    """Encrypt CC Number using strong encryption that is PCI compliant.

    Args:
        key (str): Key (256 bits) as a hexadecimal string
        value (str): Credit card number

    Returns:
        str: $AES256CBC$IV$CipherText where IV and CipherText are in Base64 format, and IV is 128 bits
    """

    keybytes = bytes.fromhex(key)
    iv = Random.new().read(16)
    aes = AES.new(keybytes, AES.MODE_CBC, iv)

    # Zero pad plaintext to multiple of block size (16 bytes)
    value += "\0"*((16 - len(value) % 16) % 16)
    ciphertext = aes.encrypt(value)

    return "$".join((
        "", "AES256CBC", base64.b64encode(iv).decode(),
        base64.b64encode(ciphertext).decode()))

def decrypt_ccnumber(key, value):
    """Decrypt CC Number using strong encryption that is PCI compliant.

    Args:
        key (str): Decryption key (256 bits) as a hexadecimal string
        value (str): Encrypted value in $AES256CBC$IV$CipherText format, where IV and CipherText are Base 64 encoded, and IV is 128 bits

    Returns:
        str: Plaintext credit card number
    """

    value = value.strip()
    if value == "":
        return ""
    pieces = value.split("$")
    if key == "":
        return value
    if not ((len(pieces) == 4) and (pieces[0] == "") and
        (pieces[1] == "AES256CBC")):
        raise Exception("The encrypted number is an unknown format.")

    keybytes = bytes.fromhex(key)
    iv = base64.b64decode(pieces[2])
    ciphertext = base64.b64decode(pieces[3])
    aes = AES.new(keybytes, AES.MODE_CBC, iv)
    plaintext = aes.decrypt(ciphertext)

    return plaintext.decode().rstrip("\0")
Tagged with:
Posted in Web Store Integration - Last modified on June 20, 2018Quinn Winkler
Still need help?
Search all content on this site, contact support at 817-870-1311, or create a support request.