【发布时间】:2014-08-03 03:45:39
【问题描述】:
我正在尝试通过网络发送一个 RSA 公钥,从用 c 编写的服务器到用 Java 编写的客户端。当客户端收到模数时,它应该使用模数和服务器也使用的公共指数 F4 重新创建 RSA 密钥。
这是服务器生成和发送密钥的方式:
//Generate RSA key in C
RSA *rsa;
int bits = 1024;
unsigned long exp = RSA_F4;
BIGNUM *bn;
BIGNUM *MyModPubKey;
bn = BN_new();
BN_set_word(bn, exp);
rsa = RSA_new();
RSA_generate_key_ex(rsa, bits, bn, NULL);
MyModPubKey = rsa->n;
//Send RSA modulus in C
unsigned char public_key_Mod[128];
unsigned char *PubMod;
int PubModLen;
PubMod = (unsigned char *)&public_key_Mod;
PubModLen = BN_bn2bin(MyModPubKey, PubMod);
assert(PubModLen == 128);
send(sd, public_key_Mod, 128, 0);
这是在 Java 中重新创建密钥的方式。省略异常处理。
//Read the modulus
byte[] public_key_mod = new byte[128];
is.read(public_key_mod, 0, 128);
//Create BigInteger modulus and exponent
BigInteger RxPubKeyMod = new BigInteger(1, public_key_mod);
BigInteger RxPubKeyExp = RSAKeyGenParameterSpec.F4;
PublicKey RxRsaPubKey = KeyFactory.getInstance("RSA").generatePublic(
new RSAPublicKeySpec(RxPubKeyMod, RxPubKeyExp));
但是,Java 中生成的公共 RSA 密钥与 C 中的不同。通过打印密钥的 base64 编码版本进行验证。 我还在两端打印模数和指数,并验证它们是否相同。这是如何完成的:
//Java
System.out.println("RxPubKeyExp: " + RxPubKeyExp.toString(16));
System.out.println("RxPubKeyMod: " + RxPubKeyMod.toString(16));
//C
printf("PubKeyExp %s \n", BN_bn2hex(MyModPubKey->e));
printf("PubKeyMod %s \n", BN_bn2hex(MyModPubKey->n));
它们是一样的。
我不明白为什么这不起作用。我在这里错过了一些重要的事情吗?复杂的因素是我有一个 C 版本的客户端,它可以正确地重新创建它从服务器接收到的信息。这可能是 BigInteger 在 Java 中签名的问题,但我也尝试插入一个额外的前导 0x00 并将字节数组扩展为 129 个字节,结果相同(生成的密钥相同)。
拜托,我完全被卡住了。 谢谢
更新:
这是 C 服务器的示例输出:
PubKeyExp 010001
PubKeyMod A8CB09C2B84762A8C822F18C9CA48036E0D9988C9D8625BF5F2DF16FDEEC92D018863E129C0AE89EB0C344FD32DFF419548C39BB41A867293BC4BD84A1ECAEB0D723EEA97BD2651AF8BD56B2C97D38A39111A0C48894BC034371C2EDB96F1E2E9CDDA9B16DFBE80580ADFDA3853445120F7429AD60300E31254864041210B0BB
PublicKey -----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAKjLCcK4R2KoyCLxjJykgDbg2ZiMnYYlv18t8W/e7JLQGIY+EpwK6J6w
w0T9Mt/0GVSMObtBqGcpO8S9hKHsrrDXI+6pe9JlGvi9VrLJfTijkRGgxIiUvAND
ccLtuW8eLpzdqbFt++gFgK39o4U0RRIPdCmtYDAOMSVIZAQSELC7AgMBAAE=
-----END RSA PUBLIC KEY-----
是这样打印的:
printf("PubKeyExp %s \n", BN_bn2hex(MyModPubKey->e));
printf("PubKeyMod %s \n", BN_bn2hex(MyModPubKey->n));
FILE *fp;
char *pubKeyString;
pubKeyString = calloc(1, 512);
if(!(fp = fmemopen(pubKeyString, 512, "w"))) {
exit(1);
}
PEM_write_RSAPublicKey(fp, pubKey);
fflush(fp);
fclose(fp);
printf("PublicKey %s \n",pubKeyString);
上面创建的 pubKey=rsa
以下是 Java 服务器的示例输出:
RxPubKeyExp: 10001
RxPubKeyMod: a8cb09c2b84762a8c822f18c9ca48036e0d9988c9d8625bf5f2df16fdeec92d018863e129c0ae89eb0c344fd32dff419548c39bb41a867293bc4bd84a1ecaeb0d723eea97bd2651af8bd56b2c97d38a39111a0c48894bc034371c2edb96f1e2e9cdda9b16dfbe80580adfda3853445120f7429ad60300e31254864041210b0bb
RxRsaPubKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoywnCuEdiqMgi8YycpIA24NmYjJ2GJb9fLfFv3uyS0BiGPhKcCuiesMNE/TLf9BlUjDm7QahnKTvEvYSh7K6w1yPuqXvSZRr4vVayyX04o5ERoMSIlLwDQ3HC7blvHi6c3amxbfvoBYCt/aOFNEUSD3QprWAwDjElSGQEEhCwuwIDAQAB
是这样打印的:
System.out.println("RxPubKeyExp: " + RxPubKeyExp.toString(16));
System.out.println("RxPubKeyMod: " + RxPubKeyMod.toString(16));
String RxRsaPubKeyChar = Base64.encodeToString(RxRsaPubKey.getEncoded(), Base64.DEFAULT);
System.out.println("RxRsaPubKey: " + RxRsaPubKeyChar);
【问题讨论】:
-
当前是同一台机器?
-
不,他们正在通过套接字进行通信。
-
您忽略了
recv()返回的计数,并假设读取已填满缓冲区。它没有义务这样做。你必须循环直到你有你需要的一切。 -
你说得对,我省略了那部分以便专注于真正的问题。它循环直到它接收到足够的字节来填充 public_key_mod 的整个字节数组。
-
可能只是两个库为键打印了不同的结构。 Java 使用 PKCS#1 公钥格式作为
getEncoded()的结果。我不知道您如何检索二进制数据以在 C 中打印,因此我无法对此发表评论。能否将二进制数据打印为十六进制字符串并放入问题中?
标签: java c sockets networking rsa