我一直在尝试在一个非常简单的客户端 - 服务器Java应用程序中实现RSA加密。
为了尝试了解加密的工作原理,我制作了自己的方案:
这对我来说都很有意义,但是当我尝试用Java实现它时,我得到了不一致的结果。有时我会得到NegativeArrayException
或ArithmeticException: BigInteger would overflow supported range
,而我从未成功解密过字符串。
这是我的实施:
RSA.java
import java.math.BigInteger;
import java.security.SecureRandom;
public class RSA {
private BigInteger n;
private BigInteger d;
private BigInteger e;
private PrivateKey privateKey;
private PublicKey publicKey;
public static class PublicKey {
public byte[] getE() {
return e;
}
public byte[] getN() {
return n;
}
private final byte[] e;
private final byte[] n;
public PublicKey(byte[] e, byte[] n) {
this.e = e;
this.n = n;
}
}
public static class PrivateKey {
public byte[] getD() {
return d;
}
public byte[] getN() {
return n;
}
private final byte[] d;
private final byte[] n;
public PrivateKey(byte[] n, byte[] d) {
this.n = n;
this.d = d;
}
}
public RSA() {
generateKeyPair(1024);
}
public RSA(int bits) {
generateKeyPair(bits);
}
public final void generateKeyPair(int bits) {
SecureRandom random = new SecureRandom();
BigInteger p = new BigInteger(bits/2, 100, random);
BigInteger q = new BigInteger(bits/2, 100, random);
BigInteger phi = (p.subtract(new BigInteger("1"))).multiply(q.subtract(new BigInteger("1")));
n = p.multiply(q);
e = new BigInteger("3");
while (phi.gcd(e).intValue() > 1) {
e = e.add(new BigInteger("2"));
}
System.out.println(e.toString());
System.out.println(n.toString());
d = e.modInverse(phi);
publicKey = new PublicKey(e.toByteArray(), n.toByteArray());
privateKey = new PrivateKey(d.toByteArray(), n.toByteArray());
}
public byte[] encrypt(byte[] plainData, PublicKey publicKey) {
return ((new BigInteger(plainData)).modPow(new BigInteger(publicKey.getE()), new BigInteger(publicKey.getN()))).toByteArray();
}
public byte[] decrypt(byte[] encryptedData) {
return ((new BigInteger(encryptedData)).modPow(d, n)).toByteArray();
}
// (d, n)
public PrivateKey getPrivateKey() {
return privateKey;
}
// (e, n)
public PublicKey getPublicKey() {
return publicKey;
}
}
Alice.java
import crypto.RSA.PublicKey;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Alice {
private final RSA encryption;
public Alice() {
encryption = new RSA();
try {
ServerSocket alice = new ServerSocket(2424);
while(true) {
Socket bob = alice.accept();
DataInputStream inputStream = new DataInputStream(bob.getInputStream());
DataOutputStream outputStream = new DataOutputStream(bob.getOutputStream());
byte[] bobE = new byte[1];
byte[] bobN = new byte[inputStream.readInt()];
// Read Bob's Public Key to encrypt Alice's Message.
inputStream.read(bobE);
inputStream.read(bobN);
PublicKey bobPublicKey = new PublicKey(bobE, bobN);
byte[] aliceE = encryption.getPublicKey().getE();
byte[] aliceN = encryption.getPublicKey().getN();
// Send Alice's Public Key to Bob just in case he wants to send Alice a message.
outputStream.writeInt(aliceN.length); // N length
outputStream.write(aliceE, 0, aliceE.length);
outputStream.write(aliceN, 0, aliceN.length);
outputStream.flush();
String messageToBob = "Hello!";
byte[] encryptedMessage = encryption.encrypt(messageToBob.getBytes(), bobPublicKey);
outputStream.writeInt(encryptedMessage.length);
outputStream.write(encryptedMessage, 1, encryptedMessage.length-1);
outputStream.flush();
}
} catch (IOException ex) {}
}
}
Bob.java
import crypto.RSA.PublicKey;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class Bob {
private final RSA encryption;
public Bob() {
encryption = new RSA();
try {
Socket alice = new Socket("127.0.0.1", 2424);
DataInputStream inputStream = new DataInputStream(alice.getInputStream());
DataOutputStream outputStream = new DataOutputStream(alice.getOutputStream());
// Send Bob's public E and N to Alice.
byte[] bobE = encryption.getPublicKey().getE();
byte[] bobN = encryption.getPublicKey().getN();
outputStream.writeInt(bobN.length); // N length
outputStream.write(bobE, 0, bobE.length);
outputStream.write(bobN, 0, bobN.length);
outputStream.flush();
byte[] aliceE = new byte[1];
byte[] aliceN = new byte[inputStream.readInt()];
// Read Alice's public key. (unused)
inputStream.read(aliceE);
inputStream.read(aliceN);
PublicKey alicePublicKey = new PublicKey(aliceE, aliceN);
// Read Alice's Message Size.
int encryptedMessageSize = inputStream.readInt();
byte[] encryptedMessageFromAlice = new byte[encryptedMessageSize];
// Read Alice's Message.
// Here is where everything goes wrong.
inputStream.read(encryptedMessageFromAlice, 1, encryptedMessageSize-1);
System.out.println("Got a message from alice (" + encryptedMessageFromAlice.length + "): ");
System.out.println(new String(encryption.decrypt(encryptedMessageFromAlice)));
} catch (IOException ex) {}
}
}
主要方法
(new Thread() {
@Override
public void run() {
Alice alice = new Alice();
}
}).start();
Bob bob = new Bob();
我觉得我可能会遗漏一些基础知识,但我已经阅读了它是如何工作的,检查了一些例子(尽管这些例子都不完整),但我看不出错误在哪里。
我知道有些库可以让这更容易,但是我需要使用BigInteger来学习它的基本实现。
任何帮助和指导将不胜感激。谢谢!
更新1: 詹姆斯说,我已经将模数的长度设为非硬编码。
运行程序后,这里打印出来的内容(消息总是相同的“Hello!”):
尝试1
Got a message from alice (41):
N�qpfcc�d�����J��
�$P�m�1�qj?��K�����s�����IQ@��g�u�)^���\m"ݒ�M��7p�}�!����
尝试2
Got a message from alice (41):
W��LW\t� b�w��P�3Ꟊ��?�g��ϔSx�N���<Vx� �ĝJ:h1��ޟ�KY�:vtad��0\R-�ǰ���3D4~_�
~���Cy�$$y��#{�^U$;՞�RU��ݘN�C'0��S
尝试3
Got a message from alice (18):
Rq���ҞlapZ�L����t�,��@k��&��=�.o�� �� ��������؉I�g>+h��ʸz��=��l���a��+�\Gഢ��ccn�n�1[1���Bu�A�] �n�b�Tu�o!