簡體   English   中英

Java 中的 RSA SHA-256 簽名驗證

[英]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. hash 數據
  2. 在 ASN.1 DigestInfo 中編碼 hash (1)
  3. 焊盤 (2) 帶有 01 FF... 00
  4. 將 (3) 作為 integer 和帶有私有指數的 modexp

可以通過重復 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM