Не удается проверить подпись JWT в Java
Я пытаюсь проверить токен JWT, проверив его подпись. Но при проверке я получаю ошибку
java.security.SignatureException: длина подписи не верна: получил 342, но ожидал 256
, Я предполагаю, что подпись является зашифрованной sha256
хеш данных. В моем случае данные это base64(header)+"."+ Base64 (body). Вот мой код:
static String certificate = "MIIDBjCCAe4CCQDmPif23IJerzANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE1MDkyMjA3MDkwMFoXDTE2MDkyMTA3MDkwMFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALYjKc8pLFkAa45j6w6PHsroe7ijOCfhZmVtMCvZ8lINaP9mR8irOJHpLdJs4vpbxEZMqqLMhKjO7iUmXBmml37QRlJXY6f25essPkTdUmhiIrU/rIrZrCanvegXUHkvf4xvOQ1BTx/p5b1iIq3Wrk5Fox3pMigzqYhk4YuiJho8uabC9zyecmS3zIoRgwx+Vacel/ZW6r6YOlB6mblN9IvasvqWgDalegmMKOIZvwkpo/3mfzcGi5haWZZ3ufUqQjb4B7raJmfyrLnwi6XI9UzzGc04pCfIAsxTb5yM8cJQcJ/5VHF3h21eFJdZKyD2210gSq7/Y8Oda0dDXQchmFcCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAmBm84PfwOe5sGemT9D4ImIYWp/1xrV6b0zeaDZ6Yi7sz9R3VKjfnAxUVOHA7GlMi8xHHdDu7VhVhAWyFCvITdG00K18xJuKap1bwzw3kr70lLQaFahV+zaWKfWAfV34tots3G/hMqdOv0a+I/5t/T7oKPCmm/IfCVKdC1tGbTji+hxVLpaAkn60RFNzLKGFwtSxv9ObxR5Hn88+wV48VAcEnwcUk2DjBi1fW6jnMcNJbVd+/oKBOwj7UK2Lk10Qaeet8KKh5fFKEpgx7D4ITwer0G/Je1NMv1/lfNzpKlTKoBureF5C6B+rJIesQ/dAfg6H/ggxbgVMuo6imIPVvrg==";
static String signedData = "Z5MwwjtXdypMQGNwmNNuCVmRcDVT24EgtwoDWalF4icxwz7jyB99Yg3262D7OsERewv4cOfdEz3bbOF-iG7YWXeSC9YZeO1tGapqlc8FRtAergSUZC7BcbFEx75MfSy7qLWYTOfdpJesQ23rOzjF7KdrAMJC_Y0T_r6RuBcZVyfT4P55kICETYyv7bBDXc9V8BJUf-QHDu6DaH7u6PSyeOmdzFInI_LwySnMlr3VahoUfUpJmauU8yHQUnFakJBgrMBe1Au9tS-HxtDVnHmoHQw8xGXsVQnEOa1aPAcVWy0v7hILUSmWNAG3IZ0JwUztQitgtnTTzXDszUxTbJ4YlQ";
static String data = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiJodHRwczovL2NpdHJpeHAuY29tOjg0NDMvIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZGQ5YjZhM2UtMjlkMS00MjU0LWE3NDYtZTAyOTQxNDQ0NTE3LyIsImlhdCI6MTQ0MjYyNjA0NSwibmJmIjoxNDQyNjI2MDQ1LCJleHAiOjE0NDI2Mjk5NDUsInZlciI6IjEuMCIsInRpZCI6ImRkOWI2YTNlLTI5ZDEtNDI1NC1hNzQ2LWUwMjk0MTQ0NDUxNyIsIm9pZCI6ImJkZDNmZDAyLTEyMzMtNGMxOC05NTRmLWJkNGFjMWYzOWU5OSIsInVwbiI6InNwQGNpdHJpeHAuY29tIiwic3ViIjoieUFfZTFzMmFOeFdvR3NSNzhfVWNYZkk5dERlYUM0QUozdXFZQXFDdllSbyIsImdpdmVuX25hbWUiOiJzIiwiZmFtaWx5X25hbWUiOiJwIiwibmFtZSI6InNwIiwiYW1yIjpbInB3ZCIsInJzYSJdLCJ1bmlxdWVfbmFtZSI6InNwQGNpdHJpeHAuY29tIiwiYXBwaWQiOiIyOWQ5ZWQ5OC1hNDY5LTQ1MzYtYWRlMi1mOTgxYmMxZDYwNWUiLCJhcHBpZGFjciI6IjAiLCJzY3AiOiJtZG1fZGVsZWdhdGlvbiIsImFjciI6IjEiLCJpcGFkZHIiOiI2My4xMTAuNTEuMTEiLCJkZXZpY2VpZCI6ImQyNzU3NzY0LWFiOTEtNDBiMS05MmM2LTViOWE4MWYxODNiYyJ9";
public static void main(String[] args) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
String certString = "-----BEGIN CERTIFICATE-----\r\n" + certificate + "\r\n-----END CERTIFICATE-----";
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X.509");
InputStream stream = new ByteArrayInputStream(certString.getBytes()); //StandardCharsets.UTF_8
java.security.cert.Certificate cert = cf.generateCertificate(stream);
PublicKey pk = cert.getPublicKey();
//verifying content with signature and content :
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify( pk );
sig.update(data.getBytes());
Boolean ret = sig.verify(signedData.getBytes());
} catch (CertificateException | SignatureException | NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
1 ответ
Часть подписи JWT имеет кодировку base64url ( см. JWS §7.1), поэтому вам необходимо правильно ее декодировать перед проверкой.
Заменить линию Boolean ret = sig.verify(signedData.getBytes());
с чем-то вроде следующего (используя декодер Base64 из кодека Apache Commons),
byte[] signature = org.apache.commons.codec.binary.Base64.decodeBase64(signedData);
Boolean ret = sig.verify(signature);
И подпись на этом JWT должна подтвердить для вас.
Сказав это, я очень рекомендую использовать библиотеку для обработки JWT. Достойная библиотека будет обрабатывать такие вещи для вас и может предоставить больше функциональности JWT.
Например, следующее с использованием библиотеки Jose4j Jose может заменить материал из кода в вопросе после получения открытого ключа из сертификата. Это не только проверяет подпись согласно спецификации, но также проверяет утверждения в JWT, чтобы убедиться, что она все еще действительна, была выдана вам и т. Д.
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setVerificationKey(pk)
.setRequireExpirationTime()
.setExpectedAudience("https://citrixp.com:8443/")
.setExpectedIssuer("https://sts.windows.net/dd9b6a3e-29d1-4254-a746-e02941444517/")
.build();
JwtClaims claims = jwtConsumer.processToClaims(data + "." + signedData);
System.out.println("Subject: " + claims.getSubject());
System.out.println("UPN: " + claims.getStringClaimValue("upn")); // or whatever, etc....
Это JWT истек несколько дней, поэтому processToClaims
выбросит исключение, которое вы хотите. Вы можете добавить .setEvaluationTime(NumericDate.fromSeconds(1442626055))
при строительстве JwtConsumer
чтобы заставить его работать для данного JWT, хотя.