Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline oleg172  
#1 Оставлено : 25 октября 2021 г. 10:13:36(UTC)
oleg172

Статус: Участник

Группы: Участники
Зарегистрирован: 25.10.2021(UTC)
Сообщений: 17
Российская Федерация
Откуда: Воронеж

В рамках интеграции с ЕСИА, нам необходимо проверять JWT токен.
ЕСИА формирует JWT токен и подписывает его сертификатом(GOST3410_2012_256).
У нас имеется открытый ключ данного сертификата и мы хотим проверить подпись данного JWT токен.
Пример токена:
Код:

eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImF1dGhvcml6YXRpb25fY29kZSIsImFsZyI6IkdPU1QzNDEwXzIwMTJfMjU2In0.eyJuYmYiOjE2MzE1MjUwODksInNjb3BlIjoib3BlbmlkIGJpbyIsImF1dGhfdGltZSI6MTYzMTUyNDcyNiwiaXNzIjoiaHR0cDpcL1wvZXNpYS5nb3N1c2x1Z2kucnVcLyIsInVybjplc2lhOnNpZCI6IjkyNjE5MDhlLWM0NGQtNGMyZS1hMWFjLWZiNDE1MzM5NjA2MCIsInVybjplc2lhOmNsaWVudDpzdGF0ZSI6IjRjOWQ3YmE5LTc3MDAtNDMyMS05Nzc0LTc5YTBjNjllNTkyYiIsImF1dGhfbXRoZCI6IlBXRCIsInVybjplc2lhOnNiaiI6eyJ1cm46ZXNpYTpzYmo6dHlwIjoiUCIsInVybjplc2lhOnNiajppc190cnUiOnRydWUsInVybjplc2lhOnNiajpvaWQiOjI0NDMzNjQyMiwidXJuOmVzaWE6c2JqOm5hbSI6IjAyMi01ODgtMjM2IDQ5IiwidXJuOmVzaWE6c2JqOmVpZCI6NDgzNTczOH0sImV4cCI6MTYzMTUyNTMyOSwicGFyYW1zIjp7InJlbW90ZV9pcCI6IjkxLjc3LjI0NS4xNSIsInVzZXJfYWdlbnQiOiJNb3ppbGxhXC81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXRcLzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZVwvOTMuMC40NTc3LjYzIFNhZmFyaVwvNTM3LjM2In0sImlhdCI6MTYzMTUyNTA4OSwiY2xpZW50X2lkIjoiNzcxNjAxIn0.KDGX6JLn3O59c3yCU-DZSCH-gaXz1n4FHicM8AEQxX11sHTYFQ014sUS0ZnjjF5Gp0vvMgfNY2SoO6ksqqMWTw

Для валидации подписи в JWT токене мы используем следующий код:
Код:

String[] parts = token.split("\\.");
byte[] tokenSign = Base64.getUrlDecoder().decode(parts[2]);
String payload = parts[1]; 

CertificateFactory factory = CertificateFactory.getInstance("X.509");
//сертификат лежит в файле
Certificate certificate = factory.generateCertificate(new ByteArrayInputStream(cer.getBytes()));

Signature signature = Signature.getInstance(((X509Certificate)certificate).getSigAlgName());

signature.initVerify(certificate);
signature.update(payload.getBytes(StandardCharsets.UTF_8));
signature.verify(tokenSign);


При выполнении кода получаю ошибку "Неверная длина подписи"

Подскажите в чем может ошибка? Возможно необходимо валидировать подпись JWT токена по другому?
Offline Евгений Афанасьев  
#2 Оставлено : 25 октября 2021 г. 13:52:22(UTC)
Евгений Афанасьев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 06.12.2008(UTC)
Сообщений: 3,910
Российская Федерация
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 20 раз
Поблагодарили: 685 раз в 646 постах
Offline oleg172  
#3 Оставлено : 26 октября 2021 г. 15:52:20(UTC)
oleg172

Статус: Участник

Группы: Участники
Зарегистрирован: 25.10.2021(UTC)
Сообщений: 17
Российская Федерация
Откуда: Воронеж

Проблема оказалась в сертификате(изначально был не корректный сертификат для проверки подписи JWT) и алгоритме проверки.

Работающий код:
Код:

String[] parts = token.split("\\.");
byte[] tokenSign = Base64.getUrlDecoder().decode(parts[2]);
String head = parts[0];
String payload = parts[1];
String headWithPayload = head + "." + payload; 

CertificateFactory factory = CertificateFactory.getInstance("X.509");
//сертификат лежит в файле
Certificate certificate = factory.generateCertificate(new ByteArrayInputStream(cer.getBytes()));

Signature signature = Signature.getInstance(((X509Certificate)certificate).getSigAlgName());

byte[] msgBytes = headWithPayload.getBytes(StandardCharsets.UTF_8);
signature.initVerify(certificate);
signature.update(msgBytes);
signature.verify(tokenSign)
Offline Евгений Афанасьев  
#4 Оставлено : 26 октября 2021 г. 17:06:51(UTC)
Евгений Афанасьев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 06.12.2008(UTC)
Сообщений: 3,910
Российская Федерация
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 20 раз
Поблагодарили: 685 раз в 646 постах
((X509Certificate)certificate).getSigAlgName() - это алгоритм подписи сертификата (как он был подписан), а не алгоритм подписи, соответствующий ключу. Пока везет, что проверяется.
Offline oleg172  
#5 Оставлено : 27 октября 2021 г. 9:56:35(UTC)
oleg172

Статус: Участник

Группы: Участники
Зарегистрирован: 25.10.2021(UTC)
Сообщений: 17
Российская Федерация
Откуда: Воронеж

Евгений, посмотрел примеры в samples-source CAdES. Я так понимаю что валидировать подпись лучше через CAdESSignature? Пытался создать объект CAdESSignature:
Код:

InputStream cadesCmsStream;
cadesCmsStream = new ByteArrayInputStream(jwtDecoder.getSignatureInBytes());
new CAdESSignature(cadesCmsStream, null, null);

В результате имею ошибку: org.bouncycastle.cms.CMSException: Unexpected object reading content.
Евгений, можете подсказать что я делаю не так?
Offline Евгений Афанасьев  
#6 Оставлено : 27 октября 2021 г. 11:07:52(UTC)
Евгений Афанасьев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 06.12.2008(UTC)
Сообщений: 3,910
Российская Федерация
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 20 раз
Поблагодарили: 685 раз в 646 постах
Возможно, вы продаёте данные в base64, а надо предварительно их декодировать из base64.
Проверять подпись с помощью CAdESSignature рекомендуется, так как эта проверка высокоуровневая и скрывает детали.
Offline oleg172  
#7 Оставлено : 27 октября 2021 г. 12:34:06(UTC)
oleg172

Статус: Участник

Группы: Участники
Зарегистрирован: 25.10.2021(UTC)
Сообщений: 17
Российская Федерация
Откуда: Воронеж

Подпись декодируется из Base64 и передается в конструктор CAdESSignature:
Код:

//разделяет токен на 3 части
String[] parts = TokenUtils.splitToken(jwt);
byte[] jwtBytes = java.util.Base64.getUrlDecoder().decode(parts[2])
InputStream cadesCmsStream;
cadesCmsStream = new ByteArrayInputStream(jwtBytes);
CAdESSignature cades = new CAdESSignature(cadesCmsStream, null, null);

В итоге ошибка:
Код:

ru.CryptoPro.CAdES.exception.CAdESException: IOException reading content.
	at ru.CryptoPro.CAdES.CAdESSignature.<init>(Unknown Source) ~[CAdES-1.0.jar:41940-A]
	at ru.CryptoPro.CAdES.CAdESSignature.<init>(Unknown Source) ~[CAdES-1.0.jar:41940-A]
	at ru.CryptoPro.CAdES.CAdESSignature.<init>(Unknown Source) ~[CAdES-1.0.jar:41940-A]
	at ru.rencredit.services.crypto_pro.manager.impl.CryptoProManagerJWTImpl.verifyJWT(CryptoProManagerJWTImpl.java:52) [classes/:?]
	at ru.rencredit.services.crypto_pro.manager.CryptoProManagerJWTTest.testVerifyJWT(CryptoProManagerJWTTest.java:61) [test-classes/:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) [junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) [junit-rt.jar:?]
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) [junit-rt.jar:?]
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) [junit-rt.jar:?]
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) [junit-rt.jar:?]
Caused by: org.bouncycastle.cms.CMSException: IOException reading content.
	at org.bouncycastle.cms.CMSContentInfoParser.<init>(Unknown Source) ~[bcpkix-jdk15on-1.60-1.0.jar:1.60.0.0]
	at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source) ~[bcpkix-jdk15on-1.60-1.0.jar:1.60.0.0]
	at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source) ~[bcpkix-jdk15on-1.60-1.0.jar:1.60.0.0]
	... 36 more
Caused by: java.io.IOException: DER length more than 4 bytes: 104
	at org.bouncycastle.asn1.ASN1InputStream.readLength(Unknown Source) ~[bcprov-jdk15on-1.58.jar:1.58.0]
	at org.bouncycastle.asn1.ASN1StreamParser.readObject(Unknown Source) ~[bcprov-jdk15on-1.58.jar:1.58.0]
	at org.bouncycastle.cms.CMSContentInfoParser.<init>(Unknown Source) ~[bcpkix-jdk15on-1.60-1.0.jar:1.60.0.0]
	at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source) ~[bcpkix-jdk15on-1.60-1.0.jar:1.60.0.0]
	at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source) ~[bcpkix-jdk15on-1.60-1.0.jar:1.60.0.0]
	... 36 more


Offline Евгений Афанасьев  
#8 Оставлено : 27 октября 2021 г. 15:54:59(UTC)
Евгений Афанасьев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 06.12.2008(UTC)
Сообщений: 3,910
Российская Федерация
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 20 раз
Поблагодарили: 685 раз в 646 постах
Приложите содержимое jwtBytes после декодирования тут в виде файл или попробуйте сами содержимое jwtBytes декодировать на lapo.it
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.