实用数字签名的步骤
(1)发送方使用MD5算法对原始信息进行计算,获得一个固定长度的信息摘要
(2)发送方用自己的私钥加密生成的信息生成发送方的数字签名,发送方把这个数字签名作为发送信息的附件和明文信息,一同用接收方的公钥进行加密,将加密后的密文一同发送给接收方;
(3)接收方首先把接收到的密文用自己的私钥解密,得到明文信息和数字签名,再用发送方的公钥对数字签名进行解密,然后使用相同的单向散列算法来计算解密得到的明文信息,得到信息摘要;对比计算出来的信息摘要和发送方发送过来的信息摘要是否一致
以下是我写的主要代码如下:
/*
* md5对原始信息计算得到一个固定长度的信息摘要
*/
public class MD5Algorithmic {
private String MD5;
public MD5Algorithmic(String msg) {
MD5 = msg;
}
/*
* @return 一个固定长度的信息摘要
*/
public String getDigest() {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5 = md.digest(MD5.getBytes());
StringBuffer sb = new StringBuffer();
String part = null;
for (int i = 0; i < md5.length; i++) {
part = Integer.toHexString(md5[i] & 0xFF);
if (part.length() == 1) {
part = "0" + part;
}
sb.append(part);
}
//System.out.println(sb.append(part));
return sb.toString();
} catch (NoSuchAlgorithmException ex) {
}
return null;
}
}
/**
* RSA 工具类。提供加密,解密,生成密钥对等方法。
* 需要到http://www.bouncycastle.org/下载bcprov-jdk14-123.jar。
*/
public class RSAUtil {
/**
* 生成密钥对
*
* @return KeyPair
* @throws EncryptException
*/
public static KeyPair generateKeyPair() {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
final int KEY_SIZE = 1024;// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGen.genKeyPair();
return keyPair;
} catch (Exception e) {
}
return null;
}
/**
* 生成公钥
*
* @param modulus
* @param publicExponent
* @return RSAPublicKey
* @throws EncryptException
*/
public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
byte[] publicExponent) {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
}
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
modulus), new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
} catch (InvalidKeySpecException ex) {
}
return null;
}
/**
* 生成私钥
*
* @param modulus
* @param privateExponent
* @return RSAPrivateKey
* @throws EncryptException
*/
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
byte[] privateExponent) {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
}
RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(
modulus), new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
} catch (InvalidKeySpecException ex) {
}
return null;
}
/**
* 加密
*
* @param key
* 加密的密钥
* @param data
* 待加密的明文数据
* @return 加密后的数据
* @throws EncryptException
*/
public static byte[] encrypt(Key key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, key);
int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
// 加密块大小为127
// byte,加密后为128个byte;因此共有2个加密块,第一个127
// byte第二个为1个byte
int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
: data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize)
cipher.doFinal(data, i * blockSize, blockSize, raw, i
* outputSize);
else
cipher.doFinal(data, i * blockSize, data.length - i
* blockSize, raw, i * outputSize);
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
i++;
}
return raw;
} catch (Exception e) {
}
return null;
}
/**
* 解密
*
* @param key
* 解密的密钥
* @param raw
* 已经加密的数据
* @return 解密后的明文
* @throws EncryptException
*/
public static byte[] decrypt(Key key, byte[] raw) {
try {
Cipher cipher = Cipher.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(cipher.DECRYPT_MODE, key);
int blockSize = cipher.getBlockSize();
ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
int j = 0;
while (raw.length - j * blockSize > 0) {
bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
j++;
}
return bout.toByteArray();
} catch (Exception e) {
}
return null;
}
/**
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
KeyPair keyPair = RSAUtil.generateKeyPair();
RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
"src/client/APriKey.txt"));
out.writeObject(priKey);
out = new ObjectOutputStream(new FileOutputStream("src/server/APubKey.txt"));
out.writeObject(pubKey);
KeyPair keyPair2 = RSAUtil.generateKeyPair();
RSAPublicKey pubKey2 = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey priKey2 = (RSAPrivateKey) keyPair.getPrivate();
ObjectOutputStream out2 = new ObjectOutputStream(new FileOutputStream(
"src/server/BPriKey.txt"));
out2.writeObject(priKey2);
out2= new ObjectOutputStream(new FileOutputStream("src/client/BPubKey.txt"));
out2.writeObject(pubKey2);
out2.close();
}
}
public class ClientApp extends JApplet implements Runnable {
private JTextField inputField;
private JTextArea displayArea;
private String message;// 发送的原文
private DataInputStream input;
private DataOutputStream output;
private Socket connection;
private PrivateKey APriKey;
private PublicKey bPubKey;
MD5Algorithmic md5;
String Msg_Digest;
public ClientApp(){
md5=new MD5Algorithmic("");
Msg_Digest=new String();
// 读取私钥
ObjectInputStream inStream;
try {
inStream = new ObjectInputStream(new FileInputStream(
"client/APriKey.txt"));
try {
APriKey = (PrivateKey) inStream.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 读取公钥
inStream = new ObjectInputStream(new FileInputStream(
"client/BPubKey.txt"));
try {
bPubKey = (PublicKey) inStream.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
inStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void displayMessage(final String message) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
displayArea.append(message + "\n");
displayArea.setCaretPosition(displayArea.getText().length());
}
});
}
/*
* run前初始化
*/
public void init() {
Container container = getContentPane();
displayArea = new JTextArea(4, 60);
displayArea.setEditable(false);
container.add(new JScrollPane(displayArea), BorderLayout.CENTER);
inputField = new JTextField();
inputField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
message = inputField.getText().trim();// 发送的原文
inputField.setEditable(true);
md5 = new MD5Algorithmic(message);
Msg_Digest = md5.getDigest();
displayMessage(message);
inputField.setText("");
//发送
// 发送方用自己的私钥加密Msa_Digest,生成数字签名,signatrue
byte[] signatrue = new byte[1024];
signatrue = RSAUtil.encrypt(APriKey, Msg_Digest.getBytes());
// 发送方把原文message和数字签名signatrue用bPubKey加密
byte[] encrypt1 = new byte[1024];
encrypt1 = RSAUtil.encrypt(bPubKey, message.getBytes());
byte[] encrypt2=new byte[1024];
encrypt2 = RSAUtil.encrypt(bPubKey,signatrue);
try {
output.write(encrypt2);
output.write(encrypt1);
output.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
container.add(inputField, BorderLayout.SOUTH);
}
public void start() {
try {
connection = new Socket(getCodeBase().getHost(), 12345);
input = new DataInputStream(connection.getInputStream());
output = new DataOutputStream(connection.getOutputStream());
} catch (IOException ioException) {
ioException.printStackTrace();
}
Thread outputThread = new Thread(this);
outputThread.start();
}
public void run() {
try {
while(true)
{
step1();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String byteToString( byte[] buf ){
String res = "";
for( int i = 0 ; i < buf.length ; i ++ )
res += (char) buf[i];
return res;
}
private byte[] getByte()
{
try {
byte[] buf = new byte[1024];
int cnt = input.read( buf );
byte[] tmp = new byte[1024];
for( int i = 0 ; i < cnt ; i ++ ) tmp[i] = buf[i];
return tmp;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*
* 接收比较判断
*/
private void step1() throws IOException {
String msg1;
String msg2;
byte[] temp=RSAUtil.decrypt(APriKey, getByte());//私钥解密数字签名
msg1 = byteToString(RSAUtil.decrypt(APriKey,getByte()));// 私钥解密明文信息
msg2 = byteToString(RSAUtil.decrypt(bPubKey,temp));// 公钥jie密数字签名
// 由明文信息得到信息摘要
MD5Algorithmic md5 = new MD5Algorithmic(msg1);
displayMessage("接收到的明文信息:"+msg1);
msg1 = md5.getDigest();
System.out.println(msg1);
System.out.println(msg2);
displayMessage("计算接收明文信息得到的信息摘要:"+msg1);
displayMessage("发送方给我的信息摘要:"+msg2);
if (msg1.equals(msg2)) {
displayMessage("数字签名确实是发送方的。");
} else
displayMessage("收到的信息是伪造的或中途被篡改的。");
}
/*
* 发送
*/
private void step2() throws IOException {
inputField.setEditable(true);
}
}
public class Server extends JApplet implements Runnable {
private JTextField inputField;
private JTextArea displayArea;
private String message;// 发送的原文
private DataInputStream input;
private DataOutputStream output;
private ServerSocket server;
private Socket connection;
private PrivateKey bPriKey;
private PublicKey APubKey;
MD5Algorithmic md5;
String Msg_Digest;
public Server() {
try {
server = new ServerSocket(12345, 1);
try {
// 读取私钥
ObjectInputStream inStream;
inStream = new ObjectInputStream(new FileInputStream(
"server/BPriKey.txt"));
try {
bPriKey = (PrivateKey) inStream.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 读取公钥
inStream = new ObjectInputStream(new FileInputStream(
"server/APubKey.txt"));
try {
APubKey = (PublicKey) inStream.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
inStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void displayMessage(final String message) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
displayArea.append(message + "\n");
displayArea.setCaretPosition(displayArea.getText().length());
}
});
}
public void start() {
try {
connection = server.accept();
input = new DataInputStream(connection.getInputStream());
output = new DataOutputStream(connection.getOutputStream());
} catch (IOException ioException) {
ioException.printStackTrace();
}
Thread outputThread = new Thread(this);
outputThread.start();
}
/*
* run前初始化
*/
public void init() {
Container container = getContentPane();
displayArea = new JTextArea(4, 60);
displayArea.setEditable(false);
container.add(new JScrollPane(displayArea), BorderLayout.CENTER);
inputField = new JTextField();
//发送
inputField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
message = inputField.getText().trim();// 发送的原文
md5 = new MD5Algorithmic(message);
Msg_Digest = md5.getDigest();
displayMessage(message);
inputField.setText("");
inputField.setEditable(true);
// 发送方用自己的私钥加密Msa_Digest,生成数字签名,signatrue
byte[] signatrue = new byte[1024];
signatrue = RSAUtil.encrypt(bPriKey, Msg_Digest.getBytes());
// 发送方把原文message和数字签名signatrue用bPubKey加密
byte[] encrypt1 = new byte[1024];
encrypt1 = RSAUtil.encrypt(APubKey, message.getBytes());
byte[] encrypt2=new byte[1024];
encrypt2 = RSAUtil.encrypt(APubKey,signatrue);
try {
output.write(encrypt2);
output.write(encrypt1);
output.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
container.add(inputField, BorderLayout.SOUTH);
}
public void run() {
try {
while(true){
step1();
//step2();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String byteToString( byte[] buf ){
String res = "";
for( int i = 0 ; i < buf.length ; i ++ )
res += (char) buf[i];
return res;
}
private byte[] getByte()
{
try {
byte[] buf = new byte[1024];
int cnt = input.read( buf );
byte[] tmp = new byte[1024];
for( int i = 0 ; i < cnt ; i ++ ) tmp[i] = buf[i];
return tmp;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*
* 接收,比较判断
*/
private void step1() throws IOException {
String msg1;
String msg2;
byte[] temp=RSAUtil.decrypt(bPriKey, getByte());//私钥解密数字签名
msg1 = byteToString(RSAUtil.decrypt(bPriKey,getByte()));// 私钥解密明文信息
msg2 = byteToString(RSAUtil.decrypt(APubKey,temp));// 公钥jie密数字签名
// 由明文信息得到信息摘要
MD5Algorithmic md5 = new MD5Algorithmic(msg1);
displayMessage("接收到的明文信息:"+msg1);
msg1 = md5.getDigest();
System.out.println(msg1);
System.out.println(msg2);
displayMessage("计算接收明文信息得到的信息摘要:"+msg1);
displayMessage("发送方给我的信息摘要:"+msg2);
if (msg1.equals(msg2)) {
displayMessage("数字签名确实是发送方的。");
} else
displayMessage("收到的信息是伪造的或中途被篡改的。");
}
}