1

I'm using CNG to generate a hash. Result of BCryptFinishHash call is MD5 of a input in hex form. Example:

char *outHash = "\x02\x34\x75\01..."

I want to convert it to printable string: 02347501...

How can I do that?

Keet Kate
  • 107
  • 8

6 Answers6

3

To encode a byte array in hex and write the encoded data to a std::string, do this:

static inline char
hex_digit(unsigned int n)
{
    if (n < 10) return '0' + n;
    if (n < 16) return 'a' + (n - 10);
    abort();
}

std::string
encode_bytes(const unsigned char *bytes, size_t len)
{
    std::string rv;
    rv.reserve(len * 2);
    for (size_t i = 0; i < len; i++) {
        rv.push_back(hex_digit((bytes[i] & 0xF0) >> 4));
        rv.push_back(hex_digit((bytes[i] & 0x0F) >> 0));
    }
    return rv;
}

Note that you must know the length of the byte array. It is not safe to treat it as a NUL-terminated "C string", because binary data can contain internal zero bytes. To know the length of a hash generated by CNG, call BCryptGetProperty to get the BCRYPT_HASH_LENGTH property.

zwol
  • 135,547
  • 38
  • 252
  • 361
1

we can use CryptBinaryToString here with CRYPT_STRING_HEXASCII or CRYPT_STRING_HEX or CRYPT_STRING_HEXRAW or CRYPT_STRING_HEX | CRYPT_STRING_NOCRLF or CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF depen how you want format string. for example

void print(PUCHAR pbHash, ULONG cbHash, DWORD dwFlags = CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF)
{
    ULONG cch = 0;
    if (CryptBinaryToStringW(pbHash, cbHash, dwFlags, 0, &cch))
    {
        if (PWSTR sz = (PWSTR)_malloca(cch * sizeof(WCHAR)))
        {
            if (CryptBinaryToStringW(pbHash, cbHash, dwFlags, sz, &cch))
            {
                DbgPrint("%S\n", sz);
            }
            _freea(sz);
        }
    }
}
RbMm
  • 31,280
  • 3
  • 35
  • 56
0

If you need an easy, one time solution, this is a useful tool: https://codebeautify.org/hex-string-converter

However, if you're looking to do this within your code itself, I found this from an earlier thread (AKA, this is not my work but that of @KEINE LUST from here )

int main(void)
{
unsigned char readingreg[4];
readingreg[0] = 0x4a;
readingreg[1] = 0xaa;
readingreg[2] = 0xaa;
readingreg[3] = 0xa0;
char temp[4];

sprintf(temp, "%x", readingreg[0]);
printf("This is element 0: %s\n", temp);
return 0;
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
0

You can print it like this:

for(const char *wsk=outHash; *wsk; ++wsk){
    printf("%02hhx", *wsk);
}

Edit based that cstring can have 0x00 numbers.
C

const char outHash[] = "\x02\x34\x75";
const int size = sizeof(outHash)/sizeof(char) - 1;
for(int i = 0; i < size; ++i){
    printf("%02hhx", outHash [i]);
}

C++

std::string outHash = "\x02\x34\x75";
for(int i = 0; i < outHash.size(); ++i) {
    printf("%02hhx", outHash [i]);
}
  • BTW `hh` is not really needed, as in variadic functions all signed (unsigned) integral types smaller than `int` are promoted to `int` (`unsigned int`) anyway. You can use straight `"%02x"`, write less and remain compatible with older compilers. – Matteo Italia Jan 24 '19 at 22:00
  • @Eric Postpischil hhx is for unsigned char if we want to display it as hex number. http://www.cplusplus.com/reference/cstdio/printf/ watch this site flags and below you have length specifiers – Michał Marszałek Jan 24 '19 at 22:15
  • 3
    This has a fairly serious bug: the binary form of a cryptographic hash is _not_ a NUL-terminated string and may contain internal 0x00 bytes. – zwol Jan 24 '19 at 22:16
  • Then you need only change *wsk as checking size of string but if this was c++ I would use std::string as container. If c and string have 0x00 chars I would have to sizeof(outHash)/sizeof(char) to get numbers of characters. – Michał Marszałek Jan 24 '19 at 22:19
  • @MatteoItalia `hh` is useful when `outHash[]` have have negative values. Else one can get output like `FFFFFF80` rather than `80`. – chux - Reinstate Monica Jan 24 '19 at 22:37
  • @chux: aah right, I always avoided the problem by storing byte-data in `unsigned char` arrays. – Matteo Italia Jan 24 '19 at 22:46
  • @EricPostpischil How is `hhx` not a correct match for `char`? Spec says "hh Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument". – chux - Reinstate Monica Jan 24 '19 at 22:47
  • @MichałMarszałek: The issue is not what we want the display to be but whether the argument matches the conversion specification. If you pass an `int` for `%x`, the behavior is undefined, because `%x` expects an `unsigned int`. The fact that you are asking it to display in hexadecimal is irrelevant; there are requirements that the arguments match the specifiers. However, chux may be right that `%hhx` is flexible and can accept either a `signed char` or an `unsigned char`. – Eric Postpischil Jan 24 '19 at 23:23
  • @EricPostpischil I thought that way too, yet the spec does appears quite explicit in allowing either. – chux - Reinstate Monica Jan 24 '19 at 23:24
  • @EricPostpischil Re: "If you pass an int for %x, the behavior is undefined" §6.5.2.2 6 excepts that when the `int` is positive. – chux - Reinstate Monica Jan 24 '19 at 23:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187292/discussion-between-chux-and-eric-postpischil). – chux - Reinstate Monica Jan 24 '19 at 23:27
0

Loop over the characters and print the numerical value (in hex).

#include <iostream>
#include <iomanip>

int main()
{
    char*  outHash = "\x02\x34\x75\x01\x23\xff";  // Get from your Hash function.
    int    sizeOfHash = 6;                        // Use appropriate size for BCryptFinishHash()


    // Set up the characteristics of the stream.
    // setw(2):       Each printed object will use a min width of 2
    // setfill('0'):  If the object is less than 2 char then fill the space with '0'
    // hex:           Print numbers in hex.
    std::cout << std::setw(2) << std::setfill('0') << std::hex;

    // Create a view of the object.
    // Makes it simpler to loop over.
    std::string_view view(outHash, sizeOfHash);

    // Loop over the string.
    for(unsigned char val: view) {
        // Convert to `unsigned char` to make sure you don't print
        // negative numbers. Then convert from there to `int` so that
        // the `std::hex will kick in and convert to hex value.
        std::cout << static_cast<int>(val);
    }   
    std::cout << "\n";
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

I am working on C++ wrapper around Windows Crypto API & CNG which I am using in my projects. I plan to move all of it to github but for now it is just a work in progress, but you can find it useful for Crypto basics like HEX / Base64 encode / decode etc.

https://github.com/m4x1m1l14n/Crypto

You can use Crypto::Hex::Encode() method to achieve what you want.

#include <Crypto\Hex.hpp>
#include <Crypto\Random.hpp>

using namespace m4x1m1l14n;

char arr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0x99, 0x00 };

encoded = Crypto::Hex::Encode(arr, sizeof(arr));

/* encoded = "aabbccdd9900" */

Also you can use wrapper for MD5 which is located in Hash namespace, like this. (If you are not using large amount of data)

#include <Crypto\Hex.hpp>
#include <Crypto\Hash.hpp>

using namespace m4x1m1l14n;

encoded = Crypto::Hex::Encode(Crypto::Hash::MD5("Whatever you want to hash"));