WangBooth's Logbook

王布斯的网络日志

0%

Jar包GPG数字签名方法

需求背景

你有没有发现,从Maven仓库下载文件的时候,除了我们需要的jar包之外,还有个.asc文件,这个文件就是jar包的数字签名,咱可以通过这个数字签名来验证jar包是否确实是Oracle官方发布的包:

对于我们的客户,生产环境上运行的程序,有些是从公司 gitlab-ci 上编译出来的,有些是开发者个人电脑上编译出来的,还有的是客户自己改代码后编译出来的,程序出问题后,到底是谁的责任呢?比如,executor-proxy-provider.jar 可以直接接触到客户生产环境的数据,万一这个文件被人恶意篡改,把客户数据删掉了,咋整…

所以,咱也需要将公司对外的jar包做个签名,客户拿到jar包后,可以根据签名文件来确认是不是官方的包。

GPG签名原理

参考下这个链接:https://www.ruanyifeng.com/blog/2013/07/gpg.html

大体流程是:

  1. 使用GPG生成一对密钥,公钥和私钥
  2. 使用私钥对文件进行签名,并生成签名文件
  3. 将文件、该文件对应的签名文件 以及 公钥,同时对外公布
  4. 用户使用公钥和签名文件,对该文件进行签名验证

Maven使用GPG签名

参考下官方链接:https://maven.apache.org/plugins/maven-gpg-plugin/usage.html

大体流程是:

  1. 公司内部统一生成一对公钥和私钥文件:public.gpgprivate.gpg
    私钥private.gpg将用于对发布包进行签名,所以必须由专人保管且只有公司统一的专用服务器可以使用(如gitlab-ci使用的容器),私人电脑不得使用该私钥对发布包进行签名;
    公钥public.gpg将用于客户验证发布包的签名,需要与公钥本身的指纹(可通过gpg --fingerprint 生成)一同公开在公司官网;

  2. 在公司统一的专用打包服务器上通过gpg --import private.gpg来导入私钥;

  3. 在项目pom中添加maven-gpg-plugin插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <project>
    ...
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>3.0.1</version>
    <executions>
    <execution>
    <id>sign-artifacts</id>
    <phase>verify</phase>
    <goals>
    <goal>sign</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    ...
    </project>
  4. 在项目assembly中将maven-gpg-plugin插件生成的签名文件(.asc)包含进来

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <fileSets>
    ...
    <fileSet>
    <directory>${project.parent.basedir}/dubhe-server-provider/target</directory>
    <outputDirectory>${assembly.name}/jars</outputDirectory>
    <includes>
    <include>*.jar</include>
    <include>*.jar.asc</include>
    </includes>
    </fileSet>
    ...
    </fileSets>
  5. 使用 mvn verify进行打包,此时jar包对应的签名文件(.asc)会自动生成,与jar包在同一级目录下

  6. 客户(或者交付运维人员)在拿到部署包后,可通过以下步骤对jar包进行签名验证:
    7.1 从官网下载公钥文件public.gpg
    7.2 通过 gpg --import public.gpg 导入公钥
    7.3 通过 gpg --fingerprint 公钥ID 查看公钥指纹,确保该指纹与官网公布的公钥指纹一致
    7.4.通过 gpg --verify dubhe-server-provider-4.3.1.jar.sig dubhe-server-provider-4.3.1.jar来验证该文件签名是否是Good signature

  7. 开发测试平时打包,可按原先流程通过mvn package打包发布测试环境,不会进行签名,当然,这种包也不能对外发布。

gitlab-ci 配置

gitlab上的ci使用了k8s来做runner,目前公司内部通用的ci脚本一般是拉代码-打包-推送这个过程,我们需要在打包过程中加入GPG签名步骤,ci需要改动的点如下:

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
34
35
36
image: docker:git
services:
- docker:dind

variables:
PROJECT_NAME: dubhe-node
DOCKER_DRIVER: overlay

stages:
- build
- upload

build:
image: docker.dtwave-inc.com/library/maven:3.5.4-jdk-8-gunpg-alpine
variables:
MAVEN_OPTS: "-Dmaven.repo.local=/build-cache/.m2/repository"
stage: build
script:
- gpg -v --batch --import <(echo "$GPG_PRIVATE_KEY")
- "mvn clean verify -U -DskipTests -Dgpg.passphrase=$GPG_PRIVATE_KEY_PWD -DSHUXI_VERSION=$CI_COMMIT_TAG"
- cp dist/$PROJECT_NAME*$CI_COMMIT_TAG.tgz /build-cache
only:
- tags

upload:
image: docker.dtwave-inc.com/library/mc
stage: package
dependencies:
- build

script:
- mc mb dtwave-inc/shuxi-dev/backend/${PROJECT_NAME}/${CI_COMMIT_TAG}/
- mc cp /build-cache/${PROJECT_NAME}*${CI_COMMIT_TAG}.tgz dtwave-inc/shuxi-dev/backend/${PROJECT_NAME}/${CI_COMMIT_TAG}/
- rm -rf /build-cache/${PROJECT_NAME}*${CI_COMMIT_TAG}.tgz
only:
- tags

改动点1:

Build时使用maven:3.5.4-jdk-8-gunpg-alpine镜像
maven:3.5.4-jdk-8-gunpg-alpine 镜像比原先的 maven:3.5.4-jdk-8-alpine镜像多安装了一个gunpg,用于GPG签名。

改动点2:

Build时先通过gpg加载私钥
gpg -v --batch --import <(echo "$GPG_PRIVATE_KEY")
GPG私钥是保存在gitlab的环境变量GPG_PRIVATE_KEY中的,该变量只允许gitlab项目管理员或者分组管理员编辑。

改动点3:

使用mvn verify来打包
mvn clean verify -U -DskipTests -Dgpg.passphrase=$GPG_PRIVATE_KEY_PWD -DSHUXI_VERSION=$CI_COMMIT_TAG
原先使用mvn install ...改为mvn verify ...,同时传入变量gpg.passphrase=$GPG_PRIVATE_KEY_PWD,用于指定使用GPG私钥的密码,该密码是存放在gitlab的环境变量GPG_PRIVATE_KEY_PWD 中的,只允许gitlab项目管理员或者分组管理员编辑。

gitlab管理员配置

在项目配置或者分组配置下,设置变量:

GPG_PRIVATE_KEY 为GPG私钥,默认情况下,GPG私钥是以二进制方式存储的,可通过gpg --armor --output private-key.txt --export-secret-keys来生成ASCII码文本,然后将私钥文本填入该变量的值中。私钥务必保证其安全性,只有管理员可见。

GPG_PRIVATE_KEY_PWD为使用GPG私钥的密码,在生成GPG密钥对的时候设置的此密码,需将密码填入该变量中。为防止私钥被滥用,该密码也需保证安全性,只有管理员可见。