简介
日常开发中,我们程序员不怎么会接触证书相关的问题,对信息安全领域相关的内容知之甚少。因为平时主要实现的业务很少要直接面向底层的通信,也就很少关注这证书这样的知识。在一般情况下,我们仅仅只是在使用一些高层的依赖中会引入证书、加密相关的依赖包,比如:

<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.58</version>
</dependency>
然而, bouncy castle这样广泛使用证书密码项目(Github)并不是广受关注的明星项目,文档也就不怎么完备,甚至有点儿法语焉不详,不够系统完整。而且,这方面的内容包含了很多密码学方面专业名词,文档读起来就更晦涩难懂。可以参与bouncy castle的WIKI。我将大致的列举一下BC在证书方面的使用方法。

概念
ROOT证书、CA证书和使用CA证签发的X.509证书之间的联系,其实就是一个使用私钥签发(issue)的关系。使用ROOT证书的私钥签发CA证书,再使用CA证书签发其他的X.509证书,这样就形成了一条可以信的Path。在被签发的证书中含有使用签发者使用自己私钥加过密的签名(sign)、签名算法(比如Sha256WithHash)以及签名者的一些身份信息,这个我们可以在验证签名的过程中得过启发。

从上面可以看出,我们可以把一个证书主要分为两类:自签名和Ca签名,并且我在上面提到的证书中只有ROOT的是自签名的。

使用Bouncy Castle
生成一个签名证书
ROOT证书是用来签发和验证CA证书的,在整个可信证书链条中处于最上层的地位,它是一个自签名的证书。签名的作用是防止在不知道签名者私钥的情况下恶意篡改证书的内容,这是证书可信保证重要的步骤。如下代码是生成一个签名的证书:

// must add BC Provider before use it in code
Security.addProvider(new BouncyCastleProvider());

// generate EC key pair
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("prime192v1");
KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", "BC");
generator.initialize(ecGenSpec, new SecureRandom());
KeyPair kp = generator.genKeyPair();

Date startDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
Date endDate = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000);

// create x.509 certificate build
X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(
  new X500Principal("CN=Test"),
  BigInteger.valueOf(System.currentTimeMillis()),
  startDate, endDate,
  new X500Principal("CN=Test"),
  kp.getPublic());

// Content Signer
ContentSigner signer = new JcaContentSignerBuilder("SHA256withECDSA")
  .setProvider("BC").build(kp.getPrivate());

// build x.509 certificate
X509CertificateHolder holder = v3CertGen.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().getCertificate(holder);

holder即我们生成的X.509证书,对于生成的证书我们可以使用org.bouncycastle.openssl.jcajce.JcaPEMWriter转化成字符串:

// certificate to write to string
X509CertificateHolder holder = ...;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(new PrintWriter(bos));
jcaPEMWriter.writeObject(holder);
jcaPEMWriter.flush();

System.out.printf(bos.toString());

这个我们生成的证书的内容:

-----BEGIN CERTIFICATE-----
MIHwMIGmoAMCAQICBgFfoSV4vTAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0
MB4XDTE3MTEwODE0MTgyOFoXDTE3MTEyNjE0NTg1N1owDzENMAsGA1UEAxMEVGVz
dDBJMBMGByqGSM49AgEGCCqGSM49AwEBAzIABLgAmZuZxd6IFZ7tJS8H0VY1Wify
2i+YTIwF5cw0O7t1pcAh6dkF021bh+W0gk/rzzAKBggqhkjOPQQDAgM5ADA2AhkA
6Vb1VWTQrwcrEr2+7eTkAcwiHJ+YqGFoAhkAxWvJu2L+8dQbXXa6bd48zvR0ifX8
849E
-----END CERTIFICATE-----
验证证书的签名
以下代码用于验证我们的签名是否合法。

// public key from issuer, used to verify signature
PublicKey publicKey = ...;

ContentVerifierProvider contentVerifierProvider = new JcaContentVerifierProviderBuilder()
  .setProvider("BC").build(publicKey);

if (!certHolder.isSignatureValid(contentVerifierProvider)) {
  System.err.println("signature invalid");
} else {
  System.out.printf("signature valid");
}
使用来自Oracle的证书支持
oracle Java使用keystore来存储证书及其私钥,并提供一个命令行工具——keytool。

//generate key pair with alias test
keytool -genkeypair -alias test -keyalg EC -validity 365 -keypass password -keysize 571 -keystore ./test.jks  -storepass password

//generate certificate with public key from keypair test
keytool -certreq -alias test -sigalg SHA256WithECDSA -keypass password -keysize 571 -keystore ./test.jks  -storepass password

//list jdk content
keytool -list -rfc  -keystore ./test.jks
加载Keystore文件
使用java.security.KeyStore加载Keystore文件:

KeyStore keyStore = KeyStore.getInstance("jks");
        keyStore.load(new FileInputStream("C:\\...\\test.jks"), "password".toCharArray());
        Certificate certificate = keyStore.getCertificate("test");
1
2
3
可以看到X.509的证书结构:

[
[
  Version: V3
  Subject: CN=yinkn, OU=ericsson, O=ericsson, L=GZ, ST=GD, C=CN
  Signature Algorithm: SHA256withECDSA, OID = 1.2.840.10045.4.3.2

Key:  Sun EC public key, 571 bits
  public x coord: 3002605725884979889022476092292003776673075892528685922593430921126421431956026876782257630852231195990030412442522229709626574940546289855595376742293701765308565676110538
  public y coord: 3969672869627949486403678776244029006985355959068388562809699610619530481229193224794938137041924106460645494573259209667845661791858569871701054704389588094823968191257337
  parameters: sect571k1 [NIST K-571] (1.3.132.0.38)
  Validity: [From: Thu Nov 09 23:02:59 GMT+08:00 2017,
               To: Fri Nov 09 23:02:59 GMT+08:00 2018]
  Issuer: CN=yinkn, OU=ericsson, O=ericsson, L=GZ, ST=GD, C=CN
  SerialNumber: [    1ae30239]

Certificate Extensions: 1
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 02 50 11 0F 4C F1 C3 FA   FC BB 0F 19 5C 83 D6 36  .P..L.......\..6
0010: 95 C3 E3 3E                                        ...>
]
]

]
  Algorithm: [SHA256withECDSA]
  Signature:
0000: 30 81 93 02 48 00 9B D1   92 9E 7B 95 FC 0E 11 19  0...H...........
0010: 28 25 C2 C2 35 2E D5 B1   74 5C F4 EA A9 D8 71 95  (%..5...t\....q.
0020: F1 72 BE 8B 53 A2 8D CA   AC 88 96 A5 86 B3 46 14  .r..S.........F.
0030: 1B B1 8B A5 12 08 B7 7D   ED 23 BF 64 DD 46 E2 33  .........#.d.F.3
0040: E0 6B C1 62 14 25 BE 5C   B9 AB 45 04 AD 02 47 6F  .k.b.%.\..E...Go
0050: D0 B9 B2 9E A1 D3 D6 98   8A 56 16 38 6B BB 27 B1  .........V.8k.'.
0060: 42 53 D7 27 95 2F 96 2E   E4 39 19 3D A3 54 54 8B  BS.'./...9.=.TT.
0070: 71 DA 0E 91 54 5F 78 37   CA 03 58 4F 56 FD C1 BD  q...T_x7..XOV...
0080: 43 9D 06 CF DF 6A B1 C6   4D 5C 0B 06 62 FF DF 8C  C....j..M\..b...
0090: 33 0B A7 62 0A ED                                  3..b..

]

验证签名是否合法
代码来自项目Californiumnn,类org.eclipse.californium.scandium.dtls.CertificateVerify:

/**
  * Tries to verify the client's signature contained in the CertificateVerify
  * message.
  * 
  * @param clientPublicKey
  *            the client's public key.
  * @param handshakeMessages
  *            the handshake messages exchanged so far.
  * @throws HandshakeException if the signature could not be verified.
  */
public void verifySignature(PublicKey clientPublicKey, byte[] handshakeMessages) throws HandshakeException {
  boolean verified = false;
  try {
    Signature signature = Signature.getInstance(signatureAndHashAlgorithm.jcaName());
    signature.initVerify(clientPublicKey);

signature.update(handshakeMessages);

verified = signature.verify(signatureBytes);

} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException e) {
    LOGGER.log(Level.SEVERE,"Could not verify the client's signature.", e);
  }

if (!verified) {
    String message = "The client's CertificateVerify message could not be verified.";
    AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE, getPeer());
    throw new HandshakeException(message, alert);
  }
}
验证Trust Chain
/**
  * Validates the X.509 certificate chain provided by the the peer as part of this message.
  * 
  * This method checks
  * <ol>
  * <li>that each certificate's issuer DN equals the subject DN of the next certiciate in the chain</li>
  * <li>that each certificate is currently valid according to its validity period</li>
  * <li>that the chain is rooted at a trusted CA</li>
  * </ol>
  * 
  * @param certificate certificate to be verified 
  * @param trustedCertificates the list of trusted root CAs
  * 
  * @throws HandshakeException if any of the checks fails
  */
public void verifyCertificate(X509Certificate certificate, X509Certificate[] trustedCertificates) throws HandshakeException {
  CertificateFactory factory = CertificateFactory.getInstance("X.509");
  CertPath certPath = factory.generateCertPath(Arrays.asList(certificate));

Set<TrustAnchor> trustAnchors = getTrustAnchors(trustedCertificates);

try {
    PKIXParameters params = new PKIXParameters(trustAnchors);
    // TODO: implement alternative means of revocation checking
    params.setRevocationEnabled(false);

CertPathValidator validator = CertPathValidator.getInstance("PKIX");
    validator.validate(certPath, params);

} catch (GeneralSecurityException e) {
    if (LOGGER.isLoggable(Level.FINEST)) {
      LOGGER.log(Level.FINEST, "Certificate validation failed", e);
    } else if (LOGGER.isLoggable(Level.FINE)) {
      LOGGER.log(Level.FINE, "Certificate validation failed due to {0}", e.getMessage());
    }
    AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE, getPeer());
    throw new HandshakeException("Certificate chain could not be validated", alert);
  }
}

private static Set<TrustAnchor> getTrustAnchors(X509Certificate[] trustedCertificates) {
  Set<TrustAnchor> result = new HashSet<>();
  if (trustedCertificates != null) {
    for (X509Certificate cert : trustedCertificates) {
      result.add(new TrustAnchor((X509Certificate) cert, null));
    }
  }
  return result;

到现在为止,本文涉及的只是安全甚至证书领域的一鳞半爪,也只是一些简单的内容。要深刻理解这方面的内容,必须深入实际的应用。

ROOT证书、CA证书和使用CA签发的X.509证书相关推荐

  1. 自建ca根证书_如何创建私有 CA 并签发证书

    为什么需要自己的 CA? 因为公共 CA (比如排名前几的这几家:Comodo, Symantec, GlobalSign, DigiCert, StartCom)颁发证书要收费,而且价格很贵.当然现 ...

  2. 实战:搭建CA认证中心,使用CA证书搭建HTTPS

    CA认证中心服务端:xuegod63.cn                         IP:192.168.0.61 客户端                  :xuegod64.cn      ...

  3. https证书互信解决方案—创建私有CA并申请证书

    前言 https相较于http而言有很大的安全性,当我们一个服务开启https并与之通信时,往往需要证书的认证,如果是浏览器访问服务,只要在浏览器内设置信任证书即可,而如果是程序内访问服务(如java ...

  4. centos 配置证书_如何在CentOS 8上设置和配置证书颁发机构(CA)

    centos 配置证书 介绍 (Introduction) A Certificate Authority (CA) is an entity responsible for issuing digi ...

  5. 沃通的证书受到质疑,StartCom CA 受处罚

    导读 Lightbot继 Mozilla 做出对沃通WoSign的处罚决定之后,谷歌也跟随了这一做法,从 Chrome 56 开始,不再信任沃通及被其收购的 StartCom 于 2016 年 10 ...

  6. 用 openssl 生成 SSL 使用的私钥和证书,并自己做 CA 签名

    一.生成 CA 证书 1.生成 CA 的公私钥对. openssl genrsa -out ca.key 1024 2.生成 CA 的自签名证书,签名使用的私钥为 ca.key . openssl r ...

  7. php 微信支付 ca证书,微信公众号红包接口开发PHP开发 CA证书出错,请登陆微信支付商户平台下载证书...

    微信红包接口调试过程中一直提示"CA证书出错,请登陆微信支付商户平台下载证书",经反复调试,大致解决方法如下: 1.首先确保CA证书的路径是否正确,一定得是绝对路径,因为是PHP开 ...

  8. java 微信转账 ca_error_对接微信红包时:CA证书出错,请登录微信支付商户平台下载证书...

    今天在对接微信支付的微信红包发放时,出现""CA证书出错,请登录微信支付商户平台下载证书"的错误,特此记录一下: 如果你也在对接微信红包,并且你也在这个页面上下载了dem ...

  9. GlobalSign即将停止签发SHA1代码签名证书

    关于GlobalSign即将停止签发SHA-1代码签名的公告 尊敬的客户/合作伙伴: GlobalSign的代码签名证书产品将面临两个主要变化,如下: 一,即将停止SHA-1代码签名的签发 Globa ...

最新文章

  1. delphi usb 读写_写作论语 | 崔嵘:写我所读——国外整本书阅读中读写结合的理论与实践(上)...
  2. Playframework2 标签速记
  3. 如何加入IETF 如何发表自己的RFC
  4. 使用Spring Boot和Logback登录到Redis
  5. 轻量级的开源集成:Apache Camel还是Spring集成?
  6. 什么是JVM?JVM概述——初识JVM(类加载器,垃圾回收器,执行引擎)
  7. 不用鼠标,程序员编程竟能如此高效?
  8. 如何在C预处理器中可靠地检测Mac OS X,iOS,Linux,Windows? [重复]
  9. 智能优化算法:水基湍流优化算法-附代码
  10. Sql server bulk insert
  11. [GAMP学习笔记]计算STEC程序中遇到的一些问题小结
  12. 常见拓展名--的含义(扩宽知识面)
  13. oracle odi 配置安装,ODI的安装和配置
  14. 微信公众号回复小程序链接
  15. 【计算机网络】2.1 应用层协议原理
  16. NCRE | 三级网络技术笔记
  17. ios自动订阅服务器,iOS IAP - 自动续期订阅
  18. 【电力电子】【2007.05】三相多电平逆变器的研究与分析
  19. Sentence-Transformer的使用及fine-tune教程
  20. 计算机资源管理器出问题怎么办,W7系统资源管理器已停止工作怎么办

热门文章

  1. 2020年贺岁杯围棋争霸赛今日开赛,AI+围棋看点十足
  2. 114DNS 出现故障,服务全线飘红!
  3. PHP美团开放平台开发记录,美团外卖,第一步授权演示及错误提示:获取门店出错(3)解决方法
  4. Nginx通过二级目录映射不同的反向代理
  5. 聊聊Redis消息队列-实现异步秒杀
  6. pd.factorize ( ) 解析
  7. Linux提取文件夹中文件名,linux命令提取文件夹内特定文件的路径与文件名
  8. 华为交换机开启 DAI功能
  9. 微信小程序第四篇:生成图片并保存到手机相册
  10. selector到底是什么