首个版本

This commit is contained in:
Cheney Wong 2025-06-18 18:49:13 +08:00
commit ccf16c927d
11 changed files with 327 additions and 0 deletions

38
.gitignore vendored Normal file
View File

@ -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

8
.idea/.gitignore vendored Normal file
View File

@ -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

7
.idea/encodings.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,5 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
</profile>
</component>

14
.idea/misc.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

64
pom.xml Normal file
View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sunyard.security</groupId>
<artifactId>sm2-crl-checker</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version> <!-- 使用最新版本 -->
</dependency>
</dependencies>
<build>
<plugins>
<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 打包所有依赖的插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.sunyard.security.Main</mainClass> <!-- 替换为你的主类 -->
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -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());
}
}

View File

@ -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 <根证书路径> <CRL文件路径>");
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验证结束");
}
}

Binary file not shown.

BIN
test/sm2crl.crl Normal file

Binary file not shown.