我已经通过复制我计划连接的IDP的509条目生成了testIdp.cer文件。然后我通过执行以下命令创建了JKS文件
keytool -importcert -alias adfssigning -keystore C:UsersuserDesktopsamlKeystore.jks -file C:UsersuserDesktoptestIdp.cer
当执行时,它要求输入一个我已经给出密码的密码。对于问题"信任此证书?[no]:",我给出了"y"作为输入。消息显示为"证书已添加到密钥库"。
然后我在securityContext.xml 中配置了以下详细信息
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/samlKeystore.jks"/>
<constructor-arg type="java.lang.String" value="mypassword"/>
<constructor-arg>
<map>
<entry key="adfssigning" value="mypassword"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="adfssigning"/>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="alias" value="adfssigning" />
<property name="signingKey" value="adfssigning"/>
</bean>
但当我运行应用程序时,当服务器启动和加载应用程序主页时,我会遇到以下两个异常。如果我遗漏了什么,有人能告诉我吗。
当我启动服务器时发生此异常
Caused by: org.opensaml.saml2.metadata.provider.FilterException: Signature trust establishment failed for metadata entry
at org.opensaml.saml2.metadata.provider.SignatureValidationFilter.verifySignature(SignatureValidationFilter.java:327)
at org.opensaml.saml2.metadata.provider.SignatureValidationFilter.processEntityGroup(SignatureValidationFilter.java:240)
at org.opensaml.saml2.metadata.provider.SignatureValidationFilter.doFilter(SignatureValidationFilter.java:158)
at org.opensaml.saml2.metadata.provider.AbstractMetadataProvider.filterMetadata(AbstractMetadataProvider.java:493)
at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.processNonExpiredMetadata(AbstractReloadingMetadataProvider.java:395)
当我运行应用程序的主页时,会出现此异常
java.lang.UnsupportedOperationException: trusted certificate entries are not password-protected
at java.security.KeyStoreSpi.engineGetEntry(Unknown Source)
at java.security.KeyStore.getEntry(Unknown Source)
at org.opensaml.xml.security.credential.KeyStoreCredentialResolver.resolveFromSource(KeyStoreCredentialResolver.java:132)
您的.cer
证书只包含公钥,不能为公钥定义<entry key="adfssigning" value="mypassword"/>
;它只能用于私人用途。只需取出adfssigning
条目,并确保包含一个私钥,就像SpringSAML示例应用程序中一样。
SAML密钥库可以包含两种基本类型的密钥——公钥和私钥(以及它们的证书)。每个密钥都有一个用来引用它的别名。密钥库本身可以由密码保护(在第二个构造函数参数中提供),每个私钥也可以由一个额外的密码保护(这些密码在alias->password映射中的构造函数的第三个参数中定义)。导入到密钥库的公钥(就像上面的命令一样)不能在这个映射中定义。它们将在导入后自动可用,无需额外声明。为了使Spring SAML工作,密钥库必须至少包含一个私钥(示例应用程序包含别名为apollo的私钥),并且其别名需要在构造函数的第三个参数中提供。
上面的示例失败了,因为您导入了一个公钥,但将其包含在只能用于私钥的映射中。
Vladimir正确回答了为什么发生错误的问题。在我的回答中,我想向展示如何将证书导入密钥库以解决该问题:
您必须导入证书和私钥,这是keytool无法直接完成的。
详细描述的解决方案如下:https://stackoverflow.com/a/8224863/1909531
摘录如下:
openssl pkcs12 -export -in server.crt -inkey server.key
-out server.p12 -name [some-alias]
-CAfile ca.crt -caname root
keytool -importkeystore
-deststorepass [changeit] -destkeypass [changeit] -destkeystore server.keystore
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass some-password
-alias [some-alias]
在尝试了所有操作,多次删除和重新创建jks文件后,都没有成功。然后我碰巧滚动了一下这个页面,找到了Pankaj的答案,就这样。我在属性文件中使用了错误的别名。正如Pankaj正确提到的,我们应该在属性文件中使用PrivateKeyEntry的别名,而不是trustedCertEntry。
步骤如下:
-
使用下面的命令创建一个JKS文件。这也会将PrivateKeyEntry添加到JKS文件中。
keytool-genkeypair-alias some_alias-keypass some_password-keyalg RSA-keysize 2048-keystore samlKeystore.jks-validity 1825
-
将IDP证书导入其中。这会将trustedCertEntry添加到文件中。
keytool-importcert-alias some_other_alias-file file_name-keystore samlKeystore.jks
-
修改您的属性文件以使用您在创建JKS文件时提供的别名,即PrivateKeyEntry 的别名
sso.keystore.privatekey.alias=some_aliassso.keystore.default.certificate.alias=some_alias
-
重新启动服务器。
我一开始犯的另一个错误是,我从IDP获得了一个metadata.xml文件,并且必须创建一个cert文件。我忘了加上"-----开始证书-----";以及"-----结束证书-----";在证书前后,这产生了一个无效的证书,让我很难过。证书应采用以下的格式
-----BEGIN CERTIFICATE-----
BLAH-BLAH-BLAH-CERT
-----END CERTIFICATE-----
当您的密钥库中没有私钥时,也会发生此错误。SAML使用私钥生成用于与IDP通信的服务提供商元数据。只需像这样在密钥库中添加一个:
keytool -genkey -v -keystore some_key_store.jks -alias some_alias -keyalg RSA -keysize 2048 -validity 36500
填写问题并将有效期设置为适当的天数。(在我的例子中,它的有效期为100年)请记住添加来自IDP的公共证书。那么你应该准备好出发了。
对于那些在java配置中寻找答案的人,请注释掉这行passwords.put("mykeyalias","mystorepass")。。。。如下面的代码片段所示。
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:saml-keystore.jks");
Map<String, String> passwords = new HashMap<>();
// passwords.put("mykeyalias", "mystorepass");
return new JKSKeyManager(storeFile, "mystorepass", passwords, "mykeyalias");
}
在以上所有解决方案之后,如果问题仍然存在,可能值得检查您在使用密钥库中的证书时是否使用了正确的别名。在我的情况下,由于有多个证书条目,我输入了不正确的别名(实际上是条目类型为trustedCertEntry的别名,这导致了问题。而您应该使用条目类型为PrivateKeyEntry的别名为此,只需使用检查现有证书
keytool -list -keystore "$JKS_CERT_PATH" -storepass "$JKS_CERT_PASSPHRASE" -noprompt -v
使用openssl命令获取公共证书:
openssl s_client -showcerts -connect iam-sso.google.net:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem
将其导入密钥库:
keytool -import -alias "new-qet-alias" -keystore /usr/share/tomcat8/webapps/ROOT/WEB-INF/classes/saml/samlKeystore.jks -file mycertfile.pem