What you are asking is a straight application for Format Preserving Encryption, which builds ciphers which input and output are in a constrained format (generically: common to input and output, hence preserved). The FPE field has many articles with proven techniques; and proposed standards, including BPS and SP800-38G Draft.
Specifically, it looks like you want a cipher on the space of 20-characters alphanumeric codes, perhaps with some restriction or special rule in consideration of the operator entering the value into the server; if you restrict to uppercase and digits, and assimilate 0125
to OIZS
, you are left with $26+10-4=32=2^5$ symbols, and your message space has a nice $100$-bit size. Among many simple techniques to build a cipher on this space, you could use a Feistel construction with AES as the round function. Adapting an earlier answer:
The AES block cipher is used with a fixed secret key
parameters:
B = 50 // half the number of bits per block
N = 8 // number of rounds (could be lowered)
enciphering plaintext block P, assumed to be 2*B bits
L = P>>B // extract left B bits
R = P & ((1<<B)-1) // extract right B bits
for I from 1 to N // round loop
encipher ((I<<B) | R) with AES, keep the B right bits H
L = L ^ H
exchange R and L
C = (R<<B) | L // append the halves, with R on the left
output ciphertext block C
deciphering ciphertext block C, assumed to be 2*B bits
L = C>>B // extract left B bits
R = C & ((1<<B)-1) // extract right B bits
for I from N downto 1 // round loop
encipher ((I<<B)|R) with AES, keep the B right bits H
L = L ^ H
exchange R and L
P = (R<<B) | L // append the halves, with R on the left
output plaintext block P
Notice that after deciphering the enciphered alphanumeric code, you need to check enough of the recovered plaintext (perhaps, the last 10 symbols out of 20 must be A
) so that a wrong ciphertext (resulting in an essentially random plaintext) will fail the check with high confidence.