SecurityHelper.prepareSignatureParams()OpenSAML调用期间发生java.lang.NullPointerException错误

SecurityHelper.prepareSignatureParams()OpenSAML调用期间发生java.lang.NullPointerException错误,java,single-sign-on,opensaml,Java,Single Sign On,Opensaml,我在使用OpenSAML支持准备SAML负载以完成与我正在使用的另一个服务的SSO事务时遇到了死胡同。我收到一个NullPointerException,它是在我使用SecurityHelper.prepareSignatureParams()例程后引发的。我有一个Stacktrace,但是附加上去会很难看 让我先说说我能做什么 为了学习这项技术并确保它能够工作,我成功地构建了一个SAML有效负载,使用证书和私钥对其进行签名,该证书和私钥存储在Java密钥存储文件中,我使用带有-genkeypa

我在使用OpenSAML支持准备SAML负载以完成与我正在使用的另一个服务的SSO事务时遇到了死胡同。我收到一个NullPointerException,它是在我使用SecurityHelper.prepareSignatureParams()例程后引发的。我有一个Stacktrace,但是附加上去会很难看

让我先说说我能做什么

为了学习这项技术并确保它能够工作,我成功地构建了一个SAML有效负载,使用证书和私钥对其进行签名,该证书和私钥存储在Java密钥存储文件中,我使用带有-genkeypair选项的Keytool程序在工作站上本地创建了该文件

据我所知,我的JKS文件包含一个自签名证书和一个私钥。我能够打开JKS文件,收集证书和私钥来构建签名证书。签名证书用于对我创建的SAML有效负载进行签名。如果您查看我将附加的代码示例,您将看到我是如何做到这一点的

什么不起作用

我想使用相同的SAML支持,使用我从GoDaddy收到的网站的可信证书来签署我的SAML有效负载。为此,我将受信任证书安装到Web服务器的密钥库中:'\Program Files\Java\jre1.8.0_102\lib\security\cacerts'。我知道cacerts文件是我们Web服务器的密钥库。我使用Keytool-importcert命令安装了可信证书。一个很大的区别是可信证书没有私钥。因此,当使用开放SAML支持准备签名证书时,我无法向凭证对象添加私钥(因为我没有私钥)

尝试对受信任证书执行上述操作时,我能够进入准备签名参数的部分(SecurityHelper.prepareSignatureParams())。这就是我得到空指针的地方

如果你能看看我正在使用的代码。我包括了从本地JKS文件读取的代码(成功地对有效负载进行签名),以及在尝试在服务器上使用受信任证书(两种情况下)时获取空指针异常的代码。这两种情况没有太大区别:

//  Signing process using OpenSAML

//  Get instance of an OpenSAML 'KeyStore' object...
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());

// Read KeyStore as File Input Stream.  This is either the local JKS 
// file or the server's cacerts file.
File ksFile = new File(keyStoreFileName);

//  Open an Input Stream with the Key Store File
FileInputStream ksfInputStream = new FileInputStream(ksFile);

// Load KeyStore.  The keyStorePassord is the password assigned to the keystore,  Usually 'changeit'
// before being changed.
keyStore.load(ksfInputStream, keyStorePassword);

// Close InputFileStream.  No longer needed.
ksfInputStream.close();


// Used to get Entry objects from the Key Store
KeyStore.PrivateKeyEntry pkEntry = null;
KeyStore.TrustedCertificateEntry tcEntry = null;

PrivateKey pk                   = null;
X509Certificate x509Certificate = null;

BasicX509Credential credential  = null;

//  The Java Key Store specific code...
// Get Key Entry From the Key Store.  CertificateAliasName identifies the
// Entry in the KeyStore.  KeyPassword is assigned to the Private Key.
pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(certificateAliasName,    new KeyStore.PasswordProtection(keyPassword));

//  Get the Private Key from the Entry
pk = pkEntry.getPrivateKey();

//  Get the Certificate from the Entry
x509Certificate = (X509Certificate) pkEntry.getCertificate();

//  Create the Credential.  Assign the X509Certificate and the Privatekey
credential = new BasicX509Credential();
credential.setEntityCertificate(x509Certificate);
credential.setPrivateKey(pk);


//  The Trusted Certificate specific code...
//  Accessing a Certificate that was issued from a trusted source - like GoDaddy.com
//
// Get Certificate Entry From the Key Store.  CertificateAliasName identifies the Entry in the KeyStore.
//  There is NO password as there is no Private Key associate with this Certificate

tcEntry = (TrustedCertificateEntry) keyStore.getEntry(certificateAliasName, null);

//  Get the Certificate from the Entry
x509Certificate = (X509Certificate) tcEntry.getTrustedCertificate();

//  Create the Credential.  There is no Provate Ley to assign into the Credential
credential = new BasicX509Credential();
credential.setEntityCertificate(x509Certificate);


//  Back to code that is not specific to either method...
//
//  Assign the X509Credential object into a Credential Object.  The BasicX509Credential object
// that has a Certificate and a Private Key OR just a Certificate added to it is now saved as a
// Cendential object.
Credential signingCredential = credential;


//  Use the OpenSAML builder to create a signature object.
Signature signingSignature = (Signature)    Configuration.getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME).build    Object(Signature.DEFAULT_ELEMENT_NAME);

//  Set the previously created signing credential
signingSignature.setSigningCredential(signingCredential);

// Get a Global Security Configuration object.
SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();

// KeyInfoGenerator.  Not sure what this is, but the example I am working from shows
// this being passed as null.
String keyInfoGeneratorProfile = "XMLSignature";

//  Prepare the Signature Parameters.
//
//  This works fine for the JKS version of the KeyStore, but gets a Null Pointer exception when I run to the cacerts file.
SecurityHelper.prepareSignatureParams(signingSignature, signingCredential, secConfig, keyInfoGeneratorProfile <or null>);

//  I need to set into the SigningSignature object the signing algorithm.  This is required when using the TrustedCertificate
signingSignature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);



//  This is my code that builds a SAML Response.  The SAML Payload contains data
// about the SSO session that I will be creating...
Response samlResponse = createSamlResponse.buildSamlResponseMessage();

//  Sign the Response using the Certificate that was created earlier
samlResponse.setSignature(signingSignature);



// Get the marshaller factory to marshall the SamlResponse
MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory();
Marshaller responseMarshaller = marshallerFactory.getMarshaller(samlResponse);

// Marshall the Response
Element responseElement = responseElement =     responseMarshaller.marshall(samlResponse);

//  Sign the Object...
Signer.signObject(signingSignature);
上述更改允许构建SAML负载并将其发送到连接端点

但是,我仍然没有建立到端点的SSO连接

以下是我看到的情况:

在构建SAML有效负载的过程中,特别是在SAML有效负载的签名被签名的过程中:

Signer.signObject(signature);
我从SAML收到一条错误消息:

ERROR: org.opensaml.xml.signature.Signer - An error occured computing the digital signature
堆栈跟踪(仅为结束部分):

我搜索了错误消息,但没有找到多少

我不理解错误消息的根源——提供了错误的密钥类型(null),OpenSAML似乎需要一个java.Security.PrivateKey

使用受信任证书时,我没有私钥,对吗?我如何才能提供私钥?在可信证书的情况下,我从密钥库读取可信证书(TrustedCertificateEntry)。TrustedCertificateEntry对象允许我访问证书,但没有获取私钥的方法(也不应该有)

但是,当我使用自签名证书执行签名操作时,我知道我确实拥有JKS文件(密钥库)中包含的证书(公钥)和私钥。我想这就是为什么当我从JKS文件中读取时,我能够读取一个私钥条目(KeyStore.PrivateKeyEntry),它具有访问公钥(证书)和私钥的方法

关于受信任证书案例,我遗漏了什么?OpenSAML支持似乎期望私钥能够计算签名

对于受信任证书,是否有方法将原始私钥打包到我的密钥存储中(与受信任证书一起)?我不确定这是否是正常的做法,甚至是可能的

希望能给我一些关于我在这里做什么的指导,谢谢

编辑(2017年1月26日)-2以提供更多详细信息

我将共享发送的SAML有效负载的一部分

对于自签名证书,我看到一个SignatureValue标记和一个X509Certificate标记。这两个标签的开始和结束都包含二进制数据

在受信任证书的情况下,我有一个空的签名值标记,看起来像:

<ds:SignatureValue/>

证书标记仍然存在,并且包含证书字节


因此,看看我在OpenSAML中看到的错误,更明显的是它无法使用可信证书中可用的数据计算签名。

好的,这是一个相当长的问题。据我所知,问题的根源是消息“抱歉,您为此操作提供了错误的密钥类型!您提供了null,但需要java.security.PrivateKey。”您正在尝试使用公钥对消息进行签名。这是不可能的。从逻辑上看,使用公钥签名并不能证明签名者的意图,因为它对每个人都是可用的

您需要做的是使用私钥进行签名。在您的情况下,您已经在计算机上生成了一个公钥和私钥,然后将CSR发送给CA,并收到CA签署的证书。 您应该使用本地计算机上的privat密钥对邮件进行签名,并将CA签名证书发送给收件人,以便他们可以使用它来确认您的签名


在中,我将解释如何从Java密钥库获取凭据,包括私钥

Stefan,谢谢你的回复。对不起,我确认
org.apache.xml.security.signature.XMLSignatureException: Sorry, you supplied the wrong key type for this operation! You supplied a null but a java.security.PrivateKey is needed.
at org.apache.xml.security.algorithms.implementations.SignatureBaseRSA.engineInitSign(SignatureBaseRSA.java:149)
at org.apache.xml.security.algorithms.implementations.SignatureBaseRSA.engineInitSign(SignatureBaseRSA.java:165)
at org.apache.xml.security.algorithms.SignatureAlgorithm.initSign(SignatureAlgorithm.java:238)
at org.apache.xml.security.signature.XMLSignature.sign(XMLSignature.java:631)
at org.opensaml.xml.signature.Signer.signObject(Signer.java:77)
<ds:SignatureValue/>