十六进制转换为base64

5

我目前正在开发一个十六进制转换成Base64的工具。如果遇到奇数个十六进制数字,应该如何处理呢? 我现在做的是将每个十六进制数字作为4位数字,所以2个十六进制数字代表1个字节。如果我遇到奇数个十六进制数字,是否只需用0来填充不完整的字节?或者应该返回错误信息?


我们这里没有上下文 - 谁负责需求?这到底是用来干什么的?通常情况下我会认为这是一个错误,但这取决于具体的上下文。 - Jon Skeet
正在解决这个挑战:http://cryptopals.com/sets/1/challenges/1/ - Ogofo
如果您期望一个字节有两个十六进制数字,但实际上并没有得到它们,那么输入就是无效的且无法修复的(FFF 是什么?0FFF 还是 FFF0?)- 抛出异常。 - Alex K.
是的 - 对于这个问题,我认为如果nybbles的数量是奇数,失败是完全合理的。 - Jon Skeet
3个回答

4
我用C语言完成了这个挑战。我使用了循环来删除前导零,或者在这种情况下是“ A”(即十六进制数前的0)。
#include <stdio.h>
#include <stdlib.h>


char* hex_to_base64(char *hex, int size)
{
    int size64 = (size * 2) / 3.0;
    size64 += 1;
    char *base64 = calloc(size64, 1);
    size64 -= 1;
    for (int i = size-1; i>= 0; i-=3, size64-=2) {
        base64[size64] |= hex[i];
        if (i > 0) {
            base64[size64] |= ((hex[i - 1] << 4) & 0x3F); //0x3F is 00111111
            base64[size64 - 1] |= (hex[i - 1] >> 2);
        }
        if (i > 1) {
            base64[size64 - 1] |= ((hex[i - 2] << 2));
        }
    }
    return base64;
}



int main(int argc, char **argv)
{
    int i = 0;
    //49276D206B696C6C696E6720796F757220627261696E206C696B65206120706F69736F6E6F7573206D757368726F6F6D
    char input[] = { 4, 9, 2, 7, 6, 13, 2, 0, 6, 11, 6, 9, 6, 12, 6, 12, 6, 9, 6, 14, 6, 7, 2, 0, 7, 9, 6, 15, 7, 5, 7, 2, 2, 0, 6, 2, 7, 
        2, 6, 1, 6, 9, 6, 14, 2, 0, 6, 12, 6, 9, 6, 11, 6, 5, 2, 0, 6, 1, 2, 0, 7, 0, 6, 15, 6, 9, 7, 3, 6, 15, 6, 14, 6, 15, 7, 5, 7, 3, 
        2, 0, 6, 13, 7, 5, 7, 3, 6, 8, 7, 2, 6, 15, 6, 15, 6, 13 };
    char *output;
    int outputsize = ((sizeof(input)* 2) / 3.0) + 1;
    char *text = calloc(outputsize + 1, 1);
    char *formatted;
    output = hex_to_base64(input, sizeof(input));
    for (i = outputsize-1; i >=0; i--) {
        if (output[i] < 26) {
            text[i] = output[i] + 65;
        }
        else if (output[i] < 52) {
            text[i] = output[i] + 97 - 26;
        }
        else if (output[i] < 62) {
            text[i] = output[i] + 48 - 52;
        }
        else if (output[i] == 62) {
            text[i] = '+';
        }
        else if (output[i] == 63) {
            text[i] = '/';
        }
    }
    i = 0;
    formatted = text;
    while (text[i++] == 'A') {
        formatted++;
    }
    printf("%s\n", formatted);
    free(text);
    return 0;
}

int size64 应该是一个有符号的64位变量吗?在我使用的CPU上,int是32位的。我声明我的变量像uint32_t等,以避免混淆。 - pgibbons
这对我完全没用。在这段时间里,我本来可以自己写的,真是太糟糕了。 - pgibbons

3

在我的加密挑战中发现了这个问题。根据维基百科的说法,答案是:

  • 添加填充,使得结果字符串可以被3字节整除。
  • 将填充的4位值设置为0(根据您在进行十六进制字符串转换之前还是之后执行此操作,这是一个实际的0还是一个“0”)
  • 在生成的base64字符串中,通过相同数量的“=”标记填充的4位值的数量。

对于0x41414141的实现似乎不是解决此挑战的完整解决方案,因为从十六进制字符串到二进制的转换是手动硬编码的。此外,我不明白为什么应该删除前导零。


0

我自己用C语言编写了一个处理任何通用十六进制字符串的解决方案。
注意:由于我正在学习C语言,它可能不是100%正确或不是最佳风格。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char b64[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
               'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
               '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'  };

char *bin_from_str(const char *str, const unsigned int size) {
  char *bin = calloc(size + 1, 1);

  for(int i = 0; i <= (int)size; ++i) {
    bin[i] = str[i];
    if (str[i] > 97) { // deal with lowercase
      bin[i] -= 87;
    } else if (str[i] > 65) { // deal with uppercase
      bin[i] -= 55;
    } else if (str[i] < 58) { // deal with nums
      bin[i] -= 48;
    }
  }

  return bin;
}

char *b64_from_hexstr(const char *hexstr, const unsigned int size) {
  char *bin = bin_from_str(hexstr, size);
  char *base64 = calloc((int)ceil(size * 4 / 6.0) + 1, 1);

  int j = 0;
  for(int i = 0; i < (int)size; i+=3, j+=2) {
    base64[j] = (bin[i] << 2) | ((i + 1 < (int)size) ? (bin[i + 1] >> 2) : 0);
    base64[j + 1] = ((bin[i + 1] & 0x3) << 4) | ((i + 2 < (int)size) ? bin[i + 2] : 0);
  }

  if (size % 3 == 1) { j--; } // remove trailing A's
  for(int i = 0; i < j; ++i) {
    printf("%c", b64[(int)base64[i]]); // pretty print b64 string
  }
  for(int i = 0; i < (int)(size % 3); ++i) {
    printf("="); // add padding
  }
  printf("\n");

  free(bin);
  return base64;
}

int main(const int argc, const char **argv) {
  if (argc != 2) { exit(1); }
  b64_from_hexstr(argv[1], strlen(argv[1]));

  return 0;
}

>> gcc 1-1.c && ./a.out 49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d

SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接