Я пытаюсь связаться с веб-сервисом SOAP на основе формата rpc / encoding. Это внешний веб-сервис, поэтому я не могу его изменить. Он защищен через SSL, а аутентификация основана на сертификате X509.
Я создал неподписанные файлы сертификатов и ключей с помощью команды openssl:
openssl req -new -sha256 -newkey rsa:2048 -nodes -keyout keyfile.key -out unsigned.pem -subj '/O= <Company_name>/OU=WebApi/CN=<uid>'
, а затем получил подписанный файл pem (назовем его signature.pem). Я объединил контент BEGIN CERTIFICATE / END CERTIFICATE с контентом BEGIN PRIVATE KEY / END PRIVATE KEY в одном файле. Затем я использую этот файл (назовем его final.pem) для аутентификации.
Я получил простой пример кода, написанный на PHP, и он действительно работает:
$client = new SoapClient('https://www.url.to.external.web.service?wsdl',
array('local_cert' => "final.pem"));
$result = $client->Login();
Сейчас я борюсь с переносом этого кода на .NET или Java, но предпочитаю .NET. Я отмечу, что я сделал и какие результаты я получил.
.NET
Установлена библиотека xmlrpcnet, потому что я читал, что формат RPC / кодировки не поддерживается "из коробки" в .NET. Я создал интерфейс прокси:
[XmlRpcUrl("https://www.url.to.external.web.service?wsdl")]
[ServiceContract]
public interface WebServiceProxy : IXmlRpcProxy
{
[XmlRpcMethod("Login")]
[OperationContract(Action = "Login")]
LoginReturn Login();
}
Позже я извлекаю экземпляр X509Certificate2 из final.pem. Я делаю это так:
public X509Certificate2 GetCertificate(string fileName)
{
var pem = File.ReadAllText(fileName);
byte[] certBuffer = GetBytesFromPEM(pem, "CERTIFICATE");
return new X509Certificate2(certBuffer);
}
private byte[] GetBytesFromPEM(string pemString, string section)
{
var header = String.Format("-----BEGIN {0}-----", section);
var footer = String.Format("-----END {0}-----", section);
var start = pemString.IndexOf(header, StringComparison.Ordinal);
if (start < 0)
return null;
start += header.Length;
var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if (end < 0)
return null;
return Convert.FromBase64String(pemString.Substring(start, end));
}
Наконец, я создаю прокси-объект, прикрепляю сертификат и вызываю метод Login ():
var proxy = XmlRpcProxyGen.Create<WebServiceProxy>();
proxy.ClientCertificates.Add(_certificate);
proxy.Login();
но я получаю WebException с сообщением "не удалось создать безопасный канал ssl / tls".
Java:
К сожалению, формат RPC / кодировка не поддерживается в новейшей версии библиотеки осей, поэтому я скачал более старую (1.4). Затем сгенерированные прокси-классы из файла WSDL с помощью инструмента WSDL2Java. Позже я добавил final.pem в хранилище ключей, используя keytool:
keytool -import -noprompt -trustcacerts -alias somealias -file final.pem -keystore final.jks -storepass password
и загрузил его в код Java. Затем создал контекст SSL, создал прокси и вызвал метод login (), но, к сожалению, я получил исключение.
var certPath = "C:\path\to\final.jks"
var file = new File(certPath);
var keyStore = KeyStore.getInstance(file, "password".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, "abcABC123".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
Service service = new APILocator("https://www.url.to.external.web.service?wsdl", new QName("http://namespace.com/", "path"));
URL url = new URL("https://www.url.to.external.web.service?wsdl");
var proxy = new APIBindingStub(url, service);
proxy.login();
и ошибка, о которой я упоминал ранее:
javax.net.ssl.SSLHandshakeException: получено фатальное предупреждение: handshake_failure
Кто-нибудь ранее пытался получить доступ к такому веб-сервису из .NET / Java?