前言
在使用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级别的证书信任。