Я зашел в тупик, используя поддержку OpenSAML для подготовки полезной нагрузки SAML для выполнения транзакции единого входа с другой службой, с которой я работаю. Я получаю исключение NullPointerException, которое возникает после использования процедуры SecurityHelper.prepareSignatureParams(). У меня есть Stacktrace, но это было довольно уродливо, чтобы добавить.java.lang.NullPointerException Ошибка во время SecurityHelper.prepareSignatureParams() OpenSAML-вызов
Позвольте мне сказать, что я был в состоянии сделать ...
Для целей обучения технологии и убедиться, что она будет работать, я был в состоянии успешно построить полезную нагрузку SAML, подписать его с помощью Сертификат и закрытый ключ, который был сохранен в файле хранилища Java-файлов, который я создал локально на своей рабочей станции, используя программу Keytool с опцией -genkeypair.
Как я понимаю, мой файл JKS содержит самоподписанный сертификат и закрытый ключ. Я смог открыть файл JKS, собрать сертификат и закрытый ключ для создания сертификата подписи. Сертификат подписи использовался для подписи полезной нагрузки SAML, которую я создал. Вы увидите, как я это сделал, если вы посмотрите на примеры кода, которые я добавлю.
Что не работает ...
Я хочу использовать ту же самую поддержку SAML подписать мой SAML Payload используя доверенный сертификат, который у меня есть на моем сайте, что я получил от GoDaddy. Для этого я установил доверенный сертификат в хранилище ключей веб-сервера по адресу: '\ Program Files \ Java \ jre1.8.0_102 \ lib \ security \ cacerts'. Я понимаю, что файл cacerts является KeyStore для нашего веб-сервера. Я установил доверенный сертификат с помощью команды Keytool -importcert. Одно большое различие заключается в том, что у доверенного сертификата нет личного ключа. Поэтому при подготовке сертификата подписи с использованием поддержки Open SAML я не могу добавить закрытый ключ к объекту Credential (потому что у меня его нет).
При попытке вышеуказанного для доверенного сертификата я могу добраться до той части, где я готовлю подписи Parms (SecurityHelper.prepareSignatureParams()). Вот где я получаю нулевой указатель.
Если вы можете взглянуть на код, который я использую. Я включаю код (который успешно подписывает мою полезную нагрузку), который читает из локального файла JKS, а также код (который получает исключение Null Pointer), когда я пытаюсь использовать доверенный сертификат на сервере (оба случая). Там не сильно отличаются между этими двумя случаями:
// 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 Payload была смоделирована после примера OPENSAML, что я нашел здесь: https://narendrakadali.wordpress.com/2011/06/05/sign-assertion-using-opensaml/
Надеясь, что кто-то может показать мне ошибку мои пути или то, что мне не хватает.
Спасибо за любые предложения.
EDIT (01/26/2016)
я смог пройти мимо NULL указателя я получал при подготовке Signature Params (SecurityHelper.prepareSignatureParams()). Изменения кода включали обновление моего файла xmlsec.jar до версии 2.0.8 (xmlsec-2.0.8.jar), и я явно устанавливал алгоритм подписи для SHA256 при использовании доверенного сертификата (из GoDaddy). Смотрите мой пример кода для использования:
signingSignature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
Вышеуказанные изменения позволяет полезной SAML быть построены и отправлены в связи конечной точке.
Однако я все еще не устанавливаю соединение SSO с моей конечной точкой.
Вот что я вижу, что происходит:
Во время обработки, а полезная нагрузка SAML строится и, в частности, подпись SAML полезной нагрузки является подписываются:
Signer.signObject(signature);
Я получаю сообщение об ошибке от SAML:
ERROR: org.opensaml.xml.signature.Signer - An error occured computing the digital signature
трассировка стека (только окончание участка):
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)
Я искал сообщения об ошибках, но я не очень много придумываю.
Я не понимаю корень сообщения об ошибке - что был указан неправильный тип ключа (null), и что OpenSAML, похоже, ожидает java.Security.PrivateKey.
При использовании доверенного сертификата у меня нет закрытого ключа, правильно? Как я могу предоставить закрытый ключ? В случае доверенного сертификата я читаю доверенный сертификат (TrustedCertificateEntry) из KeyStore. Объект TrustedCertificateEntry позволяет мне получить доступ к сертификату, но нет способа получить закрытый ключ (также не должно быть).
Однако, когда я использую самоподписанный сертификат для выполнения операции подписи, я понимаю, что у меня есть как сертификат (открытый ключ), так и закрытый ключ, содержащийся в файле JKS (KeyStore). Я думаю, именно поэтому, когда я читал из файла JKS, я могу прочитать личную запись ключа (KeyStore.PrivateKeyEntry), которая имеет методы для доступа к открытому ключу (сертификату) и к закрытому ключу.
Что мне не хватает в деле доверенного сертификата? Кажется, что поддержка OpenSAML ожидает, что секретный ключ сможет вычислить подпись.
В случае доверенного сертификата есть ли способ упаковать оригинальный секретный ключ в мой магазин ключей (вместе с доверенным сертификатом)? Я не уверен, что это то, что обычно делается или даже возможно.
Надеюсь, некоторые указания относительно того, что я здесь делаю, пожалуйста!
EDIT (01/26/2017) - 2 для дополнительной информации.
Я поделюсь частью полезной нагрузки SAML, которая отправляется ...
В случае самоподписывающегося сертификат, я вижу SignatureValue тег и тег X509Certificate. Оба имеют двоичные данные, включенные в начало и конец тега.
В случае Доверенный сертификат, я получил пустую подпись Значение тега, который выглядит как:
<ds:SignatureValue/>
Сертификат тег по-прежнему присутствует и содержит байты сертификата.
Итак, глядя на ошибку, которую я вижу из OpenSAML, более очевидно, что она не может вычислить подпись, используя данные, доступные в доверенном сертификате.
Вопрос был не в обработке исключения Null Pointer, а в том, что я делал в пакете OpenSAML, который вызывал Null Pointer. Возможно, я мог бы повторить этот вопрос, чтобы быть более точным? Я знаю, что вопрос длительный, но, вероятно, что-то полезно для других, которые работают с поддержкой OpenSAML. – SBParks