您的位置:首页 > 其它

数字签名详解与例子

2017-02-05 15:17 302 查看


签名认证是对非对称加密技术与数字摘要技术的综合运用,指的是将通信内容的摘要信息使用发送者的私钥进行加密,然后将密文与原文一起传输给信息的接收者,接收者通过发送者的公钥信息来解密被加密的摘要作息,然后使用与发送者相同的摘要算法,对接收到的内容采用相同的方式方式产生摘要串,与解密的摘要串进行对比,如果相同,则说明接收到的内容是完整的,在传输过程中没有受到第三方的篡改,否则说明通信内容已被第三方修改。

我们知道,每个人都有其特有的私钥,且都是对外界保密的,而通过私钥加密的信息,只能通过其对应的公钥来进行解密。因此,私钥可以代表私钥持有者的身份,可以通过私钥对应的公钥来对私钥拥有者的身份进行校验。通过数字签名,能够确认消息是消息发送方签名并发送过来的,因为其他人根本假冒不了消息发送方的签名,他们没有消息发送者的私钥。而不同的内容,摘要信息千差万别,通过数字摘要算法,可以确保传输内容的完整性,如果传输内容在中途被篡改了,对应的数字签名的值也将发生改变。

数字签名的产生过程如图如示:



数字签名的校验过程:



区别于不同的摘要算法,不同的非对称加密方式,数字签名的算法也不尽相同。以下为MD5withRSA和SHA1withRSA的例子。

packagecom.eudi.encode;

importjava.security.MessageDigest;
importjava.security.PrivateKey;
importjava.security.PublicKey;

importjavax.crypto.Cipher;

importsun.misc.BASE64Encoder;

/**
* 用数字签名来保证传输安全的例子
*
*/
publicclass MD5withRSA
{
publicstatic void main( String[] args ) throwsException
{
//要传送的明文
String content = "我是中国人,我爱自己的祖国";

//生成公钥私钥,用于生成数字签名
RSA rsa = newRSA();
PublicKey publicKey = rsa.getPublicKey();
PrivateKey privateKey = rsa.getPrivateKey();
//生成数字签名
byte[] mysign = sign(content.getBytes(), privateKey);

//验证数字签名
booleanresult = verify(content.getBytes(), mysign, publicKey);
if(result) {
System.out.println("验证成功,数据没有被修改!");
}else{
System.out.println("验证失败,数据被修改过!");
}
}

/**
* @param conentBytes
* @param mysign
* @param publicKey
* @return
* @throws Exception
*/
privatestatic boolean verify(byte[] contentBytes, byte[] mysign,
PublicKey publicKey) throwsException {
//首先对签名进行解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decrytBytes = cipher.doFinal(mysign);    //这是解密开的数字摘要

//接下来对明文进行摘要
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] srcBytes = md.digest(contentBytes);  //这是原数字摘要

//比较两个摘要是否相同
//将字节数据编码成Base64再进行字符串比较
if(encryptBASE64(decrytBytes).equals(encryptBASE64(srcBytes))) {
System.out.println("验证成功,传送的内容为:"+ newString(contentBytes));
returntrue;
}else{
returnfalse;
}
}

/**
* 采用MD5withRSA算法对bytes进行签名
* @param bytes
* @param privateKey
* @return
* @throws Exception
*/
privatestatic byte[] sign(byte[] conentBytes, PrivateKey privateKey) throwsException {
//生成数字摘要
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5Bytes = md.digest(conentBytes);

//对摘要进行私钥加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encrytBytes = cipher.doFinal(md5Bytes);
//这就是最后生成的签名
returnencrytBytes;
}

/**
* 用base64编码byte数组
* @param bytes
* @return
*/
privatestatic String encryptBASE64(byte[] bytes) {
BASE64Encoder encoder = newBASE64Encoder();
returnencoder.encodeBuffer(bytes);
}
}


packagecom.eudi.encode;

importjava.security.KeyPair;
importjava.security.KeyPairGenerator;
importjava.security.PrivateKey;
importjava.security.PublicKey;

publicclass RSA {

privateKeyPair keyPair;

/**
* 构造RSA对象,初始化keyPair
* @throws Exception
*/
publicRSA() throwsException{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
keyPair = keyPairGenerator.generateKeyPair();
}

/**
* @return
*/
publicPublicKey getPublicKey() {
returnkeyPair.getPublic();
}

/**
* @return
*/
publicPrivateKey getPrivateKey() {
returnkeyPair.getPrivate();
}
}


运行结果

以下是SHAwithRSA签名例子

packagecom.eudi.encode;

importjava.security.MessageDigest;
importjava.security.PrivateKey;
importjava.security.PublicKey;

importjavax.crypto.Cipher;

importsun.misc.BASE64Encoder;

/**
* 用数字签名来保证传输安全的例子
*
*/
publicclass SHA1withRSA
{
publicstatic void main( String[] args ) throwsException
{
//要传送的明文
String content = "我是中国人,我爱自己的祖国";

//生成公钥私钥,用于生成数字签名
RSA rsa = newRSA();
PublicKey publicKey = rsa.getPublicKey();
PrivateKey privateKey = rsa.getPrivateKey();
//生成数字签名
byte[] mysign = sign(content.getBytes(), privateKey);

//验证数字签名
booleanresult = verify(content.getBytes(), mysign, publicKey);
if(result) {
System.out.println("验证成功,数据没有被修改!");
}else{
System.out.println("验证失败,数据被修改过!");
}
}

/**
* @param conentBytes
* @param mysign
* @param publicKey
* @return
* @throws Exception
*/
privatestatic boolean verify(byte[] contentBytes, byte[] mysign,
PublicKey publicKey) throwsException {
//首先对签名进行解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decrytBytes = cipher.doFinal(mysign);    //这是解密开的数字摘要

//接下来对明文进行摘要
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] srcBytes = md.digest(contentBytes);  //这是原数字摘要

//比较两个摘要是否相同
//将字节数据编码成Base64再进行字符串比较
if(encryptBASE64(decrytBytes).equals(encryptBASE64(srcBytes))) {
System.out.println("验证成功,传送的内容为:"+ newString(contentBytes));
returntrue;
}else{
returnfalse;
}
}

/**
* 采用SHA1withRSA算法对bytes进行签名
* @param bytes
* @param privateKey
* @return
* @throws Exception
*/
privatestatic byte[] sign(byte[] conentBytes, PrivateKey privateKey) throwsException {
//生成数字摘要
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] rha1Bytes = md.digest(conentBytes);

//对摘要进行私钥加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encrytBytes = cipher.doFinal(rha1Bytes);
//这就是最后生成的签名
returnencrytBytes;
}

/**
* 用base64编码byte数组
* @param bytes
* @return
*/
privatestatic String encryptBASE64(byte[] bytes) {
BASE64Encoder encoder = newBASE64Encoder();
returnencoder.encodeBuffer(bytes);
}
}


Java提供了比较友好的API来使用数字签名,在sign方法中,先获取MD5withRSA的一个实例,然后使用私钥对signature进行初始化,调用update传入签名内容,最后生成签名。在verify方法中,使用公钥来对signature进行初始化,调用update传入需要校验的内容,调用signature的verify方法校验签名,并返回结果。

以下是笔者的代码实现

 
packagecom.eudi.encode;

importjava.security.PrivateKey;
importjava.security.PublicKey;
importjava.security.Signature;

/**
* 用数字签名来保证传输安全的例子
*
*/
publicclass SignatureAPI
{
publicstatic void main( String[] args ) throwsException
{
//要传送的明文
String content = "我是中国人,我爱自己的祖国";

//生成公钥私钥,用于生成数字签名
RSA rsa = newRSA();
PublicKey publicKey = rsa.getPublicKey();
PrivateKey privateKey = rsa.getPrivateKey();
//生成数字签名
byte[] mysign = sign(content.getBytes(), privateKey);

//验证数字签名
booleanresult = verify(content.getBytes(), mysign, publicKey);
if(result) {
System.out.println("验证成功,数据没有被修改!");
}else{
System.out.println("验证失败,数据被修改过!");
}
}

/**
* 基本Java的signature API对数字签名进行校验
* @param conentBytes
* @param mysign
* @param publicKey
* @return
* @throws Exception
*/
privatestatic boolean verify(byte[] contentBytes, byte[] mysign,
PublicKey publicKey) throwsException {
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
signature.update(contentBytes);
returnsignature.verify(mysign);
}

/**
* 采用Java的Signature API进行签名
* @param bytes
* @param privateKey
* @return
* @throws Exception
*/
privatestatic byte[] sign(byte[] conentBytes, PrivateKey privateKey) throwsException {
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
signature.update(conentBytes);
returnsignature.sign();
}
}


还有另一种利用其他加密类进行的数字签名 我以为一并展示出来

public class DigitalSignatureTest2 {

public static void main(String args[]) throws Exception {

String content = "我是中国人,我爱自己的祖国";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();

byte[] mysign=sign(content.getBytes(),rsaPrivateKey);

boolean result = verify(content.getBytes(), mysign, rsaPublicKey);
if(result) {
System.out.println("验证成功,数据没有被修改!");
}else {
System.out.println("验证失败,数据被修改过!");
}

}

/**
* 执行签名(私钥签名)
* 采用MD5withRSA算法对bytes进行签名
* @param conentBytes
* @param rsaPrivateKey
* @return
* @throws Exception
*/
private static byte[] sign(byte[] conentBytes, RSAPrivateKey rsaPrivateKey) throws Exception {
//生成数字摘要
PKCS8EncodedKeySpec pkcs8EncodedKeySpec=new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privateKey=keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature=Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
signature.update(conentBytes);
byte[] result=signature.sign();
System.out.println("私钥加密的数字签名"+ Hex.encodeHexString(result));
return result;
}

/**
* 验证签名(公钥验证)
* @param contentBytes
* @param mysign
* @param rsaPublicKey
* @return
* @throws Exception
*/
private static boolean verify(byte[] contentBytes, byte[] mysign,
RSAPublicKey rsaPublicKey) throws Exception {
//首先对签名进行解密
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
signature.update(contentBytes);
boolean bool = signature.verify(mysign);
return bool;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: