概念
证书
证书是由CA(Certificate Authority,证书颁发机构)或自行签发的,用于确定身份安全性和进行数字签名等操作的文件。
证书的内容
X.509标准证书通常包含以下内容:
- 版本号:标识证书的X.509版本(v1/v2/v3)
- 序列号:颁发机构分配的唯一标识符
- 签名算法:如SHA256withRSA、SHA256withECDSA等
- 颁发机构(Issuer):签发该证书的CA信息
- 证书有效时间:包含生效时间(Not Before)和过期时间(Not After)
- 主体(Subject):证书持有者的信息(域名、组织、国家等)
- 公钥:证书持有者的公钥信息
- 扩展信息:如SAN(Subject Alternative Name)、密钥用途等(v3版本支持)
证书的作用
- 身份验证:验证证书持有者的身份,确保通信对方的真实性
- 数据加密:利用证书中的公钥对数据进行加密,保证传输安全
- 签名验证:通过证书验证数据签名的真实性,确保数据完整性和不可否认性
证书的类型
- 自签名证书:由自己签发,通常用于测试环境
- CA签发证书:由权威CA机构签发,用于生产环境
- DV证书(域名验证)
- OV证书(组织验证)
- EV证书(扩展验证)
私钥
私钥是非对称加密中的关键组成部分,必须严格保密,与公钥配对使用。
私钥的格式和编码
私钥转换成字符串主要取决于私钥的格式和编码方式:
格式:
PKCS#1:较老的格式,仅支持RSA算法,以-----BEGIN RSA PRIVATE KEY-----开头
PKCS#8:通用格式,支持多种算法,以-----BEGIN PRIVATE KEY-----开头
编码:
PEM:Base64编码的文本格式,包含头尾标记
DER:二进制编码格式
PEM vs DER
PEM格式:
- 以
-----BEGIN PRIVATE KEY-----开头,以-----END PRIVATE KEY-----结尾
- 适合文本传输和存储
- Java中可以直接使用Bouncy Castle库的工具还原成私钥对象
DER格式:
- 不带BEGIN/END标记的纯二进制数据
- Java中
keyPair.getPrivateKey().getEncoded()默认生成的是PKCS#8格式的DER编码字节数组
PEM和DER的关系:
1 2 3
| -----BEGIN CERTIFICATE----- <Base64 编码的 DER 数据> -----END CERTIFICATE-----
|
PEM通过BEGIN和END标记包含DER数据的Base64编码。本质上PEM的内容就是DER数据,只是做了Base64编码以便于文本传输,两者可以相互转换。
Java中的证书和私钥处理
Java的CertificateFactory原生支持PEM格式的输入。当输入流中包含PEM头尾标记时,CertificateFactory会自动进行以下操作:
- 移除
-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----头尾标记
- 对Base64编码的数据进行解码
- 将解码后的DER字节解析为X.509证书对象
代码示例:Java中读取PEM格式私钥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.io.FileReader; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import java.nio.file.Files; import java.nio.file.Paths;
public class PemKeyReader {
public static PrivateKey readPrivateKeyFromPem(String pemFilePath) throws Exception { String content = new String(Files.readAllBytes(Paths.get(pemFilePath))); String privateKeyPEM = content .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); byte[] encoded = Base64.getDecoder().decode(privateKeyPEM); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } }
|
代码示例:Java中读取PEM格式证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.io.FileInputStream; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate;
public class PemCertReader {
public static X509Certificate readCertificateFromPem(String pemFilePath) throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); try (FileInputStream fis = new FileInputStream(pemFilePath)) { return (X509Certificate) certFactory.generateCertificate(fis); } } }
|
公钥
公钥与私钥配对使用,可以公开分发,用于加密数据或验证签名。
公钥的格式
- PKCS#1:
-----BEGIN RSA PUBLIC KEY-----
- PKCS#8(X.509 SubjectPublicKeyInfo):
-----BEGIN PUBLIC KEY-----
Java中获取公钥
1 2 3 4 5 6 7
| PublicKey publicKey = keyPair.getPublic(); byte[] encoded = publicKey.getEncoded();
X509Certificate certificate = ...; PublicKey publicKeyFromCert = certificate.getPublicKey();
|
证书链
在实际应用中,证书通常以链的形式存在:
1 2 3 4 5
| 终端证书(End Entity Certificate) ↓ 由 中间CA证书(Intermediate CA Certificate) ↓ 由 根CA证书(Root CA Certificate)签发
|
证书链验证流程
- 使用根CA证书的公钥验证中间CA证书的签名
- 使用中间CA证书的公钥验证终端证书的签名
- 验证证书的有效期、吊销状态等
Java中的证书链处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> certChain = new ArrayList<>(); certChain.add(endEntityCert); certChain.add(intermediateCert); certChain.add(rootCert);
CertPathValidator validator = CertPathValidator.getInstance("PKIX"); CertPath certPath = certFactory.generateCertPath(certChain);
PKIXParameters params = new PKIXParameters(trustAnchors); params.setRevocationEnabled(true);
validator.validate(certPath, params);
|
常见证书文件格式
| 扩展名 |
格式说明 |
编码方式 |
.pem |
Privacy Enhanced Mail,可包含证书或私钥 |
PEM(Base64) |
.cer / .crt |
证书文件 |
PEM或DER |
.der |
Distinguished Encoding Rules |
DER(二进制) |
.pfx / .p12 |
PKCS#12格式,可包含证书链和私钥 |
DER(二进制) |
.jks |
Java KeyStore,Java专用密钥库 |
专有格式 |
PKCS#12 (PFX) 文件
PKCS#12格式可以同时存储证书链和私钥,常用于证书导入导出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| KeyStore pkcs12Store = KeyStore.getInstance("PKCS12"); try (FileInputStream fis = new FileInputStream("certificate.p12")) { pkcs12Store.load(fis, "password".toCharArray()); }
KeyStore exportStore = KeyStore.getInstance("PKCS12"); exportStore.load(null, null); exportStore.setKeyEntry("alias", privateKey, "password".toCharArray(), certChain);
try (FileOutputStream fos = new FileOutputStream("export.p12")) { exportStore.store(fos, "password".toCharArray()); }
|
常用工具命令
OpenSSL常用命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| openssl genrsa -out private.key 2048
openssl pkcs8 -topk8 -inform PEM -in private.key -outform PEM -nocrypt -out private_pkcs8.key
openssl rsa -in private.key -pubout -out public.key
openssl req -x509 -new -nodes -key private.key -sha256 -days 365 -out cert.pem
openssl x509 -in cert.pem -text -noout
openssl x509 -in cert.pem -outform DER -out cert.der
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM
|
1 2 3 4 5 6 7 8 9 10 11
| keytool -genkeypair -alias mykey -keyalg RSA -keysize 2048 -keystore keystore.jks
keytool -exportcert -alias mykey -keystore keystore.jks -file cert.cer
keytool -importcert -alias ca -file ca.crt -keystore truststore.jks
keytool -list -v -keystore keystore.jks
|
安全建议
- 私钥保护:私钥必须严格保密,建议加密存储
- 证书有效期:定期更新证书,避免使用过期证书
- 证书吊销检查:验证证书时检查CRL或OCSP
- 密钥长度:RSA建议使用2048位以上,ECDSA建议使用256位以上
- 签名算法:避免使用MD5、SHA1等不安全算法,推荐使用SHA256或更高