basCryptoSys.bas
module.
You can make your own mind up which way is easier for you (we prefer the Hex version in VBA).
2017-03-23:
The same in C# code TdeaBytesAndHex.c
using the
Cipher.Encrypt()
method introduced in CryptoSys API version 5.0.
See also Creating a C# command-line app with CryptoSys API
ENCRYPTION: Given string of text to be encrypted... +---+---+---+---+---+---+---+---+---+---+---+---+----+ szPlain: |'H'|'e'|'l'|'l'|'o'|' '|'w'|'o'|'r'|'l'|'d'|'!'|'\0'| (zero-terminated string) +---+---+---+---+---+---+---+---+---+---+---+---+----+ Convert string to bytes... +---+---+---+---+---+---+---+---+---+---+---+---+ lpPlain: |48 |65 |6C |6C |6F |20 |77 |6F |72 |6C |64 |21 | (len=12) +---+---+---+---+---+---+---+---+---+---+---+---+ Pad to next multiple of block length... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ lpBlock: |48 |65 |6C |6C |6F |20 |77 |6F |72 |6C |64 |21 |04 |04 |04 |04 | (len=16) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Encrypt the block... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ lpCipher:|80 |C7 |33 |28 |BC |58 |9F |05 |9D |5B |B0 |5C |6C |EF |7A |4B | (len=16) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ DECRYPTION: Given ciphertext bytes to be decrypted... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ lpCipher:|80 |C7 |33 |28 |BC |58 |9F |05 |9D |5B |B0 |5C |6C |EF |7A |4B | (len=16) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Decrypt... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ lpBlock: |48 |65 |6C |6C |6F |20 |77 |6F |72 |6C |64 |21 |04 |04 |04 |04 | (len=16) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Strip the padding... +---+---+---+---+---+---+---+---+---+---+---+---+ lpCheck: |48 |65 |6C |6C |6F |20 |77 |6F |72 |6C |64 |21 | (len=12) +---+---+---+---+---+---+---+---+---+---+---+---+ Convert bytes to string... +---+---+---+---+---+---+---+---+---+---+---+---+----+ szCheck: |'H'|'e'|'l'|'l'|'o'|' '|'w'|'o'|'r'|'l'|'d'|'!'|'\0'| (zero-terminated string) +---+---+---+---+---+---+---+---+---+---+---+---+----+
Note that the output from the encryption operation (lpCipher, nCipher) must not be converted back to or treated as a 'text' string because it could contain a zero byte. This problem is particularly pernicious because only certain outputs will fail, leading to much scratching of heads and unfounded accusations of bugs to the purveyors of cryptographic APIs.
IV=FEDCBA9876543210 CT=80C73328BC589F059D5BB05C6CEF7A4BOr just prepend the IV and send as one hexadecimal string
FEDCBA987654321080C73328BC589F059D5BB05C6CEF7A4Bprovided the recipient knows that the first 8 bytes are the IV. Or send the same data in base64, which is shorter but case-sensitive
/ty6mHZUMhCAxzMovFifBZ1bsFxs73pLOr save as a binary file and send that.
char
types
terminated by a zero character. The length of the string can be determined just by examining it.
Before encryption we need to (1) convert this to an array of bytes and (2)
add padding so that the length of the input to the encryption function is an exact multiple of the block size,
in this case 8.
A byte array consists of a sequence of unsigned char
types, often typedef'd to BYTE
.
With byte arrays in C we also need to keep a separate variable for the length
(because a byte array could contain a zero value and so strlen does not work).
The quick-and-dirty way to convert an ANSI string to a byte array is easy in C using pointers and typecasts. Too easy, unfortunately, and this "trick" is disallowed in more modern languages.
char szPlain[] = "Hello world!"; unsigned char *lpPlain = NULL; long nPlain; lpPlain = (unsigned char*)szPlain; nPlain = strlen(szPlain);A stricter alternative method might allocate a completely separate set of data, like this
nPlain = strlen(szPlain); lpPlain = malloc(nPlain); memcpy(lpPlain, szPlain, nPlain);just don't forget to free it.
Both char
and unsigned char
types are stored in 8-bit octets.
These two types may be treated identically on your system or they may not be.
Unfortunately you can usually get away with treating them as the same thing and this can result in very muddled
(and buggy) code.
We make it a very strict rule to distinguish carefully between 'text' strings and 'byte' arrays in our own code because
we have been caught out too many times. Conceptually they are different beasts so treat them differently.
'Hello world!'but when we look at them as byte arrays we have completely different data:
48656C6C6F20776F726C6421 480065006C006C006F00200077006F0072006C0064002100and the padded input blocks and resulting ciphertext are also completely different even though the key and IV are the same:
IB=48656C6C6F20776F726C642104040404 CT=80C73328BC589F059D5BB05C6CEF7A4B
IB=480065006C006C006F00200077006F0072006C00640021000808080808080808 CT=1DD5541870B064E2B419936FD0660FCB947F629F93A78D84B197BA23F54CF4A0
Note how we convert the decrypted plaintext bytes (lpBlock, nPlain) back to a Unicode string
nwchars = nPlain / sizeof(wchar_t); lpszCheck = malloc((nwchars + 1) * sizeof(wchar_t)); memcpy(lpszCheck, lpBlock, nPlain); lpszCheck[nwchars] = L'\0';
The Unicode example above was carried out on an Intel computer that stores its data in `little-endian` format. Had we carried this out on a `big-endian` computer like a Motorola, the same text string in byte format would look like
00480065006C006C006F00200077006F0072006C00640021and the ciphertext would be completely different:
IB=00480065006C006C006F00200077006F0072006C006400210808080808080808 CT=D0098AC5D692D06C93BA1ACC437C8A28ADE7EFE87051B2D51B2AA3AFBF0AF4C2For two users passing data between systems of the same 'endianness' this problem would not arise, but it would if the systems were different.
For more information, please send us a message.
This page first published 18 August 2006. Last updated 15 August 2025