尽管这是2014年(五年多以前)以来的一个问题,但我想解决您的问题/弄清情况,这可能会对其他人有所帮助。
我想要以下Java代码的C ++版本。
BigInteger x = new BigInteger("00afd72b5835ad22ea5d68279ffac0b6527c1ab0fb31f1e646f728d75cbd3ae65d", 16);
BigInteger y = x.multiply(BigInteger.valueOf(-1));
//prints y = ff5028d4a7ca52dd15a297d860053f49ad83e54f04ce0e19b908d728a342c519a3
System.out.println("y = " + new String(Hex.encode(y.toByteArray())));
这是我的解决方案。
BIGNUM* x = BN_new();
BN_CTX* ctx = BN_CTX_new();
std::vector<unsigned char> xBytes = hexStringToBytes(“00afd72b5835ad22ea5d68279ffac0b6527c1ab0fb31f1e646f728d75cbd3ae65d");
BN_bin2bn(&xBytes[0], xBytes.size(), x);
BIGNUM* negative1 = BN_new();
std::vector<unsigned char> negative1Bytes = hexStringToBytes("ff");
BN_bin2bn(&negative1Bytes[0], negative1Bytes.size(), negative1);
BIGNUM* y = BN_new();
BN_mul(y, x, negative1, ctx);
char* yHex = BN_bn2hex(y);
std::string yStr(yHex);
//prints y = AF27542CDD7775C7730ABF785AC5F59C299E964A36BFF460B031AE85607DAB76A3
std::cout <<"y = " << yStr << std::endl;
(忽略此案。)我做错了什么?如何获取我的C ++代码以输出正确的值“ ff5028d4a7ca52dd15a297d860053f49ad83e54f04ce0e19b908d728a342c519a3”。我还尝试通过执行BN_set_word(negative1,-1)来设置negative1,但这也给了我错误的答案。
BN_set_negative
功能设置一个负数。
afd72b5835ad22ea5d68279ffac0b6527c1ab0fb31f1e646f728d75cbd3ae65d
的负数实际上是-afd72b5835ad22ea5d68279ffac0b6527c1ab0fb31f1e646f728d75cbd3ae65d
,与-2
是2
的负数相同。
ff5028d4a7ca52dd15a297d860053f49ad83e54f04ce0e19b908d728a342c519a3
是一个大正数。
您在Java中看到此数字的原因是由于toByteArray
调用。 According to its documentation,它选择最小字段宽度,该宽度是字节的整数,并且还可以保留负数的二进制补码表示形式。
换句话说,通过对当前具有1个符号位和256个值位的数字使用toByteArray
函数,最终得到264位的字段宽度。但是,如果负数的第一个半字节例如是7
,而不是a
,那么(根据本文档-我还没有实际尝试过),您将得到256位字段宽度(即8028d4...
,而不是ff8028d4
。
您在代码中使用的前导00
在OpenSSL BN中不重要。我不确定在BigInteger中它是否有意义,尽管该构造函数的文档说:“字符串表示形式包含一个可选的减号或加号,后跟指定基数中的一个或多个数字的序列。”因此,它接受负号的事实表明,如果不存在负号,则即使已设置其MSB,输入也将被视为较大的正数。 (希望Java程序员可以帮我清除这段内容。)>
请确保牢记大负值
与通过模运算对该负值进行模运算而获得的大正数之间的区别,例如toByteArray
的输出。所以您的问题确实是:Openssl BN是否具有模仿BigInteger.toByteArray()行为的函数?
我不知道是否存在这样的功能(BN库的IMHO文档相当差,而且我从未听说过它在OpenSSL之外使用,尤其是在C ++程序中没有使用)。我不希望这样,因为toByteArray
的行为有点怪异。在任何情况下,所有BN输出函数似乎都是使用符号幅度格式而不是二进制补码格式输出的。
但是要复制该输出,可以将2^256
或2^264
添加到较大的负数,然后执行BN_bn2hex
。在这种情况下,请添加2^264
。通常,您必须测量要存储的数字的当前位长,并将指数四舍五入到最接近的8的倍数。
或者您甚至可以以符号幅度格式(使用BN_bn2hex
或BN_bn2mpi
输出),然后通过反转每个半字节并固定起点来进行迭代!
NB。您要使用OpenSSL BN是否有任何特定原因? There are many alternatives。
尽管这是2014年(五年多以前)以来的一个问题,但我想解决您的问题/弄清情况,这可能会对其他人有所帮助。
在有限数论中,存在数字的“一个补码”和“两个补码”表示。一个补码仅存储绝对(正)值,不<符号。如果您想将数字的符号存储为补码,则必须将其分开存储,例如一位(0 =正,1 =负)。这正是浮点数(IEEE 754)的情况。尾数与指数和一个附加符号位一起作为补码存储。一个补码中的数字有两个零:-0和+0,因为您将符号与绝对值本身无关地对待。
以二进制补码形式,最高有效位用作符号位。之所以没有'-0',是因为在二进制补码中取一个否定值意味着先执行逻辑NOT(在C:波浪号中),然后再加一个。例如,一个字节(以2的补码形式)可以是三个值0xFF,0x00、0x01之一,表示-1、0和1。-0为no room。如果有,例如0xFF(-1)并想要取反,则逻辑NOT操作将计算0xFF => 0x00。加一产生0x01,即1。
b)OpenSSL BIGNUM和Java BigIntegerc)您正在寻找的解决方案
与-1的乘法如下(代码段,无错误检查):
BIGNUM* x = BN_bin2bn(&xBytes[0], (int)xBytes.size(), NULL);
BIGNUM* negative1 = BN_new();
BN_one(negative1); /* negative1 is +1 */
BN_set_negative(negative1, 1); /* negative1 is now -1 */
BN_CTX* ctx = BN_CTX_new();
BIGNUM* y = BN_new();
BN_mul(y, x, negative1, ctx);
更容易是:
BIGNUM* x = BN_bin2bn(&xBytes[0], (int)xBytes.size(), NULL); BN_set_negative(x,1);
[这不能解决您的问题,因为正如M.M所言,这只是使afd72b5835ad22ea5d68279ffac0b6527c1ab0fb31f1e646f728d75。您正在寻找您的大整数的二元补数,这是>
int i; for (i = 0; i < (int)sizeof(value); i++) value[i] = ~value[i]; for (i = ((int)sizeof(posvalue)) - 1; i >= 0; i--) { value[i]++; if (0x00 != value[i]) break; }
如果'value'是您的33字节输入数组,其中包含以0x00字节为前缀的大整数,则这是二进制补数的未优化版本。该操作的结果是33个字节ff5028d4a7ca52dd15a297d860053f49ad83e54f04ce0e19b908d728a342c519a3。d)使用二进制补码和OpenSSL BIGNUM
整个序列是这样的:
序言:如果输入为负(检查最高有效位),则计算输入的二进制补数。
尽管这是2014年(五年多以前)以来的一个问题,但我想解决您的问题/弄清情况,这可能会对其他人有所帮助。