cert-mgr使用

功能介绍

cert-mgr用于证书托管。

证书管理中,证书相关的私钥由单独的私钥表保存,还包含了证书表和请求表,生成的证书会保存在证书表中,子证书的请求会保存在请求表中。

证书签名算法目前支持:

  • SHA256WITHRSA

  • SHA256WITHECDSA

  • SM3WITHSM2

前置依赖

在使用本组件前,请确认系统环境已安装相关依赖软件,清单如下:

依赖软件 说明 备注
Java JDK[1.8]
Git 下载的安装包使用Git

如果您还未安装这些依赖,请参考附录

重要

FISCO-BCOS 2.0与3.0对比、JDK版本、WeBankBlockChain-Governance及其他子系统的 兼容版本说明

部署教程

目前支持从源码进行部署。

获取源码

通过git下载源码:

https://github.com/WeBankBlockchain/Governance-Cert.git

注解

进入目录:

cd Governance-Cert/cert-mgr

编译源码

方式一:如果服务器已安装Gradle

gradle build -x test

方式二:如果服务器未安装Gradle,使用gradlew编译

chmod +x ./gradlew && ./gradlew build -x test

导入jar包

cert-mgr编译之后在根目录下会生成dist文件夹,文件夹中包含cert-mgr.jar。可以将cert-mgr.jar导入到自己的项目中,例如拷贝到libs目录下,然后进行依赖配置。gradle推荐依赖配置如下,然后再对自己的项目进行编译。

repositories {
    mavenCentral()
    mavenLocal()
    maven {
        url "http://maven.aliyun.com/nexus/content/groups/public/"
    }
}
dependencies {
        compile 'org.springframework.boot:spring-boot-starter'
    	compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    	compile 'org.springframework.boot:spring-boot-starter-jta-atomikos'
    
    	testCompile('org.springframework.boot:spring-boot-starter-test') {
    		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    		//exclude group: 'junit', module: 'junit'
    	}
    	compile ('org.projectlombok:lombok:1.18.6')
    	testCompile ('org.projectlombok:lombok:1.18.6')
    	annotationProcessor 'org.projectlombok:lombok:1.18.6'
    	testAnnotationProcessor 'org.projectlombok:lombok:1.18.6'
    	testCompile 'junit:junit:4.12'
    	compile "org.apache.commons:commons-lang3:3.6"
    	compile "commons-io:commons-io:2.6"
    	compile 'com.lhalcyon:bip32:1.0.0'
    	compile 'org.web3j:core:3.4.0'
    	compile 'com.lambdaworks:scrypt:1.4.0'
    	compile 'commons-codec:commons-codec:1.9'
    	compile 'mysql:mysql-connector-java'
    	compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.60'
    	compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.60'
    	compile fileTree(dir:'libs',include:['*.jar'])
}

使用详解

依赖注入使用前需添加组件扫描,如下所示:

@SpringBootApplication
@ComponentScan(basePackages = { "com.webank.cert" })
public class CertTestApplication {
	public static void main(String[] args) {
		SpringApplication.run(CertTestApplication.class, args);
	}
}

配置

请参考下面的模板,配置application.properties。

## 证书存储db
spring.datasource.url=jdbc:mysql://[ip]:[port]/pkey_mgr?autoReconnect=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
spring.datasource.username=
spring.datasource.password=

## spring jpa config
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

建表

如果在上述配置中指定了spring.jpa.properties.hibernate.hbm2ddl.auto=update,则jpa会帮助用户自动建立数据表。

如果不希望自动建立数据表,请先关闭jpa建表开关:

spring.jpa.properties.hibernate.hbm2ddl.auto=validate

然后按下面方式手动建表,默认开启自动建表

1) 在数据源运行下述建表语句:

 -- Create syntax for TABLE 'cert_keys_info'
 drop table if exists cert_keys_info;
 CREATE TABLE `cert_keys_info` (
   `pk_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `user_id` varchar(255) NOT NULL,
   `key_alg` varchar(8) NOT NULL,
   `key_pem` longtext NOT NULL,
   `creat_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`pk_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 -- Create syntax for TABLE 'cert_info'
 drop table if exists cert_info;
 CREATE TABLE `cert_info` (
   `pk_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `user_id` varchar(255) NOT NULL,
   `subject_pub_key` longtext NOT NULL,
   `cert_content` longtext NOT NULL,
   `issuer_key_id` bigint(20) NOT NULL,
   `subject_key_id` bigint(20) NOT NULL,
   `parent_cert_id` bigint(20),
   `serial_number` varchar(255) NOT NULL,
   `issuer_org` varchar(255) NOT NULL,
   `issuer_cn` varchar(255) NOT NULL,
   `subject_org` varchar(255) NOT NULL,
   `subject_cn` varchar(255) NOT NULL,
   `is_ca_cert` int(4) NOT NULL,
   `creat_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`pk_id`),
   UNIQUE KEY (`parent_cert_id`,`serial_number`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 -- Create syntax for TABLE 'cert_request_info'
 drop table if exists cert_request_info;
 CREATE TABLE `cert_request_info` (
   `pk_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `parent_cert_id` bigint(20),
   `subject_key_id` bigint(20) NOT NULL,
   `user_id` varchar(255) NOT NULL,
   `cert_request_content` longtext NOT NULL,
   `subject_org` varchar(255) NOT NULL,
   `subject_cn` varchar(255) NOT NULL,
   `creat_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`pk_id`),
   UNIQUE KEY (`parent_cert_id`,`subject_key_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

接口说明

CertManagerService类是证书管理的统一入口,覆盖证书管理的全生命周期,包含如下功能:

  • createRootCert : 生成根证书

  • createRootCertByHexPriKey :私钥Hex格式作为入参生成证书

  • createCertRequest:生成请求

  • createCertRequestByHexPriKey :私钥Hex格式作为入参生成请求

  • createChildCert:生成子证书

  • resetCertificate:证书重置

  • queryCertList:证书列表查询

  • queryCertRequestList:请求列表查询

  • queryCertKeyList:证书私钥列表查询

  • queryCertInfoByCertId:根据id查询证书

  • queryCertRequestByCsrId:根据id证书请求

  • exportCertToFile:证书导出

参考Java doc

示例说明

下面介绍使用的例子

实例注入

在Java项目test中创建一个测试类,在测试类中注入依赖

建议通过Spring自动注入KeysManagerService服务,示例如下:

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class Example {
        @Autowired
        private CertManagerService certManagerService;
    }  

生成根证书

生成根证书,提供了多种封装接口,可按需使用

示例代码如下

    @Test
    public void testCreateRootCert() throws Exception {
        X500NameInfo issuer = X500NameInfo.builder()
                .commonName("chain")
                .organizationName("fisco-bcos")
                .organizationalUnitName("chain")
                .build();
        //用户id,如Bob
        String userId = "Bob";
        //有效期
        Date beginDate = new Date();
        Date endDate = new Date(beginDate.getTime() + CertConstants.DEFAULT_VALIDITY);
        //默认配置生成:采用RSA密钥对,也可调用其他封装接口,自定义密钥类型,支持pem和Hex格式
        CertVO cert = certManagerService.createRootCert(userId,issuer,beginDate,endDate);
        System.out.println("证书id: " + cert.getPkId());
        System.out.println("证书签发者: " + cert.getUserId());
        System.out.println("父证书id: " + cert.getPCertId());
        System.out.println("签发私钥id: " + cert.getIssuerKeyId());
        System.out.println("证书内容: " + cert.getCertContent());
    }

执行上述代码,可在控制台看到生成的证书内容,证书和相关联的签名私钥会保存在db中

证书列表查询

可以通过接口按一定条件查询已签发的证书列表

示例代码如下

    @Test
    public void testQueryCertList() throws Exception {
        List<CertVO> list = certManagerService.queryCertInfoList();
        list.forEach(certVO -> {
            System.out.println("证书id:" + certVO.getPkId() + "\t");
            System.out.println("证书id:" +certVO.getUserId() + "\t");
            System.out.println("父证书id: " + certVO.getPCertId() + "\t");
            System.out.println("签发私钥id: " + certVO.getIssuerKeyId() + "\t");
            System.out.println("---------");
        });
    }

执行上述代码,可在控制台看到所有签发的证书列表及相关信息

子证书csr生成

从上述步骤查询结果中,选择证书作为根证书,请求子证书。

示例代码如下

    @Test
    public void testCreateCertRequest() throws Exception {
        X500NameInfo subject = X500NameInfo.builder()
                .commonName("agancy")
                .organizationName("fisco-bcos")
                .organizationalUnitName("agancy")
                .build();
        String userId = "Alice";
        //自动生成密钥对,入参为:当前用户userId,父证书issuerCertId(可根据上述步骤查询得到),申请机构信息subject
        CertRequestVO csr = certManagerService.createCertRequest(userId, 1, subject);
        System.out.println("证书申请id:" + csr.getPkId());
        System.out.println("父证书签发者:" + csr.getPCertUserId());
        System.out.println("证书申请者:" + csr.getUserId());
        System.out.println("父证书id:" + csr.getPCertId());
        System.out.println("证书申请内容" + csr.getCertRequestContent());
    }

执行上述代码,证书申请和相关联的签名私钥会保存在db中,同时在db中将证书申请和其对应的父证书关联,便于后续子证书签发。

证书申请列表查询

可以通过接口按一定条件查询证书申请列表

示例代码如下

    @Test
    public void testQueryCertRequestList() throws Exception {
        List<CertRequestVO> list = certManagerService.queryCertRequestList();
        list.forEach(csr -> {
            System.out.println("证书申请id: " + csr.getPkId());
            System.out.println("父证书签发者: " + csr.getPCertUserId());
            System.out.println("证书申请者: " + csr.getUserId());
            System.out.println("父证书id: " + csr.getPCertId());
            System.out.println("---------");
        });
    }

执行上述代码,可在控制台看到所有证书申请的列表及相关信息

子证书签发

从上述步骤查询结果中,可选择证书申请签发子证书。

示例代码如下

    @Test
    public void testCreateChildCert() throws Exception {
        String userId = "Bob";
        //参数:签发者userId,证书申请id
        CertVO child = certManagerService.createChildCert(userId, 1);
        System.out.println("证书id: " + child.getPkId());
        System.out.println("证书签发者: " + child.getUserId());
        System.out.println("父证书id: " + child.getPCertId());
        System.out.println("签发私钥id: " + child.getIssuerKeyId());
        System.out.println("证书内容: " + child.getCertContent());
    } 

执行上述代码,可在控制台看到签发的子证书详情,可从第二步开始,继续签发下一级证书

证书重置

证书重置支持对证书有效期和用途配置进行重置,方法为exportCertToFile。 可通过第二步的查询证书列表,选择证书重置,如选择证书id为1的证书进行重置,示例代码如下:

    @Test
    public void testResetCertificate() throws Exception {
        String userId = "John";
        Date beginDate = new Date();
        Date endDate = new Date(beginDate.getTime() + CertConstants.DEFAULT_VALIDITY);
        CertVO cert = certManagerService.resetCertificate(userId,1,new KeyUsage(KeyUsage.dataEncipherment),beginDate,endDate);
        System.out.println("证书id: " + cert.getPkId());
        System.out.println("证书签发者: " + cert.getUserId());
        System.out.println("父证书id: " + cert.getPCertId());
        System.out.println("签发私钥id: " + cert.getIssuerKeyId());
        System.out.println("证书内容: " + cert.getCertContent());
    }

执行上述代码可以在控制台看到重置后的证书详情

证书导出

可通过第二步的查询证书列表,选择证书导出,如选择证书id为1的证书进行导出,示例代码如下:

    @Test
    public void testExportCertToFile() throws Exception {
        String filePath = "src/ca.crt";
        certManagerService.exportCertToFile(1L,filePath);
        System.out.println("导出完成,保存路径为:" + filePath);
    }

更多示例

参考证书管理接口示例
参考Java doc