[英]trying to convert Java RSA-PSS signature verification code (with SHA256 hash, SHA1 MGF hash) to Python
[英]Signature Verification in Java for RSA SHA-256
編輯 - 使用完整的 SAML 更新
我正在嘗試驗證我從 Java 中的 SAML 響應收到的簽名。SAML 響應由 samltool.io 驗證,但我在 java 中遇到簽名 class 的困難時期。無論我嘗試什么,它都會返回 false . 我已經正確規范了我的 XML 並且我能夠使用我的內容字符串中的內容成功生成正確的 MessageDigest。 希望有人能指出我做錯了什么。 這是我的代碼:
public class SigVer {
public static void main(String[] args) throws Exception {
System.out.println("Initialized");
String pubKeyStr = "MIIDqDCCApCgAwIBAgIGAYXQr1PhMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTAeFw0yMzAxMjAxOTM2MDlaFw0zMzAxMjAxOTM3MDlaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMgSFX7FkFVNzGyL7EjtIRaJRmj7/Z8diMZyAVy0rBqcIB7aL9Hn3gVefJs09XJiQiuhLVihaD4H1c9RZzS8U089wmef8w0Gc0Bca8jLziAqbzDsph9lQUxoUFCDCyGhamQZvnAk7Rk+PT012ICN/K/eB0q/Un0VEeyTyPhBimfpA2TTe3nC4kjDpKOzQPhjHvjusL0e5VMnDR37iv+yO+VKZ8bDI5WbJPeR2jWh+6EJHUIoNH0XK6Fna4ektpD4vvMSNapjKVKvyFnNoVQGpl/Hc7J0Dh7b/B4veUuYvrFEhj6iAISuD5hM70iL+xaKkf9GLObj8Ay6RTx7kR99WECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAMfjuP24tBNbcEURI9hhbMECVHxfXauVQF1H5z7uWyp5+Pqphw1LApG5KmR/RFDXzePuuVZNFZON27r78w0tixgR5Z0xrwB8Lkdt9LJSwUGNv0dxpdDPFvBoGopjcnIs1zi5MgiTwW/mkYOWJhZDo+E4CheVRyp3H32hvfEG8B+vrEH7CZ8csIK2N4hYiCJP+xktHH6yXDP7fVSWu1CdWDddpyhI0B0Gwo6aBBiyDElsNyVHGwJGT7dnsWvc8bIKP2IPFgE7zxKoARqzV/jAcryyySRiDjbS9kAw84svgWxBJefoaPAtJjhr84o7222QoxMaxi/X5V9ns+S76Wo3A4Q==";
byte[] certBytes = pubKeyStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(Base64.getDecoder().decode(certBytes));
X509Certificate certificate = (X509Certificate)certFactory.generateCertificate(in);
PublicKey publicKey = certificate.getPublicKey();
String content = "<saml2p:Response xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" Destination=\"http://10.100.1.123:8080/identityiq/external/registration.jsf\" ID=\"id130347804157883831058335572\" IssueInstant=\"2023-01-20T19:39:44.049Z\" Version=\"2.0\"><saml2:Issuer xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:entity\">http://www.okta.com/exk81ebtt38SLQ3I65d7</saml2:Issuer><saml2p:Status><saml2p:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"></saml2p:StatusCode></saml2p:Status><saml2:Assertion xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"id1303478041593882583231621\" IssueInstant=\"2023-01-20T19:39:44.049Z\" Version=\"2.0\"><saml2:Issuer Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:entity\">http://www.okta.com/exk81ebtt38SLQ3I65d7</saml2:Issuer><saml2:Subject><saml2:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">matt@revnatech.com</saml2:NameID><saml2:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml2:SubjectConfirmationData NotOnOrAfter=\"2023-01-20T19:44:44.049Z\" Recipient=\"http://10.100.1.123:8080/identityiq/external/registration.jsf\"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore=\"2023-01-20T19:34:44.049Z\" NotOnOrAfter=\"2023-01-20T19:44:44.049Z\"><saml2:AudienceRestriction><saml2:Audience>http://10.100.1.123:8080/identityiq/</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant=\"2023-01-20T19:39:44.049Z\" SessionIndex=\"id1674243584047.1963093145\"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute Name=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified\"><saml2:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">matt@revnatech.com</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>";
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(content.getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] digest = md.digest();
System.out.println(new String(Base64.getEncoder().encode(digest)));
String signature = "laE5splYJDi8uoMbbQ1tq8bWHPotfZW/aoJ1psWQkuzgc6wS0XS71MIFM9fcZ7rFQEGUWw0GXR6cy/nyDkCaEOH7o+VvKaainpKSktVrAbBtomWIkFx65AsDbHdcN0CZXIz57yRzzfFWjks+fYEHoEM48/HkB8brFxiiyJ17GEd96O7DErlCxen4W874/uOOneEZYBxXtcZnqQylJjgen3C7VyeQATK1AnGba6TK7cdClJjBDapmo/VnWBP6ovH8ZbATdd6u8au1xBqXBu/a+DLwFIdmd2TdDWmT0mkzz/MqdfCtP8W5nOk3UU9ZwfHzSBaQrWH3/mdIgqh8V57BMw==";
try {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update(content.getBytes(java.nio.charset.StandardCharsets.UTF_8));
boolean result = sig.verify(Base64.getDecoder().decode(signature.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
System.out.println("Returning " + result);
} catch (Exception e) {
System.out.println(e);
}
}
}
我的 SAML 簽名塊如下:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod>
<ds:Reference URI="#id130347804157883831058335572">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"></ec:InclusiveNamespaces>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>
1jaT9pnWmUCL0+atOcxHZ2yxhL5XOIhhHaK3Oz0gDWY=
</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
laE5splYJDi8uoMbbQ1tq8bWHPotfZW/aoJ1psWQkuzgc6wS0XS71MIFM9fcZ7rFQEGUWw0GXR6cy/nyDkCaEOH7o+VvKaainpKSktVrAbBtomWIkFx65AsDbHdcN0CZXIz57yRzzfFWjks+fYEHoEM48/HkB8brFxiiyJ17GEd96O7DErlCxen4W874/uOOneEZYBxXtcZnqQylJjgen3C7VyeQATK1AnGba6TK7cdClJjBDapmo/VnWBP6ovH8ZbATdd6u8au1xBqXBu/a+DLwFIdmd2TdDWmT0mkzz/MqdfCtP8W5nOk3UU9ZwfHzSBaQrWH3/mdIgqh8V57BMw==
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIDqDCCApCgAwIBAgIGAYXQr1PhMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTAeFw0yMzAxMjAxOTM2MDlaFw0zMzAxMjAxOTM3MDlaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMgSFX7FkFVNzGyL7EjtIRaJRmj7/Z8diMZyAVy0rBqcIB7aL9Hn3gVefJs09XJiQiuhLVihaD4H1c9RZzS8U089wmef8w0Gc0Bca8jLziAqbzDsph9lQUxoUFCDCyGhamQZvnAk7Rk+PT012ICN/K/eB0q/Un0VEeyTyPhBimfpA2TTe3nC4kjDpKOzQPhjHvjusL0e5VMnDR37iv+yO+VKZ8bDI5WbJPeR2jWh+6EJHUIoNH0XK6Fna4ektpD4vvMSNapjKVKvyFnNoVQGpl/Hc7J0Dh7b/B4veUuYvrFEhj6iAISuD5hM70iL+xaKkf9GLObj8Ay6RTx7kR99WECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAMfjuP24tBNbcEURI9hhbMECVHxfXauVQF1H5z7uWyp5+Pqphw1LApG5KmR/RFDXzePuuVZNFZON27r78w0tixgR5Z0xrwB8Lkdt9LJSwUGNv0dxpdDPFvBoGopjcnIs1zi5MgiTwW/mkYOWJhZDo+E4CheVRyp3H32hvfEG8B+vrEH7CZ8csIK2N4hYiCJP+xktHH6yXDP7fVSWu1CdWDddpyhI0B0Gwo6aBBiyDElsNyVHGwJGT7dnsWvc8bIKP2IPFgE7zxKoARqzV/jAcryyySRiDjbS9kAw84svgWxBJefoaPAtJjhr84o7222QoxMaxi/X5V9ns+S76Wo3A4Q==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
我已嘗試使用 PublicKey 解密 SignatureValue 以檢索 DigestValue 中的內容,但我在這樣做時檢索到的值不匹配。 這是我在嘗試時嘗試過的:
Cipher c = Cipher.getInstance("RSA");
c.init(Cipher.DECRYPT_MODE, publicKey);
System.out.println(new String(Base64.getEncoder().encode(c.doFinal(Base64.getDecoder().decode(signature.getBytes(java.nio.charset.StandardCharsets.UTF_8))))));
您的方法在兩個方面是錯誤的。
首先,在 SAML 使用的 XMLdsig 中,“真實”簽名(此處為 RSA 簽名)不在數據之上,甚至不在數據 hash 本身之上。 相反,數據的 hash 被放置在 Reference.DigestValue 中,公鑰簽名是在SignedInfo元素上計算的,其中包括規范化的 DigestValue。 相反,驗證檢查簽名是否與 SignedInfo 匹配,以及(或每個)Reference 中的 DigestValue 是否與(相關)數據匹配。 請參閱https://www.w3.org/TR/xmldsig-core2/#sec-Processing 。
其次,Java中的'backwards RSA' Cipher
“用私鑰加密,用公鑰解密”——不是正確的RSA簽名; 這是 1990 年代犯下的一個錯誤,此錯誤從未得到糾正,顯然是為了兼容性。 正確的 PKCS1v1 樣式簽名(現在在同一個規范中引用了 RSASSA-PKCS1-v1_5)是四個步驟:
可以通過重復 1-3 並與 modexp-public 的結果進行比較來完成驗證,后者在與 1 比較之前反轉 4 或反轉 4 和 3(刪除 01 ...)和 2(拆分 ASN.1)。Java 的倒退Cipher
僅在“加密”端執行 3 和 4,在“解密”端執行 4 和 3,讓您執行 2,以及在上面的 SignedInfo 上執行1。
這部分是一個很常見的錯誤; 在https 查看我的列表://crypto.stackexchange.com/questions/87006/why-is-data-signed-with-sha256-rsa-pkcs-and-digest-signed-with-rsa-pkcs-different plus
java 證書驗證失敗
如何使用 x509 公鑰驗證簽名/散列的 Alexa 請求正文?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.