Example 1: Symmetric key generation
Here we generate two 128 bit keys, returning the first one in a hex string format, and writing the other in raw binary format to a file. Note in both cases we are using the dst parameter (and not the key parameter) for the returned key. The src, decoding, cipher, and key parameters aren't used. Also note that although hex format for binary keys is convenient for string handling and printing purposes, handling it directly in an X variable would be the most compact (and would also eliminate the possibility of it being accidentally exposed in a subroutine trace).
++include ashinc:crypto.def
map1 keybits,b,2,128 ! 128 bit key length
map1 hexkey$,s,32 ! 128 bits = 16 bytes (32 hex chars)
map1 status,f ! return status
! return hex key directly in hexkey$ (dst) param:
xcall CRYPTO, CRYPTOP_GENKEY, status, "", "", hexkey$, "hex", &
CRYPF_NONE, CRYPTO_CIPHER_NA,"", keybits
if status = 32 then ! (expecting 32 hex characters)
print "key (in hex format):";hexkey$
else
print "Error: ";status
endif
! now generate another, writing it in binary format to file
xcall CRYPTO, CRYPTOP_GENKEY, status, "", "", "testkey.bin", "", &
CRYPF_DSTFILE, CRYPTO_CIPHER_NA, "", keybits
if status = 16 then ! (expecting 16 bytes)
print "key written to testkey.bin"
else
print "Error: ";status
endif
Note: CRYPTOP_GENKEY provides the same functionality as the openssl rand utility. In fact, the second example (above) is directly equivalent to:
openssl rand 16 > test.key
Example 2: Encrypt/decrypt a string using AES-128-ECB
Here we generate a new random key (in raw binary format) and use it to encrypt a string, which is then encoded in base64. (This is a typical approach to encrypting sensitive fields within an XML web service request.) Error checking has been removed to simplify the example.
++include ashinc:crypto.def
map1 keybits,b,2,128 ! 128 bit key
map1 symkey,x,16 ! raw symmetric key (128 bits = 16 bytes)
map1 src$,s,50 ! plain text string to encrypt
map1 enc$,s,90 ! encrypted/encoded output
map1 dec$,s,50 ! decrypted string (to test decryption)
map1 status,f ! return status
! generate key into key var
xcall CRYPTO, CRYPTOP_GENKEY, status, "", "", symkey, "", &
CRYPF_NONE, CRYPTO_CIPHER_NA,"", keybits
! now use it to encrypt the src$
src$ = "This is a secret"
xcall CRYPTO, CRYPTOP_ENCRYPT, status, src$, "", enc$, "base64", &
CRYPF_SRCTEXT, CRYPTO_CIPHER_AES,symkey, keybits, CRYPTO_MODE_ECB, CRYPTO_PAD_PKCS5
print "status=";status;" cipher text = ";enc$
! now decrypt it to see if we get the original back
! (we'll use out of the encryption as the input of the decryption)
xcall CRYPTO, CRYPTOP_DECRYPT, status, enc$, "base64", dec$, "", &
CRYPF_SRCTEXT, CRYPTO_CIPHER_AES, symkey, keybits, CRYPTO_MODE_ECB, CRYPTO_PAD_PKCS5
if dec$ = src$ then
print "Success: decrypted string matches original!"
else
print "Error: decrypted string doesn't match original!"
endif
Note that the dst parameter here must be large enough to hold the encrypted/encoded message. For block ciphers like AES, the raw encrypted message length will be rounded up to the next multiple of the block (key) size, so for a 50 byte source, the encrypted version could be 64 bytes long. The base64 encoding will increase it further by a ratio of 4/3 (rounded up to next multiple of 4), so we would need 88 bytes to be safe. You can use a dynamic string output variable, but you must pre-initialize it to at least the minimum required size (if in doubt, err on the high side!).
Example 3: Encrypt/decrypt a file using AES-128-ECB
This is similar to Example 2 except that we'll use files for the source and destination, and a hex string variable for the key. This will be equivalent to the following openssl command strin1g:
"openssl enc -aes-128-ecb -in msg.dat -out msg.enc -a -e -nosalt -K " + hexkey$
++include ashinc:crypto.def
map1 keybits,b,2,128 ! 128 bit key
map1 hexkey$,s,32 ! hex encoded key
map1 status,f ! return status
map1 src$,s,50 ! source filespec
map1 dst$,s,50 ! dest filespec
map1 decoding$,s,10 ! source file decoding
map1 encoding$,s,10 ! dest file encoding
map1 cflags,b,4
! generate 128 bit key in hex format
xcall CRYPTO, CRYPTOP_GENKEY, status, "", "", hexkey$, "hex", CRYPF_NONE, CRYPTO_CIPHER_NA,"", keybits
src$ = "msg.dat"
decoding$ = "" ! no decoding of source (treat as raw)
dst$ = "msg.enc"
encoding$ = "base64" ! encode the dest after encryption
cflags = CRYPF_KEYHEX ! key is in hex format
cflags = cflags or CRYPF_SRCFILE ! source is a file
cflags = cflags or CRYPF_DSTFILE ! destination is a file
? "encrypting ";src$;" into ";dst$;" ..."
xcall CRYPTO, CRYPTOP_ENCRYPT, status, src$, decoding$, dst$, encoding$, &
cflags, CRYPTO_CIPHER_AES, hexkey$, keybits, CRYPTO_MODE_ECB, CRYPTO_PAD_PKCS5
if status >= 0 then
print status;" bytes output to ";dst$
else
print "error: ";status
end
endif
! now decrypt it into msg.dec (which should match the original msg.dat)
src$ = "msg.enc" ! source for decryption is the encrypted file
decoding$ = "base64" ! pre-decode encrypted file before decryption
dst$ = "msg.dec" ! decrypted file
encoding$ = "" ! no post-decryption encoding
? "decrypting ";src$;" into ";dst$; " ..."
xcall CRYPTO, CRYPTOP_DECRYPT, status, src$, decoding$, dst$, encoding$, &
cflags, CRYPTO_CIPHER_AES, hexkey$, keybits, CRYPTO_MODE_ECB, CRYPTO_PAD_PKCS5
if status >= 0 then
print "Decrypted file (";dst$;") should now match original"
else
print "error: ";status
endif
Note that in this case, it isn't necessary to specify whether the files contain text or binary data. (Treating it as binary is always safe.) Also note that for the decryption, the decoding option must match the encoding for the encryption. (We base64-encoded the encrypted file; so we need to first base64-decode it before decrypting.)
Example 4: RSA encrypt/decrypt a string
Here we RSA-encrypt the 16 byte raw symmetric key (generated in Example 2), using a public key stored in the file testpub.pem. This is similar to the openssl rsautl command sequence:
echo $KEY | openssl rsautl -encrypt -inkey tetpub.pem -pubin | openssl enc -base64
Note that the source for this operation may itself be a key (e.g. the symmetric key used to encrypt something else that we want to transmit secretly to the remote side), but from the perspective of the RSA encryption operation, it's just an arbitrary message to encrypt. This technique would apply to RSA public/private key encryption of any message, provided it is no longer than the RSA modulus (typically either 1024 or 2048 bits, i.e. 128/256 bytes).
++include ashinc:crypto.def
map1 symkey,x,16 ! raw 128 bit symmetric key (generated in Example 2)
map1 symkey2,x,16 ! (to test decryption)
map1 status,f ! return status
map1 dst$,s,346 ! dest string
map1 keyfile$,s,30 ! PEM file (public or private key)
map1 cflags,b,4
cflags = CRYPF_KEYFILE ! key is a file (cryptopub.pem)
cflags = cflags or CRYPF_PUBKEY ! use the public key
keyfile$ = "testpub.pem" ! public key file
if lookup(keyfile$)= 0 then
print keyfile$;" not found! See doc notes to create"
end
endif
xcall CRYPTO, CRYPTOP_ENCRYPT, status, symkey, "", dst$, "base64", &
cflags, CRYPTO_CIPHER_RSA, keyfile$
if status >= 0
print "Encryption success: ";status;" encrypted/encoded bytes output"
else
print "Encryption error: status=";status
end
endif
! now decrypt it into rawkey2 (which should match the original msg.dat)
! (this would normally be done by the remote counterpart)
cflags = cflags and not CRYPF_PUBKEY ! clear PUB flag (we're using private key here)
keyfile$ = "test.pem" ! private key file
xcall CRYPTO, CRYPTOP_DECRYPT, status, dst$, "base64", symkey2, "", &
cflags, CRYPTO_CIPHER_RSA, keyfile$
if status >= 0 and symkey = symkey2 then
print "Decryption success: decrypted symkey matches the original"
else
print "Decryption error: status=";status
endif
Notes:
• | The size of the encrypted output for RSA encryption is always the same as the RSA modulus (1024 or 2048 bits), adjusted for the encoding. In this case, since base64 encoding expands the message by 4/3, we need at least 256 * 4/3 bytes (rounded up to next multiple of 4) or 344 bytes to hold the output. We mapped dst$ as 346 here to allow for a trailing null and even size. |
• | The keybits parameter is not used with the RSA cipher |
• | You can generate the test.pem (private key) and testpub.pem (public key) files for this example using openssl as follows: |
openssl genrsa -out test.pem 2048
openssl rsa -in test.pem -pubout > testpub.pem
Example 5: Encode binary data
Here we encode 16 bytes of binary data as a 32 byte hex string:
++include ashinc:crypto.def
map1 xdata
map2 xdata'bytes(16),b,1
map1 hexdata$,s,32 ! hex encoded key (from example 1)
map1 status,f ! return status
map1 x,f
for x = 1 to 16 ! generate 16 binary bytes
xdata'bytes(x) = x
next x
! encode it in hex
xcall CRYPTO, CRYPTOP_ENCODE, status, xdata, "", hexdata$, "hex", CRYPF_NONE
? "hex encoded bytes: ";hexdata$