You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Java JKS密钥库筛选并提取叶子证书指定信息?

从JKS密钥库筛选叶子证书并提取指定信息的解决方案

核心思路

叶子证书的核心特征是证书的Subject(Owner)CN与Issuer CN不相同(自签名/根证书的两者CN一致,属于链签名证书,需排除)。以下提供两种可靠实现方案,规避awk或PKCS12转换带来的兼容性问题。


方案1:Java KeyTool + Bash脚本(适用于Linux/macOS)

利用Java自带的keytool工具提取证书信息,通过Shell脚本完成筛选和字段提取,无需额外依赖。

脚本代码

#!/bin/bash

# 配置密钥库路径和密码,默认cacerts密码为changeit
KEYSTORE_PATH="${JAVA_HOME:-/usr/lib/jvm/java-8-openjdk}/jre/lib/security/cacerts"
STORE_PASS="changeit"

# 提取所有证书别名(跳过KeyTool输出的头部信息)
ALIASES=$(keytool -list -keystore "$KEYSTORE_PATH" -storepass "$STORE_PASS" | \
          awk 'NR>3 {print substr($0, 1, index($0, " ")-1)}')

# 遍历每个别名,筛选并提取信息
for ALIAS in $ALIASES; do
    # 获取当前别名的详细证书信息
    CERT_INFO=$(keytool -list -v -alias "$ALIAS" -keystore "$KEYSTORE_PATH" -storepass "$STORE_PASS" 2>/dev/null)
    
    # 提取Owner CN和Issuer CN
    OWNER_CN=$(echo "$CERT_INFO" | grep -E "^Owner:" | sed -n 's/.*CN=\([^,]*\).*/\1/p')
    ISSUER_CN=$(echo "$CERT_INFO" | grep -E "^Issuer:" | sed -n 's/.*CN=\([^,]*\).*/\1/p')
    
    # 仅处理Owner与Issuer CN不同的叶子证书
    if [ "$OWNER_CN" != "$ISSUER_CN" ] && [ -n "$OWNER_CN" ]; then
        # 提取目标字段
        SIGNATURE_ALGO=$(echo "$CERT_INFO" | grep -E "^Signature algorithm name:" | awk -F': ' '{print $2}')
        PUBLIC_KEY_ALGO=$(echo "$CERT_INFO" | grep -E "^Subject Public Key Algorithm:" | awk -F': ' '{print $2}')
        VALID_FROM=$(echo "$CERT_INFO" | grep -E "^Valid from:" | awk -F': ' '{print $2}')
        VALID_UNTIL=$(echo "$CERT_INFO" | grep -E "^until:" | awk -F': ' '{print $2}')
        
        # 格式化输出
        echo "======================================"
        echo "别名名称: $ALIAS"
        echo "Owner CN: $OWNER_CN"
        echo "签名算法: $SIGNATURE_ALGO"
        echo "公钥算法: $PUBLIC_KEY_ALGO"
        echo "有效期起始: $VALID_FROM"
        echo "有效期结束: $VALID_UNTIL"
    fi
done

使用说明

  1. 保存脚本为extract-leaf-certs.sh,赋予执行权限:chmod +x extract-leaf-certs.sh
  2. 根据实际环境修改KEYSTORE_PATH和STORE_PASS(若使用自定义密钥库)
  3. 运行脚本:./extract-leaf-certs.sh

方案2:Java程序实现(跨平台,更稳定)

通过Java KeyStore API直接读取证书,精确解析X500主体信息,避免Shell脚本正则表达式的兼容性问题。

代码示例

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import javax.security.auth.x500.X500Principal;

public class LeafCertificateExtractor {
    public static void main(String[] args) {
        try {
            // 配置密钥库路径和密码
            String keystorePath = System.getProperty("java.home") + "/jre/lib/security/cacerts";
            String storePassword = "changeit";

            // 加载JKS密钥库
            KeyStore keyStore = KeyStore.getInstance("JKS");
            try (FileInputStream fis = new FileInputStream(keystorePath)) {
                keyStore.load(fis, storePassword.toCharArray());
            }

            // 遍历所有别名
            Enumeration<String> aliases = keyStore.aliases();
            while (aliases.hasMoreElements()) {
                String alias = aliases.nextElement();
                // 仅处理证书条目(排除密钥条目)
                if (keyStore.isCertificateEntry(alias)) {
                    X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
                    
                    // 提取Subject和Issuer的CN
                    String subjectCN = extractCN(cert.getSubjectX500Principal());
                    String issuerCN = extractCN(cert.getIssuerX500Principal());
                    
                    // 筛选叶子证书
                    if (!subjectCN.isEmpty() && !subjectCN.equals(issuerCN)) {
                        // 输出指定信息
                        System.out.println("======================================");
                        System.out.println("别名名称: " + alias);
                        System.out.println("Owner CN: " + subjectCN);
                        System.out.println("签名算法: " + cert.getSigAlgName());
                        System.out.println("公钥算法: " + cert.getPublicKey().getAlgorithm());
                        System.out.println("有效期起始: " + cert.getNotBefore());
                        System.out.println("有效期结束: " + cert.getNotAfter());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 从X500Principal中提取CN字段
    private static String extractCN(X500Principal principal) {
        String dn = principal.getName();
        String[] components = dn.split(",");
        for (String comp : components) {
            String trimmed = comp.trim();
            if (trimmed.startsWith("CN=")) {
                return trimmed.substring(3);
            }
        }
        return "";
    }
}

使用说明

  1. 保存代码为LeafCertificateExtractor.java
  2. 编译:javac LeafCertificateExtractor.java
  3. 运行:java LeafCertificateExtractor
  4. 若使用自定义密钥库,修改keystorePath和storePassword的值即可

常见问题说明

  • 为何之前的awk/PKCS12方法效果差?:KeyTool的输出格式可能随Java版本变化,awk正则容易失效;转换为PKCS12时可能丢失别名或证书链结构,导致筛选逻辑出错。
  • 别名带空格的处理:方案2的Java程序天然支持带空格的别名,方案1的脚本若遇到带空格的别名,需修改别名提取逻辑(如改用keytool -list -v的输出逐行解析)。

内容的提问来源于stack exchange,提问作者Ashar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:47:03