Home > Enterprise >  How to decode a picture converted to base64 using CryptStringToBinary?
How to decode a picture converted to base64 using CryptStringToBinary?

Time:08-15

My doubt is about how to use the value returned by the API to reconstruct the image.

Also, does the way I'm creating the Bitmap preserve the picture transparency?

Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

LPCWSTR base64 = L"";

DWORD dwSkip;
DWORD dwFlags;
DWORD dwDataLen;

CryptStringToBinary(
    base64,
    _tcslen(base64),
    CRYPT_STRING_BASE64,
    NULL,
    &dwDataLen,
    &dwSkip,
    &dwFlags);

DWORD imageSize = dwDataLen;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, ???? , imageSize);

IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);

Gdiplus::Image image(pStream);

image.GetWidth();

int wd = image.GetWidth();
int hgt = image.GetHeight();
auto format = image.GetPixelFormat();

Bitmap* bmp = new Bitmap(wd, hgt, format);
auto gg = std::unique_ptr<Graphics>(Graphics::FromImage(bmp));
gg->Clear(Color::Transparent);
gg->DrawImage(&image, 0, 0, wd, hgt);

HICON hIcon;
bmp->GetHICON(&hIcon);

pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);

wc.hIcon = hIcon;

CodePudding user response:

My doubt is about how to use the value returned by the API to reconstruct the image.

You are calling CryptStringToBinary() only 1 time, to calculate the size of the decoded bytes. You are even allocating memory to receive the decoded bytes. But, you are not actually decoding the base64 string to produce the bytes. You need to call CryptStringToBinary() a second time for that, eg:

LPCWSTR base64 = L"...";
    
DWORD dwDataLen = 0;
    
if (!CryptStringToBinaryW(
    base64,
    wcslen(base64),
    CRYPT_STRING_BASE64,
    NULL,
    &dwDataLen,
    NULL,
    NULL))
{
    // error handling...
}
    
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, dwDataLen);
if (!hMem)
{
    // error handling...
}

LPVOID pImage = ::GlobalLock(hMem);
    
if (!CryptStringToBinaryW(
    base64,
    wcslen(base64),
    CRYPT_STRING_BASE64,
    pImage,
    &dwDataLen,
    NULL,
    NULL))
{
    // error handling...
}

::GlobalUnlock(hMem);

// use hMem as needed...

::GlobalFree(hMem);

CodePudding user response:

we can use CreateIconFromResourceEx instead gdi . example of convert base64 to HICON (assumed that wholea icon file is converted to base64)

struct ICONDIRENTRY
{
    BYTE    bWidth;                 // Width of the image
    BYTE    bHeight;                // Height of the image (times 2)
    BYTE    bColorCount;            // Number of colors in image (0 if >=8bpp)
    BYTE    bReserved;              // Reserved
    WORD    wPlanes;                // Color Planes
    WORD    wBitCount;              // Bits per pixel
    DWORD   dwBytesInRes;           // how many bytes in this resource?
    DWORD   dwImageOffset;          // where in the file is this image
};

struct ICONDIR
{
    WORD            NotInFile;      // not in file
    WORD            Reserved;       // Reserved
    WORD            Type;           // resource type (IMAGE_ICON for icons)
    WORD            Count;          // how many images?
    ICONDIRENTRY    Entries[];      // the entries for each image
};

HICON LoadIcon(_In_ ICONDIR* pid, _In_ ULONG cb, _In_ ULONG cxDesired, _In_ ULONG cyDesired)
{
    if (cb < sizeof(ICONDIR) || pid->Type != IMAGE_ICON)
    {
        return 0;
    }

    if (ULONG Count = pid->Count)
    {
        if (cb < sizeof(ICONDIR)   Count * sizeof(ICONDIRENTRY))
        {
            return FALSE;
        }

        ICONDIRENTRY* Entry = pid->Entries;
        do 
        {
            if (Entry->bWidth == cxDesired && Entry->bHeight == cyDesired)
            {
                if (Entry->wPlanes != 1)
                {
                    return 0;
                }

                switch (Entry->bColorCount)
                {
                case 0:
                case 1:
                case 4:
                case 8:
                case 16:
                case 24:
                case 32:
                    break;
                default:
                    return 0;
                }

                DWORD dwImageOffset = Entry->dwImageOffset   FIELD_OFFSET(ICONDIR, Reserved);

                if (cb < dwImageOffset)
                {
                    return 0;
                }

                DWORD dwBytesInRes = Entry->dwBytesInRes;

                if (cb - dwImageOffset < dwBytesInRes)
                {
                    return 0;
                }
                return CreateIconFromResourceEx((PBYTE)pid   dwImageOffset, dwBytesInRes, TRUE, 0x00030000, cxDesired, cyDesired, 0);
            }
        } while (Entry  , --Count);
    }

    return 0;
}

HICON LoadIcon(_In_ PSTR psz, _In_ ULONG cch, _In_ int cxDesired, _In_ int cyDesired)
{
    ULONG cb = 0;
    PUCHAR pb = 0;
    PUCHAR buf = 0;

    HICON hi = 0;

    while (CryptStringToBinaryA(psz, cch, CRYPT_STRING_BASE64, pb, &cb, 0, 0))
    {
        if (pb)
        {
            hi = LoadIcon((ICONDIR*)buf, cb   FIELD_OFFSET(ICONDIR, Reserved), cxDesired, cyDesired);
            break;
        }

        if (cb < sizeof(ICONDIR))
        {
            break;
        }

        if (!(buf = new UCHAR[cb   FIELD_OFFSET(ICONDIR, Reserved)]))
        {
            break;
        }

        pb = buf   FIELD_OFFSET(ICONDIR, Reserved);
    }
    
    if (buf) delete [] buf;

    return hi;
}

for convert icon file to base 64 can use next code:

BOOL IsValidIcon(_In_ ICONDIR* pid, _In_ ULONG cb)
{
    if (cb < sizeof(ICONDIR) || pid->Type != IMAGE_ICON)
    {
        return FALSE;
    }

    if (ULONG Count = pid->Count)
    {
        if (cb < sizeof(ICONDIR)   Count * sizeof(ICONDIRENTRY))
        {
            return FALSE;
        }

        ICONDIRENTRY* Entry = pid->Entries;
        do 
        {
            if (Entry->wPlanes != 1)
            {
                return FALSE;
            }

            switch (Entry->bColorCount)
            {
            case 0:
            case 1:
            case 4:
            case 8:
            case 16:
            case 24:
            case 32:
                break;
            default:
                return FALSE;
            }

            DWORD dwImageOffset = Entry->dwImageOffset   FIELD_OFFSET(ICONDIR, Reserved);

            if (cb < dwImageOffset)
            {
                return FALSE;
            }

            DWORD dwBytesInRes = Entry->dwBytesInRes;

            if (cb - dwImageOffset < dwBytesInRes)
            {
                return FALSE;
            }

        } while (Entry  , --Count);
    }

    return FALSE;
}

BOOL mi(_In_ PCWSTR lpIconName, _Out_ PSTR* ppsz, _Out_ ULONG* pcch)
{
    HANDLE hFile = CreateFileW(lpIconName, FILE_GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }

    BOOL f = FALSE;
    FILE_STANDARD_INFO fsi;
    if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fsi, sizeof(fsi)))
    {
        if ((ULONG64)fsi.EndOfFile.QuadPart - 1 < 0x100000)
        {
            if (PBYTE buf = new UCHAR[fsi.EndOfFile.LowPart   FIELD_OFFSET(ICONDIR, Reserved)])
            {
                PBYTE pb = buf   FIELD_OFFSET(ICONDIR, Reserved);

                if (ReadFile(hFile, pb, fsi.EndOfFile.LowPart, &fsi.EndOfFile.LowPart, 0) &&
                    IsValidIcon((ICONDIR*)buf, fsi.EndOfFile.LowPart)   FIELD_OFFSET(ICONDIR, Reserved))
                {
                    ULONG cch = 0;
                    PSTR psz = 0;
                    while (CryptBinaryToStringA(pb, fsi.EndOfFile.LowPart, CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF, psz, &cch))
                    {
                        if (psz)
                        {
                            *pcch = cch, *ppsz = psz, psz = 0, f = TRUE;
                            break;
                        }

                        if (!(psz = new CHAR[cch]))
                        {
                            break;
                        }
                    }

                    if (psz)
                    {
                        delete [] psz;
                    }

                }
                delete [] buf;
            }
        }
    }

    CloseHandle(hFile);

    return f;
}

for test icon:

ULONG cch;
PSTR psz;
if (mi(L"...ico", &psz, &cch))
{
    HICON hi = LoadIcon(psz, cch, 48, 48);
    delete [] psz;

    if (hi)
    {
        TASKDIALOGCONFIG TaskConfig = { 
            sizeof(TaskConfig), 0, 0, TDF_USE_HICON_MAIN, TDCBF_CLOSE_BUTTON, 0, {hi}
        };
        TaskDialogIndirect(&TaskConfig, 0, 0, 0);

        DestroyIcon(hi);
    }
}
  • Related