java实现签名验签详解(含所有代码)

第一部分:什么是签名验签?

私钥:可以解密公钥加密的数据

公钥:可以解密私钥加密的数据

也就是说公钥和私钥之间可以互相加解密

公钥加密私钥解密称之为——加解密

私钥加密公钥解密称之为——签名验签

签名:使用私钥对数据进行加密,该操作称之为——签名

验签:使用与私钥对应的公钥进行解密,该操作称之为——验签

到此知道什么是公钥什么是私钥,以及区别和可以用来干嘛的了。那么下面开始进入正题(如果公钥私钥和签名验签的概念还有不明白的朋友请自行百度,这篇博客的重点是关于如何通过java代码在实现签名验签)

第二部分:java实现签名验签

废话:数据在网络中通信,安全一直是一个比较核心的问题和困扰,博主在2018年的时候,参与过一个支付项目的开发,凡是鉴于当时的水平原文,对于数据安全那一块的开发并没有参与,仅仅是使用了别人写好的api然后调用。印象比较深刻的就是pfx文件和cer文件。当时就知道pfx是用来签名的,cer文件是用来验签的。别的就不知道了。后来到了现在的这家公司,我也到了支付小组,发现这边的数据传输是通过时间戳和一些约定的其他的参数来做一个MD5摘要(这里强调下,MD5不是加密,而是一种信息摘要的算法,是一种散列函数),瞬间就感觉好low的。然后我就会想起之前公司的方式。花了大量的时间去网上查阅资料和看博客。但是结果发现很多都是你抄我我抄你,而且网上的很多根本就没法用于生产,你们不信自己百度就知道了。你去看看你的证书或者是私钥怎么给对方,直接将串给对方吗?这样是不是显然没有达到生成的级别

总体思路:先介绍下总体思路,有助于读者更好的理解本博客的内容。服务端,创建根证书(相当于ca机构,https协议之所以能够保证数据的安全传输,其核心就是签名验签,如果您对这部分也不了解,或者说想学习下这部分,请给博主留言。只要有一人想知道。我就不惜下班后加班加点写博客。为你们解释清楚),然后通过根证书来创建实际来签名验签的证书,当然,除了根证书,这样的用来签名验签数据的证书需要有两套。为什么需要两套呢?

①服务端自己的一套公私钥(服务端的公钥是需要先提供给客户端的)。这一套的作用是:当服务端向客户端传输数据的时候,服务端使用服务端的私钥进行签名。然后客户端使用服务端的公钥验签,这样客户端可以验证服务端的身份和数据是否被篡改

②客户端自己也有一套公私钥(当然客户端的这一套是需要服务端提供的,服务端将客户端这套的私钥提供给客户端,同时服务端需要保留客户端的公钥)。这一套的作用是:当客户端向服务端传输数据的时候,客户端需要通过客户端的私钥来签名,而服务端刚好可以使用客户端的公钥来验签,以判断数据在传输的过程中是否被篡改过

明白没?没明白看代码。我会注释的很详细的

2.1、创建根证书

  1 import com.example.signature.util.IssueCertUtils;
  2 import org.slf4j.Logger;
  3 import org.slf4j.LoggerFactory;
  4 import sun.security.tools.keytool.CertAndKeyGen;
  5 import sun.security.x509.X500Name;
  6 
  7 import java.io.*;
  8 import java.security.*;
  9 import java.security.cert.CertificateException;
 10 import java.security.cert.X509Certificate;
 11 
 12 /**
 13  * fileName:IssueRootCert
 14  *
 15  * @author :zyz
 16  * Date    :2020/1/15 11:29
 17  * -------------------------
 18  * 功能和描述:颁发根证书
 19  **/
 20 public class IssueRootCert {
 21     public static final Logger logger = LoggerFactory.getLogger(IssueRootCert.class);
 22     private static SecureRandom secureRandom;
 23 
 24     static {
 25         //定义随机数来源
 26         try {
 27             secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
 28         } catch (NoSuchAlgorithmException e) {
 29             logger.error("算法不存在");
 30         } catch (NoSuchProviderException e) {
 31             logger.error("该随机数提供者不存在");
 32         }
 33     }
 34 
 35     /**
 36      * 定义pfx根证书文件
 37      */
 38     public static final String ROOT_ISSUE_PFX_FILE = "D:\\signverify\\rootcert\\ROOTCA.pfx";
 39 
 40     /**
 41      * 定义私钥证书的密码
 42      */
 43     public static final String ROOT_ISSUE_PFX_PASSWORD = "123456";
 44     /**
 45      * 定义crt根证书文件
 46      */
 47     public static final String ROOT_ISSUE_CRT_FILE = "D:\\signverify\\rootcert\\ROOTCA.cer";
 48 
 49     /**
 50      * 定义根证书的别名
 51      */
 52     public static final String ROOT_ISSUE_ALIAS = "rootca";
 53 
 54     public static void main(String[] args) {
 55         try {
 56             X500Name issue = new X500Name("CN=RootCA,OU=ISI,O=BenZeph,L=CD,ST=SC,C=CN");
 57             issueRootCert(issue);
 58         } catch (IOException e) {
 59             e.printStackTrace();
 60         }
 61 
 62     }
 63 
 64     /**
 65      * 签名算法
 66      */
 67     public static final String ALGORITHM = "MD5WithRSA";
 68 
 69     public static void issueRootCert(X500Name x500Name) {
 70         try {
 71             CertAndKeyGen certAndKeyGen = new CertAndKeyGen("RSA", ALGORITHM, null);
 72             //设置生成密钥时使用的随机数的来源
 73             certAndKeyGen.setRandom(secureRandom);
 74 
 75             //设置密钥长度,太短容易被攻击破解
 76             certAndKeyGen.generate(1024);
 77 
 78             //时间间隔设置为10年(设置证书有效期的时候需要使用到)
 79             long interval = 60L * 60L * 24L * 3650;
 80             //
 81             X509Certificate x509Certificate = certAndKeyGen.getSelfCertificate(x500Name, interval);
 82 
 83             X509Certificate[] x509Certificates = new X509Certificate[]{x509Certificate};
 84 
 85             IssueCertUtils.createKeyStore(ROOT_ISSUE_ALIAS, certAndKeyGen.getPrivateKey(), ROOT_ISSUE_PFX_PASSWORD.toCharArray(), x509Certificates, ROOT_ISSUE_PFX_FILE);
 86             //根据私钥导出公钥
 87             OutputStream outputStream = new FileOutputStream(new File(ROOT_ISSUE_CRT_FILE));
 88             outputStream.write(x509Certificate.getEncoded());
 89             outputStream.close();
 90         } catch (NoSuchAlgorithmException e) {
 91             e.printStackTrace();
 92         } catch (NoSuchProviderException e) {
 93             e.printStackTrace();
 94         } catch (InvalidKeyException e) {
 95             e.printStackTrace();
 96         } catch (CertificateException e) {
 97             e.printStackTrace();
 98         } catch (SignatureException e) {
 99             e.printStackTrace();
100         } catch (FileNotFoundException e) {
101             e.printStackTrace();
102         } catch (IOException e) {
103             e.printStackTrace();
104         }
105     }
106 }

2.2、创建服务端证书

  1 import com.example.signature.util.IssueCertUtils;
  2 import sun.security.tools.keytool.CertAndKeyGen;
  3 import sun.security.x509.*;
  4 
  5 import java.io.*;
  6 import java.security.*;
  7 import java.security.cert.CertificateException;
  8 import java.security.cert.CertificateFactory;
  9 import java.security.cert.X509Certificate;
 10 import java.util.Date;
 11 import java.util.Random;
 12 
 13 /**
 14  * fileName:IssueCert
 15  *
 16  * @author :zyz
 17  * Date    :2020/1/15 12:46
 18  * -------------------------
 19  * 功能和描述:颁发证书
 20  **/
 21 public class IssueCert {
 22     private static SecureRandom secureRandom;
 23 
 24     static {
 25         try {
 26             secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
 27         } catch (NoSuchAlgorithmException e) {
 28             e.printStackTrace();
 29         } catch (NoSuchProviderException e) {
 30             e.printStackTrace();
 31         }
 32     }
 33 
 34     /**
 35      * 私钥证书-用于签名
 36      */
 37     public static final String ISSUE_PFX_FILE = "D:\\signverify\\mycert\\ISSUE.pfx";
 38     /**
 39      * 公钥证书-用于验签
 40      */
 41     public static final String ISSUE_CRT_FILE = "D:\\signverify\\mycert\\ISSUE.cer";
 42 
 43     /**
 44      * 定义pfx根证书文件
 45      */
 46     public static final String ROOT_ISSUE_PFX_FILE = "D:\\signverify\\rootcert\\ROOTCA.pfx";
 47 
 48     /**
 49      * 定义私钥证书的密码
 50      */
 51     public static final String ROOT_ISSUE_PFX_PASSWORD = "123456";
 52     /**
 53      * 定义crt根证书文件
 54      */
 55     public static final String ROOT_ISSUE_CRT_FILE = "D:\\signverify\\rootcert\\ROOTCA.cer";
 56 
 57     /**
 58      * 定义根证书的别名
 59      */
 60     public static final String ROOT_ISSUE_ALIAS = "rootca";
 61 
 62     /**
 63      * 证书别名
 64      */
 65     public static final String ISSUE_ALIAS = "subject";
 66 
 67     /**
 68      * 私钥证书密码
 69      */
 70     public static final String ISSUE_PASSWORD = "123456";
 71 
 72 
 73     /**
 74      * 签名算法
 75      */
 76     public static final String SIG_ALG = "MD5WithRSA";
 77 
 78     public static void main(String[] args) {
 79         try {
 80 
 81             X500Name issue = new X500Name("CN=RootCA,OU=ISI,O=BenZeph,L=CD,ST=SC,C=CN");
 82             X500Name subject = new X500Name(
 83                     "CN=subject,OU=ISI,O=BenZeph,L=CD,ST=SC,C=CN");
 84             createIssueCert(issue, subject);
 85         } catch (IOException e) {
 86             e.printStackTrace();
 87         }
 88     }
 89 
 90     /**
 91      *
 92      */
 93     public static void createIssueCert(X500Name rootX500name, X500Name subjectX500Name) {
 94         try {
 95             CertAndKeyGen certAndKeyGen = new CertAndKeyGen("RSA", SIG_ALG, null);
 96 
 97             //生成密钥时候使用的随机数的来源
 98             certAndKeyGen.setRandom(secureRandom);
 99 
100             //设置密钥的大小
101             certAndKeyGen.generate(1024);
102 
103 
104             //设置时间,设置证书有效期的时候需要使用到
105             long validity = 60L * 60L * 24L * 3650;
106             Date startDate = new Date();
107             Date endDate = new Date();
108             endDate.setTime(startDate.getTime() + validity * 1000);
109             //设置证书有效期
110             CertificateValidity interval = new CertificateValidity(startDate, endDate);
111 
112             //获取X509CertInfo对象,并为其添加所有的强制属性
113             X509CertInfo info = new X509CertInfo();
114 
115             info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
116             info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new Random().nextInt() & 0x7fffffff));
117 
118             AlgorithmId algID = AlgorithmId.get(SIG_ALG);
119             info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algID));
120 
121             info.set(X509CertInfo.SUBJECT, subjectX500Name);
122 
123             info.set(X509CertInfo.KEY, new CertificateX509Key(certAndKeyGen.getPublicKey()));
124 
125             info.set(X509CertInfo.VALIDITY, interval);
126 
127             info.set(X509CertInfo.ISSUER, rootX500name);
128 
129             PrivateKey privateKey = getPrivateKey();
130 
131             X509CertImpl cert = new X509CertImpl(info);
132             cert.sign(privateKey, SIG_ALG);
133 
134             //X509Certificate certificate = (X509Certificate) cert;
135 
136             X509Certificate x509Certificate = readX509Certificate();
137 
138             X509Certificate[] x509Certificates = new X509Certificate[]{cert, x509Certificate};
139 
140             IssueCertUtils.createKeyStore(ISSUE_ALIAS, certAndKeyGen.getPrivateKey(), ISSUE_PASSWORD.toCharArray(), x509Certificates, ISSUE_PFX_FILE);
141 
142             OutputStream outputStream = new FileOutputStream(new File(ISSUE_CRT_FILE));
143             outputStream.write(cert.getEncoded());
144             outputStream.close();
145 
146         } catch (NoSuchAlgorithmException e) {
147             e.printStackTrace();
148         } catch (NoSuchProviderException e) {
149             e.printStackTrace();
150         } catch (InvalidKeyException e) {
151             e.printStackTrace();
152         } catch (CertificateException e) {
153             e.printStackTrace();
154         } catch (IOException e) {
155             e.printStackTrace();
156         } catch (SignatureException e) {
157             e.printStackTrace();
158         }
159     }
160 
161 
162     /**
163      * 获取私钥
164      *
165      * @return
166      */
167     private static PrivateKey getPrivateKey() {
168         try {
169             //后去指定类型的KeyStore对象
170             KeyStore keyStore = KeyStore.getInstance("PKCS12");
171             InputStream in = null;
172             in = new FileInputStream(ROOT_ISSUE_PFX_FILE);
173             keyStore.load(in, ROOT_ISSUE_PFX_PASSWORD.toCharArray());
174             in.close();
175             //使用指定的密码来获取指定的别名对应的私钥
176             Key key = keyStore.getKey(ROOT_ISSUE_ALIAS, ROOT_ISSUE_PFX_PASSWORD.toCharArray());
177             return (PrivateKey) key;
178         } catch (KeyStoreException e) {
179             e.printStackTrace();
180         } catch (FileNotFoundException e) {
181             e.printStackTrace();
182         } catch (CertificateException e) {
183             e.printStackTrace();
184         } catch (NoSuchAlgorithmException e) {
185             e.printStackTrace();
186         } catch (IOException e) {
187             e.printStackTrace();
188         } catch (UnrecoverableKeyException e) {
189             e.printStackTrace();
190         }
191         return null;
192     }
193 
194     /**
195      * 读取crt根证书信息
196      *
197      * @return
198      */
199     private static X509Certificate readX509Certificate() {
200         InputStream inputStream = null;
201         try {
202             inputStream = new FileInputStream(ROOT_ISSUE_CRT_FILE);
203             CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
204             X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
205 
206             inputStream.close();
207             return certificate;
208         } catch (FileNotFoundException e) {
209             e.printStackTrace();
210         } catch (CertificateException e) {
211             e.printStackTrace();
212         } catch (IOException e) {
213             e.printStackTrace();
214         }
215         return null;
216     }
217 }

2.3、创建客户端证书

和创建客户端方式一模一样

2.4、测试签名验签

  1 import sun.misc.BASE64Decoder;
  2 import sun.misc.BASE64Encoder;
  3 
  4 import java.io.FileInputStream;
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.security.KeyStore;
  8 import java.security.PrivateKey;
  9 import java.security.PublicKey;
 10 import java.security.Signature;
 11 import java.security.cert.Certificate;
 12 import java.security.cert.CertificateFactory;
 13 
 14 /**
 15  * fileName:SignVerifyDemo
 16  *
 17  * @author :zyz
 18  * Date    :2020/1/15 14:35
 19  * -------------------------
 20  * 功能和描述:
 21  **/
 22 public class SignVerifyDemo {
 23     public static final String PRIVATE_KEY_PASSWORD = "123456";
 24 
 25     public static final String PUBLIC_KEY_FILE_PATH = "D:\\signverify\\mycert\\ISSUE.cer";
 26     //public static final String PUBLIC_KEY_FILE_PATH = "D:\\signature\\mykey.cer";
 27     public static final String PRIVATE_KEY_FILE_PATH = "D:\\signverify\\mycert\\ISSUE.pfx";
 28     //public static final String PRIVATE_KEY_FILE_PATH = "D:\\signature\\mykey.pfx";
 29     public static final String ALIAS_NAME = "subject";
 30     public static final String DEFAULT_UTF8 = "UTF-8";
 31 
 32     public static void main(String[] args) {
 33         String originalData = "Hello我是原始的数据World";
 34         String sign = sign(originalData);
 35         System.out.println(sign);
 36         boolean verify = verify(sign, originalData);
 37         if (verify) {
 38             System.out.println("验签通过");
 39         } else {
 40             System.out.println("验签失败");
 41         }
 42 
 43     }
 44 
 45     /**
 46      * 签名
 47      */
 48     public static String sign(String originalData) {
 49         String base64Sign = "";
 50         try {
 51 
 52             //返回与此给定的别名的密码,并用给定的密钥来恢复它
 53             PrivateKey privateKey = getPrivateKey();
 54 
 55             //返回指定签名的Signature对象
 56             Signature sign = Signature.getInstance("SHA1withRSA");
 57 
 58             //初始化这个用于签名的对象
 59             sign.initSign(privateKey);
 60 
 61             byte[] bysData = originalData.getBytes(DEFAULT_UTF8);
 62 
 63             //使用指定的byte数组更新要签名的数据
 64             sign.update(bysData);
 65             //返回所有已经更新数据的签名字节
 66             byte[] signByte = sign.sign();
 67             //对其进行Base64编码
 68             BASE64Encoder encoder = new BASE64Encoder();
 69             base64Sign = encoder.encode(signByte);
 70         } catch (Exception e) {
 71             System.out.println("签名异常");
 72             e.printStackTrace();
 73         }
 74         return base64Sign;
 75     }
 76 
 77 
 78     /**
 79      * 验签
 80      *
 81      * @param signStr      签名数据
 82      * @param originalData 原始数据
 83      * @return
 84      */
 85     public static boolean verify(String signStr, String originalData) {
 86         System.out.println("开始进行验签,原始数据为:" + originalData);
 87         try {
 88             //从此证书对象中获取公钥
 89             PublicKey publicKey = getPublicKey();
 90 
 91             //将签名数据
 92             BASE64Decoder decoder = new BASE64Decoder();
 93             byte[] signed = decoder.decodeBuffer(signStr);
 94 
 95             //通过Signature的getInstance方法,获取指定签名算法的Signature对象
 96             Signature signature = Signature.getInstance("SHA1withRSA");
 97             //初始化用于验证的对象
 98             signature.initVerify(publicKey);
 99             //使用指定的byte[]更新要验证的数据
100             signature.update(originalData.getBytes(DEFAULT_UTF8));
101             //验证传入的签名
102             return signature.verify(signed);
103         } catch (Exception e) {
104             return false;
105         }
106 
107     }
108 
109     /**
110      * 获取公钥
111      *
112      * @return
113      */
114     private static PublicKey getPublicKey() {
115         InputStream in = null;
116         try {
117             in = new FileInputStream(PUBLIC_KEY_FILE_PATH);
118             //获取实现指定证书类型的CertificateFactory对象
119             CertificateFactory cf = CertificateFactory.getInstance("x509");
120             //生成一个证书对象,并从执行的输入流中读取数据对它进行初始化
121             Certificate certificate = cf.generateCertificate(in);
122             //从此证书中获取公钥
123             return certificate.getPublicKey();
124         } catch (Exception e) {
125             e.printStackTrace();
126             return null;
127         } finally {
128             if (null != in) {
129                 try {
130                     in.close();
131                 } catch (IOException e) {
132                     e.printStackTrace();
133                 }
134             }
135         }
136     }
137 
138 
139     /**
140      * 获取私钥
141      *
142      * @return
143      */
144     private static PrivateKey getPrivateKey() {
145         InputStream in = null;
146         try {
147             in = new FileInputStream(PRIVATE_KEY_FILE_PATH);
148             //返回指定类型的KeyStore对象
149             KeyStore keyStore = KeyStore.getInstance("PKCS12");
150 
151             char[] pscs = PRIVATE_KEY_PASSWORD.toCharArray();
152             //从给定的输入流中加载此keyStore
153             keyStore.load(in, pscs);
154             //返回与给定别名关联的密钥,并用给定的密码来恢复它
155             return (PrivateKey) keyStore.getKey(ALIAS_NAME, pscs);
156         } catch (Exception e) {
157             e.printStackTrace();
158             return null;
159         } finally {
160             if (null != in) {
161                 try {
162                     in.close();
163                 } catch (IOException e) {
164                     e.printStackTrace();
165                 }
166             }
167         }
168 
169     }
170 
171 }

猜你喜欢

转载自www.cnblogs.com/zyzblogs/p/12197542.html