java - 通过网络将 RSA 公钥从 C 发送到 Java

标签 java c sockets networking rsa

我正在尝试通过网络发送 RSA 公钥,从用 c 编写的服务器发送到用 Java 编写的客户端。当客户端收到模数时,它应该使用模数和服务器也在使用的公共(public)指数 F4 重新创建 RSA key 。

服务器生成和发送 key 的方式如下:

//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 中重新创建 key 的方式。省略了异常处理。

//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 中生成的公共(public) RSA key 与在 C 中生成的公共(public) RSA key 不同。通过打印 key 的 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 并将 bytearray 扩展到 129 个字节,结果相同(生成的 key 相同)。

拜托,我完全卡住了。 谢谢

更新:

这是 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);

最佳答案

输出不一样,因为他们使用了不同的编码。 Java 以 SubjectPublicKeyInfo(或 SPKI)格式输出 RSA 公钥,而您的 C 代码以 PKCS#1 格式获取 RSA 公钥。

我已经很多年没有使用过 openssl,但是通过查看 openssl 3.0 的文档,似乎使用 EVP_PKEY 类型的函数是可行的方法,并且 i2d_PUBKEY函数将返回一个 SPKI 编码的公钥。然后可以通过 Java 读取它并通过类似的方式将其转换为 RSA 公钥

// The C server writes the length L of the encoded public key data as a
// big-endian 4 byte int followed by the L bytes of spki

DataInputStream dis = new DataInputStream(sock.getInputStream());
int spkiLength = dis.readInt();
byte [] spkiBytes = new byte[spkiLength];
dis.readFully(spkiBytes);
KeyFactory rsaKf = KeyFactory.getInstance("RSA");
PublicKey pubKey = rsaKf.generatePublic(new X509EncodedKeySpec(spkiBytes));

关于java - 通过网络将 RSA 公钥从 C 发送到 Java,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24190810/

相关文章:

java - DatagramPacket 和 DatagramSocket 发送的字符串不等于其本身的文字表示

java - Applet:如何将参数从Applet传递到html

java - 如何在 Java 中从 Jenkins 获取作业的最后构建日期

java - 使用对象数组/对象数组求平均值

c# - dllexport 结构指针并返回 uint8_t

java - 从套接字读取字节

c - 调用 sendto() 时长度错误

c - 使用openmp没有结果

java - Java的DatagramChannel.write()的含义

java - 在java中获取用于tcp连接的开放套接字