Статус: Участник
Группы: Участники
Зарегистрирован: 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 токена по другому?
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 06.12.2008(UTC) Сообщений: 3,927 Откуда: Крипто-Про Сказал(а) «Спасибо»: 20 раз Поблагодарили: 691 раз в 652 постах
|
|
|
|
|
|
Статус: Участник
Группы: Участники
Зарегистрирован: 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)
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 06.12.2008(UTC) Сообщений: 3,927 Откуда: Крипто-Про Сказал(а) «Спасибо»: 20 раз Поблагодарили: 691 раз в 652 постах
|
((X509Certificate)certificate).getSigAlgName() - это алгоритм подписи сертификата (как он был подписан), а не алгоритм подписи, соответствующий ключу. Пока везет, что проверяется. |
|
|
|
|
Статус: Участник
Группы: Участники
Зарегистрирован: 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. Евгений, можете подсказать что я делаю не так?
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 06.12.2008(UTC) Сообщений: 3,927 Откуда: Крипто-Про Сказал(а) «Спасибо»: 20 раз Поблагодарили: 691 раз в 652 постах
|
Возможно, вы продаёте данные в base64, а надо предварительно их декодировать из base64. Проверять подпись с помощью CAdESSignature рекомендуется, так как эта проверка высокоуровневая и скрывает детали. |
|
|
|
|
Статус: Участник
Группы: Участники
Зарегистрирован: 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
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 06.12.2008(UTC) Сообщений: 3,927 Откуда: Крипто-Про Сказал(а) «Спасибо»: 20 раз Поблагодарили: 691 раз в 652 постах
|
Приложите содержимое jwtBytes после декодирования тут в виде файл или попробуйте сами содержимое jwtBytes декодировать на lapo.it |
|
|
|
|
Быстрый переход
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.
Important Information:
The Форум КриптоПро uses cookies. By continuing to browse this site, you are agreeing to our use of cookies.
More Details
Close