JAVA签名过程

本文通过实例演示了如何使用RSA算法进行数据签名和验证的过程,包括生成密钥对、使用私钥加密散列值以及利用公钥解密验证等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面代码演示了对内容进行签名的过程,包括散列,加密,解密。

其中的两个类引用了如下两个博客,我这里只是做个整合工作。谢谢两位博主~

http://blog.youkuaiyun.com/zidan_2011/article/details/7708557

http://blog.youkuaiyun.com/liuhuabai100/article/details/7585879


/**
 * RSA加密算法的演示验证
 * RSA是一种分组加密算法
 * 注意:密钥对采用的长度决定了加密块的长度,我这里取的是2048,即256byte
 * 由于加密块的长度固定为256,因此明文的长度至多为256 - 11 = 245byte
 * 我这里明文长度取的是128byte,密文长度为256byte,它适合于小文件加密
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;


/**
 * 签名过程
 */
public class CipherDemo {
    public static void main(String[] args) throws Exception {
        
        // 1. 第一步: 对原始内容进行散列(MD5算法)
        MD5Demo md5 = new MD5Demo();
        byte[] srcContentBytes = md5.getFileBytes("d:\\test.txt");  // 获取原始内容字节流。test.txt是要签名的内容文件
        String md5Str = md5.getMD5(srcContentBytes);                // MD5散列
        md5.saveString2File(md5Str, "d:\\test.md5.txt");            // 保存散列内容到文件
        
        // 2. 第二步:得到私匙和公匙对
        RSADemo rsa = new RSADemo();
        rsa.initKey();
        rsa.savePublicKey(new File("D:\\public.key"));
        rsa.savePrivateKey(new File("D:\\private.key"));
        
        // 3. 第三步:使用私匙对散列内容加密。并把加密后的内容保存到文件 “散列文件.encrypt”
        // 3.1 然后,加密者把 原始内容文件+加密后的散列内容文件+公匙 发给解密者
        rsa.encryptFile(new File("D:\\test.md5.txt"));
        
        // 4. 解密者用公匙对“散列文件.encrypt”进行解密,得到解密后的散列内容,并保存到“散列文件.encrypt.decrypt”
        // 4.1 机密者对元素内容文件进行同样的散列,把得到的散列值和机密得到的散列值进行比对,如果一样,这任务元素内容是经过签名的,是可信的。
        rsa.decryptFile(new File("D:\\test.md5.txt.encrypt"));
        
    }
}

/**
 *
 */
class RSADemo {
    /**
     * 公钥
     */
    private RSAPublicKey publicKey;
    /**
     * 私钥
     */
    private RSAPrivateKey privateKey;
    /**
     * 用于加解密
     */
    private Cipher cipher;
    /**
     * 明文块的长度 它必须小于密文块的长度 - 11
     */
    private int originLength = 128;
    /**
     * 密文块的长度
     */
    private int encrytLength = 256;
    
    /**
     * 得到初始化的公钥和私钥
     * @throws NoSuchAlgorithmException 
     * @throws NoSuchPaddingException 
     */
    public void initKey() throws NoSuchAlgorithmException, NoSuchPaddingException {
        //RSA加密算法:创建密钥对,长度采用2048
        KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
        kg.initialize(encrytLength * 8);
        KeyPair keypair = kg.generateKeyPair();
        //分别得到公钥和私钥
        publicKey = (RSAPublicKey) keypair.getPublic();
        privateKey = (RSAPrivateKey) keypair.getPrivate();
        System.out.println("public key: " + publicKey);
        System.out.println("private key: " + privateKey);
    }
    /**
     * 将公钥保存至文件
     * @param file 待写入的文件
     * @return true 写入成功;false 写入失败
     */
    public boolean savePublicKey(File file) {
        return this.saveKey(publicKey, file);
    }
    /**
     * 将私钥保持至文件
     * @param file 待写入的文件
     * @return true 写入成功;false 写入失败
     */
    public boolean savePrivateKey(File file) {
        return this.saveKey(privateKey, file);
    }
    //将Key文件保持到文件中
    private boolean saveKey(Key key,File file) {
        boolean write;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            //System.out.println(key.getFormat());
            //公钥默认使用的是X.509编码,私钥默认采用的是PKCS #8编码
            byte [] encode = key.getEncoded();
            //注意,此处采用writeObject方法,读取时也要采用readObject方法
            oos.writeObject(encode);
            write = true;
        } catch (IOException e) {
            write = false;
        } finally {
                try {
                    if(fos != null) fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
        return write;
    }
    /**
     * 从文件中得到公钥
     * @param file 保存公钥的文件
     */
    public void getPublicKey(File file) {
        getKey(file,0);
    }
    /**
     * 从文件中得到私钥
     * @param file 保存私钥的文件
     */
    public void getPrivateKey(File file) {
        getKey(file,1);
    }
    
    private void getKey(File file,int mode) {
        FileInputStream fis;
        try {
            //读取数据
            fis = new FileInputStream(file);
            ObjectInputStream ois = new ObjectInputStream(fis);
            byte [] keybyte = (byte[]) ois.readObject();
            //关闭资源
            ois.close();
            //默认编码
            KeyFactory keyfactory = KeyFactory.getInstance("RSA");
            //得到公钥或是私钥
            if(mode == 0) {
                X509EncodedKeySpec x509eks= new X509EncodedKeySpec(keybyte);
                publicKey = (RSAPublicKey) keyfactory.generatePublic(x509eks);
                //System.out.println(pk.getAlgorithm());
            } else {
                PKCS8EncodedKeySpec pkcs8eks = new PKCS8EncodedKeySpec(keybyte);
                privateKey = (RSAPrivateKey) keyfactory.generatePrivate(pkcs8eks);
                //System.out.println(pk.getAlgorithm());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }       
    }

    /**
     * 数据RSA加密 (用私匙加密)
     * @param origin 明文
     * @return 密文
     */
    protected byte [] encrypt(byte [] origin) {
        //byte [] enc = new byte [encrytLength];
        byte [] enc = null;
        try {
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            enc = cipher.doFinal(origin);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return enc;
    }
    /**
     * 数据RSA解密 (用公匙解密)
     * @param enc 密文
     * @return 明文
     */
    protected byte [] decrypt(byte [] enc) {
        //byte [] origin = new byte [originLength];
        byte [] origin = null;
        try {
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            origin = cipher.doFinal(enc);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return origin;
    }
    /**
     * 加密文件
     * @param origin 明文件
     * @throws IOException
     */
    public void encryptFile(File origin) throws IOException {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        
        //读入
        fis = new FileInputStream(origin);
        BufferedInputStream bis = new BufferedInputStream(fis);
        byte [] originbyte = new byte [originLength];
        //写出
        fos = new FileOutputStream(new File(origin+".encrypt"));
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        byte [] encryptbyte;
        //int k;
        while(bis.read(originbyte) > 0) {
            encryptbyte = this.encrypt(originbyte);
            bos.write(encryptbyte);
            originbyte = new byte[originLength];
        }
        //压入
        bos.flush();
        //关闭资源
        if(fis != null) fis.close();
        if(fos != null) fos.close();        
    }
    /**
     * 解密文件
     * @param encrypt 密文件
     * @throws IOException
     */
    public void decryptFile(File encrypt) throws IOException {
        
        FileInputStream fis = null;
        FileOutputStream fos = null;
        //读入
        fis = new FileInputStream(encrypt);
        BufferedInputStream bis = new BufferedInputStream(fis);
        byte [] encryptbyte = new byte [encrytLength];
        //写出
        fos = new FileOutputStream(new File(encrypt+".decrypt"));
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        byte [] originbyte;
        
        //int k;
        while(bis.read(encryptbyte) > 0) {
            originbyte = this.decrypt(encryptbyte);
            bos.write(originbyte);
            encryptbyte = new byte [encrytLength];
        }
        //压入
        bos.flush();
        //关闭资源
        if(fis != null) fis.close();
        if(fos != null) fos.close();
    }
    
    
    /**********************************
     * MD5 part
     **********************************/
}

/**
* MD5的算法在RFC1321 中定义
* 在RFC 1321中,给出了Test suite用来检验你的实现是否正确:
* MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
* MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
* MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
* MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
* MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
*
* @author yilee
*
* 传入:一个字节数组
* 传出:字节数组的 MD5 结果字符串
*/
class MD5Demo {
    
    public byte[] getFileBytes(String fileName) throws Exception {
            FileInputStream fis = new FileInputStream(fileName);
            BufferedInputStream bis = new BufferedInputStream(fis);
            
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            byte[] data = new byte[4096];
            int count = -1;
            while ((count = bis.read(data, 0, 4096)) != -1) {
                outStream.write(data, 0, count);
            }

            data = null;
            if(fis != null) fis.close();
            
            return outStream.toByteArray();
    }
    
    public void saveString2File(String content, String fileName) throws Exception {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName)));
        bw.write(content);
        bw.close();
    }
    
    public String getMD5(byte[] source) {
        String s = null;
        char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
            md.update(source);
            byte tmp[] = md.digest();          // MD5 的计算结果是一个 128 位的长整数,
            // 用字节表示就是 16 个字节
            char str[] = new char[16 * 2];   // 每个字节用 16 进制表示的话,使用两个字符,
            // 所以表示成 16 进制需要 32 个字符
            int k = 0;                                // 表示转换结果中对应的字符位置
            for (int i = 0; i < 16; i++) {    // 从第一个字节开始,对 MD5 的每一个字节
                // 转换成 16 进制字符的转换
                byte byte0 = tmp[i];  // 取第 i 个字节
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];  // 取字节中高 4 位的数字转换,
                // >>> 为逻辑右移,将符号位一起右移
                str[k++] = hexDigits[byte0 & 0xf];   // 取字节中低 4 位的数字转换
            }
            s = new String(str);  // 换后的结果转换为字符串
 
        } catch (Exception e) {
            e.printStackTrace();
        }
        return s;
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值