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

mum43rcc  于 2022-12-11  发布在  Java
关注(0)|答案(2)|浏览(165)

我尝试通过网络从用C编写的服务器向用Java编写的客户端发送RSA公钥。当客户端收到模数时,它应该使用模数和公共指数F4重新创建RSA密钥,服务器也在使用F4。
下面是服务器生成和发送密钥的过程:

//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版本的客户端,它可以正确地重新创建它从服务器接收的信息。这可能是与在Java中签名的BigInteger有关的东西,但我也尝试插入一个额外的前导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);
5f0d552i

5f0d552i1#

Java的输出是SubjectPublicKeyInfo(或SPKI)格式的RSA公钥,而C代码则是PKCS#1格式的RSA公钥。
我已经很多年没有使用openssl了,但是在看过openssl 3.0的文档后,我发现使用EVP_PKEY类型的函数是最好的选择,i2d_PUBKEY函数将返回一个SPKI编码的公钥。

// 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));
sigwle7e

sigwle7e2#

通过网络发送套接字时,Java使用Big-Endian,而C使用Little-Endian。Java客户端收到数据后,尝试将其从小端转换为大端。您可以在客户端使用ByteBuffer
所以试试这个:

ByteBuffer bbf = ByteBuffer.allocate(Income_Data.length);
bbf.put(Income_Data);
bbf.order(ByteOrder.BIG_ENDIAN);
byte[] goodData = bbf.array();

您还可以尝试:

ByteBuffer bbf = ByteBuffer.warp(Income_Data);

请参阅:C++ Client to a Java Server(TCP/IP)

相关问题