数字证书


因此,数字证书就是集合了多种密码学算法,用于实现数据加解密、身份认证、签名等多种功能的一种安全标准。

数字证书可以防止中间人攻击,因为它采用链式签名认证,即通过根证书(Root CA)去签名下一级证书,这样层层签名,直到最终的用户证书。而Root CA证书内置于操作系统中,所以,任何经过CA认证的数字证书都可以对其本身进行校验,确保证书本身不是伪造的。

我们在上网时常用的HTTPS协议就是数字证书的应用。浏览器会自动验证证书的有效性:

要使用数字证书,首先需要创建证书。正常情况下,一个合法的数字证书需要经过CA签名,这需要认证域名并支付一定的费用。开发的时候,我们可以使用自签名的证书,这种证书可以正常开发调试,但不能对外作为服务使用,因为其他客户端并不认可未经CA签名的证书。

在Java程序中,数字证书存储在一种Java专用的key store文件中,JDK提供了一系列命令来创建和管理key store。我们用下面的命令创建一个key store,并设定口令123456:

几个主要的参数是:

  • keyalg:指定RSA加密算法;
  • sigalg:指定SHA1withRSA签名算法;
  • validity:指定证书有效期3650天;
  • alias:指定证书在程序中引用的名称;
  • dname:最重要的指定了Common Name,如果证书用在HTTPS中,这个名称必须与域名完全一致。

执行上述命令,JDK会在当前目录创建一个my.keystore文件,并存储创建成功的一个私钥和一个证书,它的别名是mycert

有了key store存储的证书,我们就可以通过数字证书进行加解密和签名:

  1. import java.io.InputStream;
  2. import java.math.BigInteger;
  3. import java.security.*;
  4. import java.security.cert.*;
  5. import javax.crypto.Cipher;
  6. public class Main {
  7. public static void main(String[] args) throws Exception {
  8. byte[] message = "Hello, use X.509 cert!".getBytes("UTF-8");
  9. // 读取KeyStore:
  10. KeyStore ks = loadKeyStore("/my.keystore", "123456");
  11. // 读取私钥:
  12. PrivateKey privateKey = (PrivateKey) ks.getKey("mycert", "123456".toCharArray());
  13. // 读取证书:
  14. X509Certificate certificate = (X509Certificate) ks.getCertificate("mycert");
  15. // 加密:
  16. byte[] encrypted = encrypt(certificate, message);
  17. System.out.println(String.format("encrypted: %x", new BigInteger(1, encrypted)));
  18. byte[] decrypted = decrypt(privateKey, encrypted);
  19. System.out.println("decrypted: " + new String(decrypted, "UTF-8"));
  20. byte[] sign = sign(privateKey, certificate, message);
  21. System.out.println(String.format("signature: %x", new BigInteger(1, sign)));
  22. // 验证签名:
  23. boolean verified = verify(certificate, message, sign);
  24. System.out.println("verify: " + verified);
  25. }
  26. static KeyStore loadKeyStore(String keyStoreFile, String password) {
  27. try (InputStream input = Main.class.getResourceAsStream(keyStoreFile)) {
  28. if (input == null) {
  29. throw new RuntimeException("file not found in classpath: " + keyStoreFile);
  30. }
  31. KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
  32. ks.load(input, password.toCharArray());
  33. return ks;
  34. } catch (Exception e) {
  35. throw new RuntimeException(e);
  36. }
  37. }
  38. static byte[] encrypt(X509Certificate certificate, byte[] message) throws GeneralSecurityException {
  39. Cipher cipher = Cipher.getInstance(certificate.getPublicKey().getAlgorithm());
  40. cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
  41. static byte[] decrypt(PrivateKey privateKey, byte[] data) throws GeneralSecurityException {
  42. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  43. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  44. return cipher.doFinal(data);
  45. }
  46. static byte[] sign(PrivateKey privateKey, X509Certificate certificate, byte[] message)
  47. throws GeneralSecurityException {
  48. Signature signature = Signature.getInstance(certificate.getSigAlgName());
  49. signature.initSign(privateKey);
  50. signature.update(message);
  51. return signature.sign();
  52. }
  53. static boolean verify(X509Certificate certificate, byte[] message, byte[] sig) throws GeneralSecurityException {
  54. Signature signature = Signature.getInstance(certificate.getSigAlgName());
  55. signature.initVerify(certificate);
  56. signature.update(message);
  57. return signature.verify(sig);
  58. }
  59. }

在上述代码中,我们从key store直接读取了私钥-公钥对,私钥以PrivateKey实例表示,公钥以X509Certificate表示,实际上数字证书只包含公钥,因此,读取证书并不需要口令,只有读取私钥才需要。如果部署到Web服务器上,例如Nginx,需要把私钥导出为Private Key格式,把证书导出为X509Certificate格式。

  1. 浏览器向服务器发起请求,服务器向浏览器发送自己的数字证书;
  2. 浏览器用操作系统内置的Root CA来验证服务器的证书是否有效,如果有效,就使用该证书加密一个随机的AES口令并发送给服务器;
  3. 服务器用自己的私钥解密获得AES口令,并在后续通讯中使用AES加密。

上述流程只是一种最常见的单向验证。如果服务器还要验证客户端,那么客户端也需要把自己的证书发送给服务器验证,这种场景常见于网银等。

注意:数字证书存储的是公钥,以及相关的证书链和算法信息。私钥必须严格保密,如果数字证书对应的私钥泄漏,就会造成严重的安全威胁。如果CA证书的私钥泄漏,那么该CA证书签发的所有证书将不可信。数字证书服务商就发生过私钥泄漏导致公司破产的事故。

从下载练习:使用数字证书 (推荐使用快速下载)

数字证书就是集合了多种密码学算法,用于实现数据加解密、身份认证、签名等多种功能的一种安全标准。

数字证书采用链式签名管理,顶级的Root CA证书已内置在操作系统中。