Я так понял что pkcs7 хранит в себе сертификат, и просто этим сертификатом проверяешь эту подпись(то есть себя ж), возможно я что-то я понял не так.
Вот код:
public boolean verify(byte[] data) {
try {
CMSSignedData cmsSignedData = new CMSSignedData(data);
// получаем список сертификатов, которые содержатся в файле
Store<X509CertificateHolder> certs = cmsSignedData.getCertificates();
// p7s файл может содержать данные, которые были подписаны, то есть ваш pdf файл
byte[] content = (byte[]) cmsSignedData.getSignedContent().getContent();
// проходимся по списку подписей, обычно одна подпись, но стандарт поддерживает множество подписей
for (SignerInformation si : cmsSignedData.getSignerInfos()) {
// список всех атрибутов, подписанных вместе с файлом
AttributeTable signedAttributes = si.getSignedAttributes();
// атрибут с именем
Attribute attrSurname = signedAttributes.get(new ASN1ObjectIdentifier("2.5.4.4"));
// атрибут с именем 2
Attribute attrGivenName = signedAttributes.get(new ASN1ObjectIdentifier("2.5.4.42"));
// атрибут с датой подписи
Attribute attrSigningTime = signedAttributes.get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.5"));
// выводим дату подписи
System.out.println(attrSigningTime.getAttributeValues()[0].toString());
// возможно некоторые атрибуты не были частью подписи, формат поддерживает и такие
//AttributeTable unsignedAttributes = si.getUnsignedAttributes();
// получаем идентификатор сертификата, которым подписаны данные
SignerId signerId = si.getSID();
// ищем сертификат в коллекции по идентификатору
Collection certCollection = certs.getMatches(signerId);
// выбираем первый сертификат, здесь не должно быть больше одного
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
// создаем Verifier на основании сертификата, Verifier использует публичный ключ сертификата
SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().build(certHolder);
// проверяем подпись
return si.verify(verifier);
}
} catch (CertificateException | OperatorCreationException | CMSException e) {
logger.error(e);
}
return false;
}
byte[] data = Base64.getUrlDecoder().decode(a);
Привет. Помогите пожалуйста с получением маркера доступа к данным есиа. Авторизационный код получил. Нужно сделать post запрос на тестовую среду для получения токеда доступа. Вот мой код:
public void getNewToken(String code, String state, String scope, Token token, boolean byRefresh)
throws OAuthSystemException, OAuthProblemException, JsonException {
if (state.equals(this.state.getStateAuthorize())) {
String timestamp = timestampUtil.generateTimestamp();
String clientSecret = certificateUtil.getUrlSafeSign(
scope + timestamp +
rpguConfig.getClientID() + this.state.generateStateToken()
);
GrantType grantType;
if (byRefresh) {
grantType = GrantType.REFRESH_TOKEN;
} else {
grantType = GrantType.AUTHORIZATION_CODE;
}
OAuthClientRequest requestToEsia = OAuthClientRequest
.tokenLocation(esiaConfig.getEsiaTokenPoint())
.setClientId(rpguConfig.getClientID())
.setRedirectURI(rpguConfig.getRedirectUrl())
.setScope(scope)
.setCode(code)
.setGrantType(grantType)
.setClientSecret(clientSecret)
.setParameter("state", this.state.getStateToken())
.setParameter("token_type", rpguConfig.getTokenType())
.setParameter("timestamp", timestamp)
.setParameter("access_type", rpguConfig.getAccessType())
.buildBodyMessage();
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
//OAuthAccessTokenResponse response = oAuthClient.accessToken(requestToEsia);
OAuthJSONAccessTokenResponse oauthResponse = oAuthClient.accessToken
(requestToEsia, OAuth.HttpMethod.POST, OAuthJSONAccessTokenResponse.class);
if (!inspectorToken.checkToken(oauthResponse.getAccessToken())) {
parserTokenData.parseTokenData(oauthResponse, token); //парсим данные токена
}
}
}
Но при выполнении метода по получению токена – oAuthClient.accessToken выбрасывает ошибку такую:
org.apache.oltu.oauth2.common.exception.OAuthSystemException: java.net.ConnectException: Connection timed out: connect
at org.apache.oltu.oauth2.client.URLConnectionClient.execute(URLConnectionClient.java:108)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:65)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:55)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:71)
at controller.server.esia.AuthorizationServer.getNewToken(AuthorizationServer.java:140)
at service.token.TokenService.getNewTokenByScope(TokenService.java:48)
at web.controller.EsiaController.getAccessCode(EsiaController.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:220)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748) ....
Если пройти по трейсу и посмотреть вглубь всех вызывающих методов то ошибка вываливается при конекте(получение стрима) объекта HttpСlient. В документации сказано что нужно отправлять запросы на: https://esia-portal1.test.gosuslugi.ru/aas/oauth2/te
В чем может быть ошибка? Почему к нему нет доступа?
Возможно кому-то приходилось проводить интеграцию с данным сервисом ЕСИА
. Вопрос возникает в формировании сигнатуры к запросу, т.к. используется протокол SSO SAML
: после формирования запроса на аутентификацию требуется поместить в GET-запрос
ряд параметров: SamlAuth
, AlgSig
, relayState
и Signature
.
Вопрос 1: Что такое
relayState
и как оно формируется?
задан 24 июл 2015 в 6:49
16 бронзовых знаков
Рекомендую копать в сторону spring-saml, у меня оно отлично привинтилось.
Приложу свой конфиг saml-security.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/user/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/user/saml/authnrequestresponselistener/**"
filters="samlWebSSOProcessingFilter"/>
</security:filter-chain-map>
</bean>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/user/login/esia/"/>
</bean>
<!-- Register authentication manager with SAML provider -->
<!--<security:authentication-manager alias="samlAuthenticationManager">-->
<!--<security:authentication-provider ref="samlAuthenticationProvider"/>-->
<!--</security:authentication-manager>-->
<bean id="samlAuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<!-- OPTIONAL property: can be used to store/load user data after login -->
<!--<property name="userDetails" ref="samlUserDetailsService" />-->
</bean>
</list>
</constructor-arg>
</bean>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger">
<property name="logMessages" value="true"/>
</bean>
<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:esia/esia.jks"/>
<constructor-arg type="java.lang.String" value="******"/>
<constructor-arg>
<map>
<entry key="entryKey" value="******"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="someValue"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
<property name="binding" value="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"/>
</bean>
</property>
</bean>
<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator"/>
</constructor-arg>
</bean>
<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<!-- Do no forget to call iniitalize method on providers -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">classpath:esia/esia_edu_metadata.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">classpath:esia/esia_prod_metadata.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">classpath:esia/shibboleth.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
</list>
</constructor-arg>
<!-- Set provider edu or prod -->
<property name="hostedSPName" value="hostname.ru"/>
</bean>
<!-- Provider of default SAML Context -->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<constructor-arg>
<value type="java.lang.String">/user/saml/authnrequestresponselistener</value>
</constructor-arg>
<property name="authenticationManager" ref="samlAuthenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory"
factory-method="getEngine"/>
<!-- XML parser pool needed for OpenSAML parsing -->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
ответ дан 24 июл 2015 в 9:09
relayState – можно не указывать.
Signature в ЕСИА надо указать ‘-‘ примерно так на Ruby:
params[:SAMLRequest]= Base64.strict_encode64(Zlib::Deflate.deflate(signed_request, 9)[2..-5])
params[:Signature] = '-'
params[:SigAlg] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
Подписание дело тонкое: ищите как подписываются xml, куда вставляются блоки, ds:Signature, каноникализация и пр.
Можете посмотреть информацию здесь: http://esia.pro
ответ дан 27 июл 2015 в 10:56
Исходя из нашего опыта работы могу добавить:
relayState – это уникальный идентификатор сессии в интегрируемом приложении, в результате авторизации ЕСИА вернет его обратно и приложение сможет его связать с запросом
Порядок формировании подписи:
1) выполняется deflate строки запроса – получается byte
2) выполняется подпись полученного на шаге 1 – получается byte
3) выполняется кодирование в base64 полученного на шаге 2
В дополнение могу порекомендовать посмотреть не только готовое решение, упомянутое Samoilenko Yuri
, но и ESIA-Bridge
.
ответ дан 3 фев 2016 в 12:52
2 бронзовых знака
We would like to use third party cookies and scripts to improve the functionality of this website.
More info
Аутентификация через госуслуги (ЕСИА)
https://<ESIA_HOST>/aas/oauth2/ac?client_id={0}&client_secret={1}
&redirect_uri={2}&scope={3}&response_type=code&state={4}
×tamp={5}&access_type={6}
Расчет секрета приложения (client_secret)
Секрет приложения расчитывается как подпись конкатенации параметров scope, timestamp, client_id, state
, закодированная как base64 URL safe
.
Пример класса расчета подписи
Расчет client_secret
Если URL сформирован корректно, пользователя перенаправляет на портал ЕСИА, где пользователь проходит аутентификацию, и соглашается на доступ к его данным. Если аутенитфикация прошла успешно, то ЕСИА перенаправляет пользтваеля на целевой сайт, и в URL передает два параметра code
и state
- state
– предзназначен для проверки безопасности ответа ЕСИА, должен совпадать со state
переданным в URL авторизации ЕСИАю - code
– код авторизации, обменивается на access_token, с помощью которого осуществляется доступ к данным.
Получение метки доступа (access_token)
После того, как получе код авторизации, его нужно обменять на access_token. Для этого нужно отправить POST запрос по адресу
https://<ESIA_HOST>/aas/oauth2/te