Decrypt data in a byte array using the specified block cipher algorithm, mode and padding. The key and initialization vector are passed as byte arrays.
Public Declare Function CIPHER_DecryptBytes Lib "diCrPKI.dll" (ByRef lpOutput As Byte, ByVal nOutBytes As Long, ByRef lpInput As Byte, ByVal nInputLen As Long, ByRef lpKey As Byte, ByVal nKeyLen As Long, ByRef lpIV As Byte, ByVal nIvLen As Long, ByVal strAlgModePad As String, ByVal nOptions As Long) As Long
nRet = CIPHER_DecryptBytes(lpOutput(0), nOutBytes, lpInput(0), nInputLen, lpKey(0), nKeyLen, lpIV(0), nIvLen, strAlgModePad, nOptions)
long __stdcall CIPHER_DecryptBytes(unsigned char *lpOutput, long nOutBytes, const unsigned char *lpInput, long nInputLen, const unsigned char *lpKey, long nKeyLen, const unsigned char *lpIV, long nIvLen, const char *szAlgModePad, long nOptions);
NULL
for ECB mode.If successful, the return value is the number of bytes in or required in the output; otherwise it returns a negative error code.
Public Function cipherDecryptBytes
(lpInput() As Byte, lpKey() As Byte, lpIV() As Byte, szAlgModePad As String, Optional nOptions As Long = 0) As Byte()
Cipher.Decrypt Method (Byte[], Byte[], Byte[], CipherAlgorithm, Mode, Padding, Cipher.Opts)
static bvec_t dipki::Cipher::Decrypt (const bvec_t &data, const bvec_t &key, const bvec_t &iv, Alg alg, Mode mode=Mode::ECB, Padding pad=Padding::Default, Opts opts=Opts::None)
static bvec_t dipki::Cipher::Decrypt (const bvec_t &data, const bvec_t &key, const bvec_t &iv, const std::string algModePad, Opts opts=Opts::None)
static Cipher.decrypt_block(data, key, iv=None, alg=Alg.TDEA, mode=Mode.ECB)
static Cipher.decrypt(data, key, iv=None, algmodepad='', alg=None, mode=Mode.ECB, pad=Pad.DEFAULT, opts=Opts.DEFAULT)
static Cipher.decrypt_block(data, key, iv=None, alg=Alg.TDEA, mode=Mode.ECB)
The algorithm/mode/padding must be specified using either the szAlgModePad string or nOptions parameter, but not both (see Specifying the algorithm, mode and padding for generic block cipher functions). The length of key lpKey must be exactly the required key size, and the length of the IV, if required, exactly the block size. See Valid key and block sizes.
[New in v12.3] You can find the required output length in bytes by setting
nOutBytes to zero or lpOutput to 0 (or NULL
in C or ByVal 0&
in VBA).
For ECB and CBC cipher modes, the return value is now (as of [v12.3]) the exact length of plaintext after padding has been removed.
NULL
to find the exact decrypted length.
It is an error (DECRYPT_ERROR
) if the padding bytes after decryption are not as expected according to the padding method specified.
If the cipher mode is ECB or CBC, it is an error (BAD_LENGTH_ERROR
)
if the length of the input is not an exact multiple of the block size.
The input buffer lpInput may point to the same location as the output buffer.
If so, the input ciphertext data will be overwritten by the output (and the user must truncate to remove any padding bytes).
Defaults: If padding is not specified then the default padding method depends on the cipher mode:
pkcs5padding will be used for ECB and CBC mode and nopadding for all other modes.
The default cipher mode is ECB.
Thus "aes128"
is the same as "aes128/ecb/pkcs5padding"
.
Dim key() As Byte Dim iv() As Byte Dim ct() As Byte Dim ok() As Byte Dim dt() As Byte Dim ctlen As Long Dim dtlen As Long Dim keylen As Long Dim ivlen As Long Dim algstr As String ' INPUT for known test vector algstr = "Aes128/CBC/OneAndZeroes" Debug.Print algstr key = cnvBytesFromHexStr("0123456789ABCDEFF0E1D2C3B4A59687") iv = cnvBytesFromHexStr("FEDCBA9876543210FEDCBA9876543210") keylen = UBound(key) + 1 ivlen = UBound(iv) + 1 Debug.Print ("KY=" & cnvHexStrFromBytes(key)) Debug.Print ("IV=" & cnvHexStrFromBytes(iv)) ct = cnvBytesFromHexStr("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E1771D4CDA34FBFB7E74B321F9A2CF4EA61B") ctlen = UBound(ct) + 1 Debug.Print ("CT=" & cnvHexStrFromBytes(ct)) ' OK = "Now is the time for all good men to" ok = cnvBytesFromHexStr("4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F") ' DECRYPT - OLD METHOD Debug.Print "Decrypt - old method with truncation" ' 1. Set output buffer to same length as input (final output will always be shorter for CBC mode) dtlen = ctlen ReDim dt(dtlen - 1) ' 2. Perform decryption including padding into output buffer dtlen = CIPHER_DecryptBytes(dt(0), dtlen, ct(0), ctlen, key(0), keylen, iv(0), ivlen, algstr, 0) Debug.Print "CIPHER_DecryptBytes returns " & dtlen Debug.Assert dtlen > 0 ' 3. Re-dimension the output to the correct length ReDim Preserve dt(dtlen - 1) Debug.Print ("DT=" & cnvHexStrFromBytes(dt)) Debug.Print ("DT='" & StrConv(dt, vbUnicode) + "'") ' DECRYPT - NEW METHOD [v12.3] Debug.Print "Decrypt - new method with exact length" ' 1. Find exact output length using null/zero-length output dtlen = CIPHER_DecryptBytes(ByVal 0&, 0, ct(0), ctlen, key(0), keylen, iv(0), ivlen, algstr, 0) Debug.Print "CIPHER_DecryptBytes(NULL) returns " & dtlen Debug.Assert dtlen > 0 ' 2. Dimension output buffer to exact length ReDim dt(dtlen - 1) ' 3. Perform decryption - no need to truncate dtlen = CIPHER_DecryptBytes(dt(0), dtlen, ct(0), ctlen, key(0), keylen, iv(0), ivlen, algstr, 0) Debug.Print "CIPHER_DecryptBytes returns " & dtlen Debug.Assert dtlen > 0 Debug.Print ("DT=" & cnvHexStrFromBytes(dt)) Debug.Print ("DT='" & StrConv(dt, vbUnicode) + "'") Debug.Print "Check actual padding by decrypting with NoPadding..." algstr = "Aes128/CBC/NoPadding" Debug.Print algstr dtlen = ctlen ReDim dt(dtlen - 1) dtlen = CIPHER_DecryptBytes(dt(0), dtlen, ct(0), ctlen, key(0), keylen, iv(0), ivlen, algstr, 0) Debug.Print "CIPHER_DecryptBytes(NoPadding) returns " & dtlen Debug.Print ("DT=" & cnvHexStrFromBytes(dt))
This should result in output as follows:
Aes128/CBC/OneAndZeroes KY=0123456789ABCDEFF0E1D2C3B4A59687 IV=FEDCBA9876543210FEDCBA9876543210 CT=C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E1771D4CDA34FBFB7E74B321F9A2CF4EA61B Decrypt - old method with truncation CIPHER_DecryptBytes returns 35 DT=4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F DT='Now is the time for all good men to' Decrypt - new method with exact length CIPHER_DecryptBytes(NULL) returns 35 CIPHER_DecryptBytes returns 35 DT=4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F DT='Now is the time for all good men to' Check actual padding by decrypting with NoPadding... Aes128/CBC/NoPadding CIPHER_DecryptBytes(NoPadding) returns 48 DT=4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F80000000000000000000000000
Example 2: showing use of PKI_IV_PREFIX to prepend the IV before the ciphertext in the output.
Dim strCipherValue As String Dim strPlain As String Dim pt() As Byte Dim ptlen As Long Dim ct() As Byte Dim ctlen As Long Dim key() As Byte Dim keylen As Long Dim iv() As Byte Dim ivlen As Long Dim nRet As Long Dim strDT As String Dim dt() As Byte Dim dtlen As Long ' The plaintext to encrypt... strPlain = "<encryptme>hello world</encryptme>" Debug.Print "PT='" & strPlain & "'" ' We need this as a byte array... pt = StrConv(strPlain, vbFromUnicode) Debug.Print "HEX(PT)=" & cnvHexStrFromBytes(pt) ptlen = UBound(pt) + 1 Debug.Print "Len(PT)=" & ptlen ' 128-bit AES key ' (Usually generated at random but here we use a fixed test one) key = cnvBytesFromHexStr("6162636465666768696A6B6C6D6E6F70") keylen = UBound(key) + 1 Debug.Print "HEX(KEY)=" & cnvHexStrFromBytes(key) ' Generate a random 128-bit IV for AES encryption ivlen = PKI_BLK_AES_BYTES ReDim iv(ivlen - 1) nRet = RNG_Bytes(iv(0), ivlen, "", 0) Debug.Print "HEX(IV)=" & cnvHexStrFromBytes(iv) ' Encrypt using AES-128 with IV prefixed to ciphertext ' How many bytes in the encrypted output? ctlen = CIPHER_EncryptBytes(ByVal 0&, 0, pt(0), ptlen, key(0), keylen, iv(0), ivlen, "aes128/cbc/pkcs5", PKI_IV_PREFIX) Debug.Print "Len(IV|CT)=" & ctlen Debug.Assert (ctlen > 0) ' Allocate the buffer ReDim ct(ctlen - 1) ' Encrypt to output buffer ctlen = CIPHER_EncryptBytes(ct(0), ctlen, pt(0), ptlen, key(0), keylen, iv(0), ivlen, "aes128/cbc/pkcs5", PKI_IV_PREFIX) Debug.Assert (ctlen > 0) Debug.Print "IV|CT=" & cnvHexStrFromBytes(ct) ' Encode CT using base64 strCipherValue = cnvB64StrFromBytes(ct) ' Output the CipherValue element ' (Note this will be different each time) Debug.Print "<CipherValue>" & strCipherValue & "</CipherValue>" ' ------------------------------------------ ' PART 2 - decrypt the given ciphertext Debug.Print Debug.Print "DECRYPTING..." ' INPUT: strCipherValue, key ' Decode base64 to byte array ct = cnvBytesFromB64Str(strCipherValue) ctlen = UBound(ct) + 1 Debug.Print "IV|CT=" & cnvHexStrFromBytes(ct) ' Find decrypted output length - new behaviour in [v12.3] (previously needed to truncate) dtlen = CIPHER_DecryptBytes(ByVal 0&, 0, ct(0), ctlen, key(0), keylen, ByVal 0&, 0, "aes128/cbc/pkcs5", PKI_IV_PREFIX) Debug.Assert (dtlen > 0) ReDim dt(dtlen - 1) ' Note we don't need to specify the IV: it is included in the prefix dtlen = CIPHER_DecryptBytes(dt(0), dtlen, ct(0), ctlen, key(0), keylen, ByVal 0&, 0, "aes128/cbc/pkcs5", PKI_IV_PREFIX) Debug.Print "Len(DT)=" & dtlen Debug.Assert (dtlen >= 0) Debug.Print "DT=" & cnvHexStrFromBytes(dt) ' Convert from bytes to string strDT = StrConv(dt, vbUnicode) Debug.Print "DT='" & strDT & "'"
PT='<encryptme>hello world</encryptme>' HEX(PT)=3C656E63727970746D653E68656C6C6F20776F726C643C2F656E63727970746D653E Len(PT)=34 HEX(KEY)=6162636465666768696A6B6C6D6E6F70 HEX(IV)=556DA05DF58DE51874B032768BA99A26 Len(IV|CT)=64 IV|CT=556DA05DF58DE51874B032768BA99A26884B6CD2045916D7F1D8D88E6BB8F5520C69479D352701BBBA2E67DE03DF0319B3C83EA90093AC6C77E4709A26B46E80 <CipherValue>VW2gXfWN5Rh0sDJ2i6maJohLbNIEWRbX8djYjmu49VIMaUedNScBu7ouZ94D3wMZs8g+qQCTrGx35HCaJrRugA==</CipherValue> DECRYPTING... IV|CT=556DA05DF58DE51874B032768BA99A26884B6CD2045916D7F1D8D88E6BB8F5520C69479D352701BBBA2E67DE03DF0319B3C83EA90093AC6C77E4709A26B46E80 Len(DT)=34 DT=3C656E63727970746D653E68656C6C6F20776F726C643C2F656E63727970746D653E DT='<encryptme>hello world</encryptme>'
See example for VBA wrapper cipherEncryptBytes
in CIPHER_EncryptBytes.