From ccf16c927d1b566c13bddbf2bb62e4773323761b Mon Sep 17 00:00:00 2001 From: Cheney Wong Date: Wed, 18 Jun 2025 18:49:13 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E4=B8=AA=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 38 +++++ .idea/.gitignore | 8 + .idea/encodings.xml | 7 + .idea/inspectionProfiles/Project_Default.xml | 5 + .idea/misc.xml | 14 ++ .idea/vcs.xml | 6 + pom.xml | 64 ++++++++ .../com/sunyard/security/CRLValidator.java | 145 ++++++++++++++++++ src/main/java/com/sunyard/security/Main.java | 40 +++++ test/sm2Root_67B599D6E4B0C757A77D4886.cer | Bin 0 -> 444 bytes test/sm2crl.crl | Bin 0 -> 537 bytes 11 files changed, 327 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 pom.xml create mode 100644 src/main/java/com/sunyard/security/CRLValidator.java create mode 100644 src/main/java/com/sunyard/security/Main.java create mode 100644 test/sm2Root_67B599D6E4B0C757A77D4886.cer create mode 100644 test/sm2crl.crl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..1c2fda5 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..a156f52 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..8d66637 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0c41917 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..9661ac7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..603139d --- /dev/null +++ b/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + com.sunyard.security + sm2-crl-checker + 1.0 + + + 8 + 8 + UTF-8 + + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + + com.sunyard.security.Main + + + + jar-with-dependencies + + + + + package + + single + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/sunyard/security/CRLValidator.java b/src/main/java/com/sunyard/security/CRLValidator.java new file mode 100644 index 0000000..f99ee2a --- /dev/null +++ b/src/main/java/com/sunyard/security/CRLValidator.java @@ -0,0 +1,145 @@ +package com.sunyard.security; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CRLConverter; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMParser; + +import java.io.FileInputStream; +import java.io.FileReader; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Date; + +public class CRLValidator { + + static { + // 添加Bouncy Castle作为安全提供者 + Security.addProvider(new BouncyCastleProvider()); + } + + + /** + * 读取PEM格式的根证书 + */ + /** + * 读取证书文件,支持PEM和DER格式 + */ + static X509Certificate readRootCertificate(String certPath) throws Exception { + try (FileInputStream fis = new FileInputStream(certPath)) { + // 首先尝试作为PEM文件读取 + if (certPath.toLowerCase().endsWith(".pem")) { + try (FileReader reader = new FileReader(certPath); + PEMParser pemParser = new PEMParser(reader)) { + + Object obj = pemParser.readObject(); + if (obj instanceof X509CertificateHolder) { + return new JcaX509CertificateConverter().getCertificate((X509CertificateHolder) obj); + } + throw new IllegalArgumentException("PEM文件不包含有效的X.509证书"); + } + } + // 否则作为DER格式二进制文件读取 + else { + // 方法1:使用Java标准CertificateFactory(需要BC提供者) + CertificateFactory certFactory = CertificateFactory.getInstance("X.509", "BC"); + return (X509Certificate) certFactory.generateCertificate(fis); + + // 方法2:使用Bouncy Castle的X509CertificateHolder + // fis.getChannel().position(0); // 重置流 + // X509CertificateHolder certificateHolder = new X509CertificateHolder(Streams.readAll(fis)); + // return new JcaX509CertificateConverter().getCertificate(certificateHolder); + } + } + } + + /** + * 读取CRL文件 + */ + public static X509CRL readCRLFile(String crlPath) throws Exception { + try (FileInputStream fis = new FileInputStream(crlPath)) { + // 对于PEM格式的CRL + if (crlPath.endsWith(".pem")) { + try (FileReader reader = new FileReader(crlPath); + PEMParser pemParser = new PEMParser(reader)) { + + Object obj = pemParser.readObject(); + if (obj instanceof X509CRLHolder) { + return new JcaX509CRLConverter().getCRL((X509CRLHolder) obj); + } + throw new IllegalArgumentException("文件不包含有效的CRL"); + } + } + // 对于DER格式的CRL + else if (crlPath.endsWith(".crl") || crlPath.endsWith(".der")) { + return new JcaX509CRLConverter().getCRL(new X509CRLHolder(fis)); + } else { + throw new IllegalArgumentException("不支持的CRL文件格式"); + } + } + } + + /** + * 验证CRL签名 + */ + public static void verifyCRLSignature(X509Certificate issuerCert, X509CRL crl) throws Exception { + try { + crl.verify(issuerCert.getPublicKey()); + System.out.println("CRL签名验证成功"); + } catch (Exception e) { + throw new SecurityException("CRL签名验证失败", e); + } + } + + /** + * 检查CRL是否有效(未过期) + */ + public static void checkCRLValidity(X509CRL crl) { + Date now = new Date(); + if (crl.getNextUpdate().before(now)) { + throw new SecurityException("CRL已过期"); + } + if (now.before(crl.getThisUpdate())) { + throw new SecurityException("CRL尚未生效"); + } + System.out.println("CRL在有效期内"); + } + + /** + * 打印CRL信息 + */ + public static void printCRLInfo(X509CRL crl) { + System.out.println("CRL信息:"); + System.out.println("颁发者: " + crl.getIssuerX500Principal()); + System.out.println("本次更新时间: " + crl.getThisUpdate()); + System.out.println("下次更新时间: " + crl.getNextUpdate()); + + // 获取CRL编号(如果有) + byte[] crlNumberExt = crl.getExtensionValue(Extension.cRLNumber.getId()); + if (crlNumberExt != null) { + try { + // 正确解析CRL编号扩展 + ASN1InputStream asn1In = new ASN1InputStream(crlNumberExt); + ASN1OctetString octetString = (ASN1OctetString) asn1In.readObject(); + + ASN1InputStream asn1In2 = new ASN1InputStream(octetString.getOctets()); + ASN1Integer crlNumber = ASN1Integer.getInstance(asn1In2.readObject()); + + System.out.println("CRL编号: " + crlNumber.getPositiveValue()); + } catch (Exception e) { + System.err.println("解析CRL编号时出错: " + e.getMessage()); + } + } + + System.out.println("吊销证书数量: " + crl.getRevokedCertificates().size()); + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/security/Main.java b/src/main/java/com/sunyard/security/Main.java new file mode 100644 index 0000000..b4c806e --- /dev/null +++ b/src/main/java/com/sunyard/security/Main.java @@ -0,0 +1,40 @@ +package com.sunyard.security; + +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; + +public class Main { + public static void main(String[] args) throws Exception { + + if ( args.length < 2 ) { + System.out.println("用法: java -jar sm2-crl-checker-1.0-jar-with-dependencies <根证书路径> "); + return; + } + + String rootCertPath = args[0]; + String crlFilePath = args[1]; + + + System.out.println("根证书路径: " + rootCertPath); + System.out.println("CRL文件路径: " + crlFilePath); + System.out.println("开始验证CRL..."); + + + // 1. 读取根证书 + X509Certificate rootCert = CRLValidator.readRootCertificate(rootCertPath); + + // 2. 读取CRL文件 + X509CRL crl = CRLValidator.readCRLFile(crlFilePath); + + // 3. 验证CRL签名 + CRLValidator.verifyCRLSignature(rootCert, crl); + + // 4. 检查CRL是否有效(未过期) + CRLValidator.checkCRLValidity(crl); + + // 5. 可选:打印CRL信息 + CRLValidator.printCRLInfo(crl); + + System.out.println("CRL验证结束!"); + } +} \ No newline at end of file diff --git a/test/sm2Root_67B599D6E4B0C757A77D4886.cer b/test/sm2Root_67B599D6E4B0C757A77D4886.cer new file mode 100644 index 0000000000000000000000000000000000000000..6f0d2cf9da185fcc61244d09ba6f4bf65bb60139 GIT binary patch literal 444 zcmXqLV%%ZS#2CGRnTe5!i6?#Q%xh0J91mY!>(OSw!^WZ2D04oPvAL9$!9dTD+klge zIh2J>n914CkPj#gVIvesE@PhcvJZwS!{vpne26E!OMy3WvhL#2vCME`E zQR2MDAg(!-OR{;92EuIYT5TTZY?&Bg4r|h7c4A=JZMRq1EX1ClrRV`uTe_Q&*_Rjh z81ulkItM-$Wx2(%2GIuc zK-bGEvq%_-HHc(R&A%ihsjGW*`Tu*T4!yj7jIo;2Ko+Eck420HyEHdQnxBOQ7>>ul zK_e^7!fL?G$oL;M9GN{C4BVI$8Cw1FRN3qt_ik8x;EB)uOg^RZ{foZr*U@q?<-Gmm rX=DLV$?IOTg|c&$9UkBBImDpr)pYMcj>~&5Bc@kk3mZT9p8E;_kAs5V literal 0 HcmV?d00001 diff --git a/test/sm2crl.crl b/test/sm2crl.crl new file mode 100644 index 0000000000000000000000000000000000000000..087b504289980c27c2fbaee00c99a5cd7c5b1ec9 GIT binary patch literal 537 zcmXqLViGlIV%*8Z$Y{XB#-Y_Hb3T-@xs;W`K+lldfRl|ml!Z;0$=T144=4@faPhGE z1vxu98}b|Qg80llY(f71A1Do6tXl(H;(}q(6zFnEX-VtY#_xM{%r@IYzWxS zS!MYLq8R9OV*>+YOH{>-5XE;cKiQz+pl|Qw3|DMvY-kF!7};dT1%(Fr28ke>lv$z- zA{s<8r{-T0lGN2by8QpWQ-@w&KgL+ixzK2d0jZ(e1oYj^Yfm;D4_{vE(FXC12n#bt ztTB5q7`QSisL1MV2#Ef?Je*^0l9SZhx>p4;+|ElITD)ez6lrJPznw{u;m({7SN7~K fve$8*ow6fj!g^D`!$GU`u7nrb-4V{!yqygIEj5wi literal 0 HcmV?d00001