maven添加信任证书

Dec 11, 2016


前言

在使用maven的过程,很多公司都会搭建自己内部的maven仓库,所以我们经常需要配置maven的私有仓库地址,以便我们可以使用公司的私有仓库,一般来说,如果使用http的maven私有库域名,那也没什么问题,但是如果使用https的私有库域名,那就不行了,因为maven是使用jdk提供的http客户端连接进行更新本地仓库的,https的连接由于证书不受jdk信任,因此会导致请求失败,因此也无法连接私有仓库,证书不受信任的时候,打包过程会出现类似如下警告:

[WARNING] Could not transfer metadata org.leapframework:leap:0.4.0b-SNAPSHOT/maven-metadata.xml from/to bingo-maven-repository-hosted ($bingo.maven$): sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

解决这个问题的方案有两种:

  • 在jdk中添加信任证书
  • 在maven中添加信任证书

如果在jdk中添加证书,实际上是会让所有使用jdk的应用都信任这个证书,因此我们一般不太建议使用这种方式,不过相对配置简单。

第二种方式相对来说影响范围小一些,比较推荐,但是配置起来相对麻烦。

现在我们以maven 3.3.9为例,分别说说这两种配置方式,本文使用的操作系统是CentOS 7,其他操作系统同理。

注:在本文中,使用的是品高的私有maven库作为测试的,因为这个私有库并不是对外开放的,因此所有涉及私有库真实地址的url全部使用$bingo.maven$替换了。

配置私有仓库

在开始配置信任证书之前,我们需要先配置一个使用https的私有仓库。

先修改maven的全局配置:

$> vim ~/.m2/settings.xml

添加如下仓库配置:

<repository>
        <id>bingo-maven-repository-hosted</id>
        <name>Bingo Hosted Maven Repository</name>
        <url>https://$bingo.maven$</url>
        <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
                <checksumPolicy>warn</checksumPolicy>
        </releases>
        <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>warn</checksumPolicy>
        </snapshots>
</repository>
<repository>
        <id>bingo-maven-repository-public</id>
        <name>Bingo Maven Public Repository</name>
        <url>https://$bingo.maven$</url>
        <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
                <checksumPolicy>warn</checksumPolicy>
        </releases>
        <snapshots>
                <enabled>false</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>warn</checksumPolicy>
        </snapshots>
        <layout>default</layout>
</repository>

配置完成之后使用打包命令,如果打包的工程有依赖包是在这个私有仓库中的话,那么就会出现前面说的警告了。

解决方案

要解决这个问题,有两种方案,我们分别来介绍。

首先我们需要下载客户端证书,这个直接在浏览器即可下载。

假设我们下载好的证书命名为outter.$bingo.maven$.cer

在jdk中添加证书信任

在jdk中添加证书信任,其实就是使得所有使用这个jdk的应用都信任这个证书,实际上是定制jdk。

${JAVA_HOME}/jre/lib/security目录下,有一个cacerts文件,这是jdk的证书库,我们只需要把需要信任的证书添加到这里即可。

现在我们使用jdk提供的keytool工具将证书导入jdk的证书库中:

$> keytool -import -alias ${cername} -keystore  ${keystore} -file  ${cerFile} -trustcacerts -storepass changeit

这个指令中有三个变量是需要我们根据实际情况填的:

cername : 证书别名,用来标识不同的证书,这里我们用outer.$bingo.maven$.cer
keystore: 证书库,用来存储信任证书,这里我们导入jdk的证书一般是${JAVA_HOME}/jre/lib/security/cacerts
cerFile: 要信任的证书,也就是我们前面下载好的证书文件outer.$bingo.maven$.cer

执行这个指令之后,输入yes确认导入即可。

现在我们再执行maven指令,就会发现没有证书不信任的警告了,私有仓库的依赖包也能自动下载了。

删除证书

虽然可以在jdk中添加信任证书,但是存在两个问题:

  • 对于所有使用这个jdk的应用都信任这个这个证书,这可能存在安全漏洞
  • 每次重新安装jdk都需要重新导入证书,使用上并不方便

其实maven是支持指定证书库的,因此我们可以做好一个特定的证书库,并且使用maven的参数指定使用我们的证书库,这样就能保证不会影响使用jdk的其他应用了,另外,maven可以打成绿色包给其他人使用和备份。

现在我们先删除刚刚在jdk中添加的证书,先确认一下证书是否存在:

$> keytool -list -keystore ./jre/lib/security/cacerts -alias outer.$bingo.maven$.cer

输入jdk证书库默认密码changeit:

$> keytool -list -keystore ./jre/lib/security/cacerts -alias outer.$bingo.maven$.cer 
Enter keystore password:  
outer.$bingo.maven$.cer, Dec 13, 2016, trustedCertEntry, 
Certificate fingerprint (SHA1): 97:37:CA:FA:95:41:0B:BD:18:09:FA:F6:4F:CD:32:F7:EC:DB:60:19

可以看到证书了,现在把证书删掉:

$> keytool -delete -keystore ./jre/lib/security/cacerts -alias outer.$bingo.maven$.cer

现在我们再查看证书:

$> keytool -list -keystore ./jre/lib/security/cacerts -alias outer.$bingo.maven$.cer       
Enter keystore password:  
keytool error: java.lang.Exception: Alias <outer.$bingo.maven$.cer> does not exist

发现证书已经不存在了。

重新使用maven打包就会发现,证书不信任的警告重新出现了。

给maven单独指定信任证书

给maven单独指定信任证书,需要以下三步:

  • 创建一个新的证书库
  • 把maven需要信任的证书全部导入创建的证书库中
  • 在maven的启动脚本中添加指定信任证书库

创建新的证书库

我们先到maven的根目录下创建security文件夹,用来存放我们的证书和证书库:

$> mkdir ${MAVEN_HOME}/security
$> cd ${MAVEN_HOME}/security

现在我们开始创建证书:

使用如下命令:

keytool -genkeypair -alias ${alias} -keyalg RSA -validity 365 -keystore ${jksName} -storepass ${passowrd}

这条命令会在生成keystore后接着生成一个密钥对。

这里有三个变量:

alias: 密钥对别名,可以自己定义,这里用maven.certificatekey
jksName: 证书库文件名,一般是jks后缀,这里用maven.keystore.jks
passowrd: 证书库密码,这里用changeit方便记忆

RSA是非对称密钥算法,也可以改为keytool支持的其他密钥算法,365代表的是证书的有效期,可以自己指定。

$> keytool -genkeypair -alias maven.certificatekey -keyalg RSA -validity 365 -keystore maven.keystore.jks -storepass changeit
What is your first and last name?
  [Unknown]:  kael
What is the name of your organizational unit?
  [Unknown]:  bingosoft
What is the name of your organization?
  [Unknown]:  bingosoft
What is the name of your City or Locality?
  [Unknown]:  guangzhou
What is the name of your State or Province?
  [Unknown]:  guangdong
What is the two-letter country code for this unit?
  [Unknown]:  gz
Is CN=kael, OU=bingosoft, O=bingosoft, L=guangzhou, ST=guangdong, C=gz correct?
  [no]:  yes

Enter key password for <maven.certificatekey>
        (RETURN if same as keystore password):  

这里执行后按照要求输入信息,最后设置密码的时候直接回车表示和密钥对密码一致(changeit)。

这样我们就生成好证书库了:

$> ls
maven.keystore.jks

现在将我们要信任的证书添加到这个证书库中:

$> keytool -import -alias ${cername} -keystore  ${keystore} -file  ${cerFile} -trustcacerts -storepass changeit

这里导入的方式和前面导入jdk证书一致:

$> keytool -import -alias outer.$bingo.maven$.cer.cer -keystore  maven.keystore.jks -file  ~/Downloads/outter.$bingo.maven$.cer -trustcacerts -storepass changeit

回车后输入yes确认导入。

现在我们的证书库就做好了。

给maven指定证书库

maven指令在执行的时候,会先执行~/.mavenrc脚本,因此我们可以在这个脚本中添加一些参数来指定证书库:

MAVEN_OPTS="$MAVEN_OPTS -Xmx512m \
    -Djavax.net.ssl.trustStore=$MAVEN_HOME/security/maven.keystore.jks \
    -Djavax.net.ssl.trustStorePassword=changeit  \
    -Djavax.net.ssl.trustStoreType=jks"

这里要注意,如果我们没有在系统环境变量中配置MAVEN_HOME,那这里的javax.net.ssl.trustStore参数需要使用maven的全路径名,如果配置了就不用了。

添加了这个脚本之后,我们就可以发现,使用maven打包的时候,我们刚刚导入证书的私有库已经没有警告了,但是其他使用https的库全部出现警告了,这是因为我们指定的库覆盖了默认的jdk证书库。为了解决这个问题,我们可以复制一份jdk的证书库,并在证书库中导入我们额外的证书即可。

$> mv ${JAVA_HOME}/jre/lib/security/cacerts ${MAVEN_HOME}/security/maven.keystore.jks
$> keytool -import -alias outer.$bingo.maven$.cer -keystore  ${MAVEN_HOME}/security/maven.keystore.jks -file  ~/Downloads/outter.$bingo.maven$.cer -trustcacerts -storepass changeit

使用了jdk的证书库副本,并添加了我们自己的信任证书,这个时候在使用maven打包就不会再有警告了。

实际上,maven支持两种类型的证书,除了我们前面使用的jks之外,还有pkcs12证书。maven指定证书的参数一共有六个:

javax.net.ssl.trustStore
信任证书库路径

javax.net.ssl.trustStoreType
信任证书类型,默认是jks,也可以指定为pkcs12

javax.net.ssl.trustStorePassword
证书库密码

javax.net.ssl.keyStore
用户私有证书库路径

javax.net.ssl.keyStoreType
用户私有证书库类型

javax.net.ssl.keyStorePassword
用户私有证书库密码

总结

虽然直接给jdk添加证书是一个非常简单方便的做法,但是我们 应该清楚其中背后的含义,那就是会使得所有使用这个jdk的应用都信任了这个证书,这是会造成安全隐患的,因此为了减小证书信任的影响范围,建议尽量使用maven级别的证书信任。