Is there method available to do base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the string for given alg?

Issue #223 wontfix
Arjun Balla created an issue

I need to calculate c_hash and s_hash and just wondering if there is generic method available or I need write a util method on my own using jose4j core classes.

Comments (4)

  1. Arjun Balla reporter

    It was not obvious that AlgorithmIdentifiers.EDDSA is EdDSA (Edwards Curve DSA using Ed25519 and SHA-512).

    Attached is the util class if someone wants to use.

    package com.arjun;
    
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Arrays;
    
    import org.jose4j.base64url.Base64Url;
    import org.jose4j.jws.AlgorithmIdentifiers;
    
    public class HashUtil {
    
        private static byte[] hashString(String input, String hashAlg) throws Exception {
            MessageDigest digest = MessageDigest.getInstance(hashAlg);
            return digest.digest(input.getBytes(StandardCharsets.UTF_8));
        }
    
        public static String getHashingAlgorithm(String alg) throws Exception {
            if (AlgorithmIdentifiers.HMAC_SHA256.equals(alg) 
                    || AlgorithmIdentifiers.RSA_USING_SHA256.equals(alg) 
                    || AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256.equals(alg)
                    || AlgorithmIdentifiers.RSA_PSS_USING_SHA256.equals(alg) 
                    || AlgorithmIdentifiers.ECDSA_USING_SECP256K1_CURVE_AND_SHA256.equals(alg)/* ES256K (ECDSA using secp256k1 and SHA-256) */) {
                return "SHA-256";
            } else if (AlgorithmIdentifiers.HMAC_SHA384.equals(alg) 
                    || AlgorithmIdentifiers.RSA_USING_SHA384.equals(alg)
                    || AlgorithmIdentifiers.ECDSA_USING_P384_CURVE_AND_SHA384.equals(alg) 
                    || AlgorithmIdentifiers.RSA_PSS_USING_SHA384.equals(alg)) {
                return "SHA-384";
            } else if (AlgorithmIdentifiers.HMAC_SHA512.equals(alg) 
                    || AlgorithmIdentifiers.RSA_USING_SHA512.equals(alg)
                    || AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512.equals(alg) 
                    || AlgorithmIdentifiers.RSA_PSS_USING_SHA512.equals(alg)
                    || AlgorithmIdentifiers.EDDSA.equals(alg)/* EdDSA (Edwards Curve DSA using Ed25519 and SHA-512) */) {
                return "SHA-512";
            } else {
                throw new IllegalArgumentException("Unsupported algorithm: " + alg);
            }
        }
    
        private static int calculateTruncateHashLength(String hashAlg) throws Exception {
            int hashLengthInBytes;
            if ("SHA-256".equals(hashAlg)) {
                hashLengthInBytes = 32; // 256 bits
            } else if ("SHA-384".equals(hashAlg)) {
                hashLengthInBytes = 48; // 384 bits
            } else if ("SHA-512".equals(hashAlg)) {
                hashLengthInBytes = 64; // 512 bits
            } else {
                throw new IllegalArgumentException("Unsupported algorithm: " + hashAlg);
            }
            return hashLengthInBytes;
        }
    
        private static byte[] truncateHash(byte[] hashBytes, String hashAlg) throws Exception {
            int hashLengthInBytes = calculateTruncateHashLength(hashAlg);
            return Arrays.copyOf(hashBytes, hashLengthInBytes / 2); // Get the left-most half
        }
    
        public static String generateTruncatedHash(String input, String alg) throws Exception {
            String hashAlg = getHashingAlgorithm(alg);
            byte[] hashBytes = hashString(input, hashAlg);
    
            // Take the left-most half of the hash
            byte[] truncatedHash = truncateHash(hashBytes, hashAlg);
    
            // Base64url encode the result using jose4j
            return Base64Url.encode(truncatedHash);
        }
    
        public static void main(String[] args) {
            try {
                // START
                String alg = "HS256";
                String state = "should-be-random-string"; 
                String code = "idMMjUW5RNjEhE7CggOwHTi1BR0Hxxnwv0y6qV2FVzs";
                String accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjE2NDAsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNDgwNDAsImlhdCI6MTcyMzk2MTY0MCwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoibHNNMnpMWnR3WnBQLTVJYnMxUF92Ul9QWVlOazhZdjMtU2lyMTY4S2xxNCJ9.GVsYFcr6m32FsTxHCJvwOtnD_gG1nCBX6ZNuixQ4_K1Mx7n8H6gxbtPFPsTrvEUxQF6I1OEqMuTMhOAVEUHTTR-vZaF1VXuVvvBDVoHPLidZgEEpRmnRdVd47xS0vyAEychqXb5EmzbHOBE1WxCMoXGQbpM2DJzkFF8HRE21EHipQP04G9zg26hiT7G3QrrUqjxV4aJPMg9DuUWWKkdCYXChjLWjHWAqbLW7pRx4aIuMYypuUrKUcf-QBnfNX97KuKlifQ-AuMTjYVTzLq-UpSrp79oKI9W9qbKoHjvoJgMdCwWJNMgZ3DJBRMs9kgG4-LZ0jolDp6PEe4pB0OwEVQ";
    
                String atHashExpected = "QGNErb77VHMX5_NCGGDZhA";
                String cHashExpected = "NF6L6mQG7WIHtUQ15k578Q";
                String sHashExpected = "Ty65ewKog9Ho3zwMT5I7cw";
    
                String atHash = generateTruncatedHash(accessToken, alg);
                String cHash = generateTruncatedHash(code, alg);
                String sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash" + atHash.equals(atHashExpected));
                System.out.println("cHash" + cHash.equals(cHashExpected));
                System.out.println("sHash" + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "HS384";
                code = "wPydJgQD1SsQfqqHGeL87oR_XI7vZVzmoBbU02Ka350";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTA4MDgsImlhdCI6MTcyMzk2NDQwOCwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiaWd2eHVZYm9KX1BaY2VVVUdFUGd3cnZhbDNweFFIRTZibmE1aG1yZ0ZqcyJ9.roF73wXaFVQvR3dN02NlOirEF_EFVJtp3HTxhOsBX4mQt0k-2V0u0-yq-mqvWxbTlXmtPXeRrC_O9Bf2Qxp5VRC_EVgr6xZR6uYU4U8Sf19yGHghWRBCzN4ke34eqoVbo41ZxgBRTgAegL9QS1Kn9s51czSJwzJ3ezeDxeTZESgatAwCbeIrHAXPvxCyKDbbXsIbApjisRtjhM6Ecoz72up07cqxLJGBKFVoRRs2r2vjfSiPNRmJZ0i6L4dVi-iIYsi8X-PcCKJU5EIk8DAg6wOJezwN14rPOXEfPC2hrb-Wjp2sQ9y1tgVHF5HGmkfIcIj4FJcRNOmfR2p9ICNmYQ";
    
                atHashExpected = "hJTPOV0Nxpmx2FPGpIIonMB8RgiROZdk";
                cHashExpected = "L2hwGiGDELA--BZrwQ9T-nGlI9398f3q";
                sHashExpected = "FF4NRl05KVWmZkFNJBqg-s-RtIcR03va";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "HS512";
                code = "qimQIBo9KBJDQzZkGa0bDb0IaIByO1ys_YS1gqv9Bz0";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTEzODcsImlhdCI6MTcyMzk2NDk4NywiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoickhUcWQ1bVN3d2txR0Rmb2g3SzVwa0gxQVhyZW9aNDNxVzdMaEI0MUptNCJ9.fo3fk5T24K_61Jdgj1oBScaN97uKXypyDP-p1exKRugaRFxykaqECz4sEQNt7v2w_sZ5NmFx2HSxrOnloHgvykk-kOOe9hBlWMEpl7uDmL1yQ1DrbzlmIyzLX1HeTPF5-yYHQ3uaBJ4bi73QnLeyfxsmP9hi3oBHSYPwo9P-45lumCd_f2SVRQ6ckq47PeATLqHfeTnrqNzL4VeUimXia2miznEj2ANz-nhqwSUX8BiCrep2osvFLYd1Pt0tJELRCpn_DNAlgEO59aikKCr5t7WzpELmSBDQ_zmCyRfV5L8QkfkfyHFSoX9ZSG4k3rHtrEZVakBIIrH1TXiift06Sw";
    
                atHashExpected = "2nSFP9JlYm_YKsFYccVDlbM8GuujH7ZeDKmie_lZVVE";
                cHashExpected = "gfud5Z9qnehl1kjMxJkXqTPSm1QeUHnFn9QBhpKh-vQ";
                sHashExpected = "pvvYRKe25WWuwzLURAZzJUZ09TX47jFSGMLeOWzsFz8";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                alg = "RS256";
                code = "kF_gAwpkuzJAcdHX3HNkm7ENXoymckQH80eQklePsIM";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTQxNTQsImlhdCI6MTcyMzk2Nzc1NCwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiRTBSbzlGbWFhTkxXZ3NBTGtjUmU3aTl4TXJ1c1hMM2ZHbjZRV3Y0YkdUMCJ9.rM-R-K0TQuE9_23VILJB1Jo221nciCqjBQkUEFcZ4brce8RLbRTMAKTkI02_sepe9Q1w1B6FQB5nLi6VTF-uDC_asmgaIvyCbCRv6qpBsDlPu0E2NFLofO7ldr3AkIbhItTxm6C06zdz33z4rJz69cdW9zmkmF3QFJb55AQccZ2kSQ60H74Eym82xXT72rrPP7zszGr15KDkvZnWjHWIGA9NSHZMrGC6MYE9z5RWHhLF1uAK3_gwr86FEvQow0IEoDJHM0W4CGaQCp6t8YSVVir2ewH_4Lr1-orz6gdLJMS4az3MEs6t-8qf3-y08h4NIxGsO-2zQY188grCImsI8w";
    
                atHashExpected = "htAzKuONBzZfGqjF2G80GA";
                cHashExpected = "YLiR4AeB2n7e75HE0YMowA";
                sHashExpected = "Ty65ewKog9Ho3zwMT5I7cw";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "RS384";
                code = "Lvou5V1yFCI4jYnJZotGuheeuudQn6awkn1_rCdbSwA";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTQ1MDAsImlhdCI6MTcyMzk2ODEwMCwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoicW4xWU5aWElqVmhlQ2p0MTQ1eUFrNFFyR3AxVTl5b19SWlBiTzJDa0R3TSJ9.cCVYTuUFv_HQ_wE2eM_nDSTIGuroM3zWTZvYMifwsYYPoCMXpCeE1zy38R3pItIqJ32qSsFtikNr9LUBtkH8RnWkDu_0QT5eRz44GazElnjG6Qpv9kOWo2vDWwWHje_CLBp3wafO0p_CW0ThgG9XgGHPmSQcQCtcHltCllwqaTbLqSM1reP5EO3R3RdFlPAXqu2aYbUnIE4Qd8qVilq31H95EzYylBT8uj_ARzXtOPDrKCydw6_8mplvNX-Y2MgAEaMr6kyCxfeAFDT15CrA0qjA_t_aL9heKUrKKiDXFJHr-6zWZ_5NqLuq0yz3ZRqepBmCiGQyw8gr0-Y2NOF2LQ";
    
                atHashExpected = "gQDNYRgpSxUo-Otp9lfdwQrV7C7HPJwP";
                cHashExpected = "HrcuInZcDtwVR5mi0UFEavYiWNtx1NVX";
                sHashExpected = "FF4NRl05KVWmZkFNJBqg-s-RtIcR03va";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
    
                // START
                alg = "RS512";
                code = "t7LZW7yvOx8Jak0ejItq_Pwzcqb8oHh2q-0ug1FMxMM";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTQ5OTYsImlhdCI6MTcyMzk2ODU5NiwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiRVE5TlF3WjBSWDN3cDVhQWcwdnpUNXNyUk96amV5aGNYckg5cEM5MDZRZyJ9.c458EcTTuu87kJn4Hn0joc7RXLuLUW0qPrpzf9TOpyUJs6vcxeQkyeuG1L0hBNuYqnE5_FgRR4ljFPLSab_1hkSTf4B54sQzKkU4j_Sj6IpCY-6CWZWqGQ2yVzQhpHmuPk_Pi64KlRIuDrxq7BTfDpvo9B4INV9fYAE4A0gGIH6u57FRfFFmJchcr6bfxNzvZSJ0uz0JdBgX9n8iYrbS-vUJ7dCxTHX_ZfRNgzPrNMHDYEeYAnsr-YuNraWLjGCw4VhBYHvP-IP3qbQxbzYLgvqIHE0pHIY-CZRe-XWvnS1BuYAHcw24QojRDZgn7bQ_eY6ZT4XBzJ5FkdA0kXzJAg";
    
                atHashExpected = "3mmldkDRf0MeSzTVVHtKZQ3ISJ1WwtoEGVGpd6R5a4U";
                cHashExpected = "kuNUzhjMeiiC2MyHQ1oXjBd2Pt1lOt3CJQ2ShVHCzt8";
                sHashExpected = "pvvYRKe25WWuwzLURAZzJUZ09TX47jFSGMLeOWzsFz8";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "ES256";
                code = "wdS6BXetMfWYb7fKfReJK0b3HX4Q4KfznSAYnLOCJSg";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjQwMDg3NzcsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwOTU0NjcsImlhdCI6MTcyNDAwOTA2NywiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoid3E1LTBEeDFualkzRTIwZS1pbC03NGM1VG5acGd4WERjVTZRWmlTcDlFdyJ9.KGfNTqyMNZ9PAyPzQJ0hIsYnfxhuvYrq94tr-US8VJ8eAVspAVilQaC92AQ1IzHEtA_B9aC1oPLNh02NF8MJsmOK399pXDZhXrLjNUX0stViJWIGxASfsYPbK9URSdq_qGBBRiqYtrADHOLaLICMfA6NMBH487IUKzY4y-WS6SGwIil2KOpRP2E_jGQbNPlRSliQxV-0NvDTNNOe3C2KWaNWmUSX8iqfmtrSCcjFwCir5ecA4_GAE9w6AWYWZSoKszB95E5MZW37_WsL0_aziRzmp3LXPj2GW1C2ZZWs018CNrbrfLq9zHJnkMAHl3FZSeCINmQ4AmJ9t_vt8Sul9w";
    
                atHashExpected = "tFK7W7XjHS3Ht3flww6K2Q";
                cHashExpected = "s-BJ-yjZdnU_gj2C7KdaWQ";
                sHashExpected = "Ty65ewKog9Ho3zwMT5I7cw";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "ES384";
                code = "Y8a907TSi-W4JTQCvDJjPva_Pr0797hVF_yzGbyivUI";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjQwMDg3NzcsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwOTY3MzksImlhdCI6MTcyNDAxMDMzOSwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiMEhFRXR6VWVuOUNnaXdCSjFQalVQUFhuZkc5eWVWeG5pU3lZQjI5NXJUTSJ9.bDHYpPzZW1Bk2BexuJI_O4tzpkPIXq37AL9U388Cok6G6gCjGfgvodLAusHUpiWLrY0XB1bJZLiqHIpHsqJn3hZbqdQm_cBzKGBqQlH9mbWF3PBwrGPfxnHRX-iUQm4IuuX2xYrkTpvgL-DPCv99ReLVyW4sAu7DlBlDtWciP2gYAiEgkuLxwAXdzEL_i9mV_-KnCPX67dOj9xeNalXh8iBh4UZX3Ytp_cXy5BIXXS3ncfxwLDO2lGXXDQ_3k3ggCydOnj52O8Uw8tZNYPKZSgHiMRxyvvLFRYnua4GHeCARhULrDa2SNdFmkTzyJoYqyOfeMXmA_9k89y7mmwNoEw";
    
                atHashExpected = "j1JA3dUL4fIv2jQF7fW6MM_gz1oLYqFi";
                cHashExpected = "OE-RTlj973fioaNeu9fhbSFFTEdfpoqo";
                sHashExpected = "FF4NRl05KVWmZkFNJBqg-s-RtIcR03va";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "ES512";
                code = "0bZ45pR163U9m0mn1eCoPUN0Zs1HZMdRY2tZ-saR39E";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjQwMDg3NzcsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwOTc5NzksImlhdCI6MTcyNDAxMTU3OSwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiai04MXBJQXRiOENCTWI4SWJtcXY0dTdMNUo2T3UwQ3VnelIzVzVGVWVnQSJ9.AV6wN3UugS3wrYd4foWA5puDpEGBhxiqkZY4o_BygEfeF-XqwXtsNvjbfMntJB7JhdInEQPMQ_7X9annQwpvwryfm8L_Bi5OLzO_eMwIqaSI-9SgMhIaOg92Qkff3QNOaAG-TZhJmUBdGIj-1GnvmsRrnNGh_A_3-920BN46ug_R7PxDOpyD9OpmLD3yvQlExjBIhqPMk6TdVCHRw8ZiQmHX14EMpWHtu4-KrarWbjxiLLs2XeGXRpFgrgOs5bQmfQtMb7rtbT4V2UEuRi6DPXvZo_MZI7IaeBgzFly2N7KdXGtpm8etkxRFKtTMn9jf_taDTDJTqF3RfXVwSuNRlw";
    
                atHashExpected = "07zxYX1_5rHhVR8OkXnijx7BuqfNpmpdKgfcDD2XefA";
                cHashExpected = "auym9XSkDs_LrQmQTa7QQYd1kTrX1K6Q2LUX22kZ194";
                sHashExpected = "pvvYRKe25WWuwzLURAZzJUZ09TX47jFSGMLeOWzsFz8";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "PS256";
                code = "pAfJEN_EDGEo4yFAhxNvmmn6b6XVSX5HCWiZnun-A5k";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjQwMDg3NzcsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwOTkyODQsImlhdCI6MTcyNDAxMjg4NCwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiYXVJSVlGS0JqM3VUOXFybUpqNHVrR1ZyUjdsSktHajBPQkdQcnNxTEJNRSJ9.Zj2ZWd8A0Qj6CX4zEXnsBKPe99KoyEKFLRBe6b95JosyQpvEOE31PnnfLSgv9xUdlbb6PocRw4dqhVUhvBx2qA2k5OI-KLqwx_hRCpxibYheeTHWSbn9CHJrC0GAySi7W2hyHnyJr3i2D2SqxAVrurRsJmZ9ARN9mhl4luaq-2794JVY6UEfo0RJnQUwiPGQLrl4l2vhxBBd1T5ADCUc0-g0g8KwQBpzz-IERzcLrnJnuHlLAgsB1FmDmUAzlxD-gTiOQURI1juA-3LK-eK7ZeSCAP6u8MJD9MIZu9Pp831YBZoHCSeAX_ZNtJHtNH29AJnsVK73HGUtiQgO2ifV_g";
    
                atHashExpected = "fFCK7f1AfkJKE4vI-RToIQ";
                cHashExpected = "-gETQJpJ7eN9hP1plClEDQ";
                sHashExpected = "Ty65ewKog9Ho3zwMT5I7cw";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "PS384";
                code = "GJ3wpjYrL0wLm6Z7XgZrFxunBRagO6QBIcdAyisVfus";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjQwMDg3NzcsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwOTk1OTcsImlhdCI6MTcyNDAxMzE5NywiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiVktzdjlydWJ5dF9JeXR6MWdHZ0hlVndIcV9RX29wUzBYa0dzbEFvZUF3WSJ9.AkJTQxZgra5yW9xQSiud2nV6xlj5q7_VO_w_o_l-NeckQtmKMKH6Jm8vq9WGSfeEt9n2n5TUpH6OwD24yPaYUn5YLXcP-WFUPrYTz1LTm0-hRUMoAJDNEDn-htpdzol_N28Fj8AZVdCc3-XJtjMyP5A86anVVqn9toljRiSMGw_zIUMlCVeC-7SMM18e38XX13-7KhV-OWe88PG2mRSIJYrbT4dUjfhLNPE8W0RgS_YIi3RgKglTI6eLUAI5VoSw2xdfp5pbsZfga5Vz1Uuk1q3XPg9g7EB_k-uf0HDqDhq0CXlmKwE-UVwTP_w_5pPl33TE3BatMVbrG-6S8PitEQ";
    
                atHashExpected = "7o0z94J52fqpdlwWLfJxUr1ejvE1UQUl";
                cHashExpected = "dC9X4XGfTibq9pckMQCIUg0nns0JAkmR";
                sHashExpected = "FF4NRl05KVWmZkFNJBqg-s-RtIcR03va";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "PS512";
                code = "oSFinAUfqf5i0uLK61O5gK1h_Z57lt1rQQbVfhQrdy0";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjQwMDg3NzcsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwOTk2NTksImlhdCI6MTcyNDAxMzI1OSwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoibTMya0hWTXpOZFZOTXFKZjh6ck9BS2JfWFVJdHdyTC1TelJBbERYOTl3MCJ9.OdjRzwWO7BbN_w9M4450Jz8p64FpfSP1cP3ILMvLIsDXjg8xH5-nodOWhPY1wRrLvQszjNB_dM1tvI-idsbzOSv2PJR6VqMb5i_aGvFuTQdzODUtkotoNMGq-eEymlQuHDTZp8Jg5s2OzOAlny8Lfg-6Mbw4eD0FaALJNKcu1p7ng9bFcwI5FAuxO6jfJdIl-bWtgc3kCqv1T7et6odU1upF5wmYX8tVmgjIrrY4Y_5azQ1-4zc1mYxy5jbwNhVHRtaUrXcvcGHVYDbNwHTHiuK3wQ0HLzkbx9-Rjfw_tZMMGPD9pMVrBTGd6gFxeu-lTIQkuj-BMfWrjlWnmEJ1pQ";
    
                atHashExpected = "nCT8wZCjceYkVFfZAQEH2XxNCp_CHfPxKh_BoJnt9O4";
                cHashExpected = "fJI9gSS0amI0OJGKctbfaD24Tv90xCn1SL9OutAAYy8";
                sHashExpected = "pvvYRKe25WWuwzLURAZzJUZ09TX47jFSGMLeOWzsFz8";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
                // START
                alg = "ES256K";
                code = "1lA7vYMhdPITkXZx-9p_bZQifdBuz1orB0mOZquFGJ8";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTY4MzQsImlhdCI6MTcyMzk3MDQzNCwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoic25Nc05wNkRJUURMVWQyTUN0VF94SHNNRUZKaW40YTltSWhHWVQxMjljcyJ9.NVKHlUpecdzoJMS33J0Rk-vuTfTT3-3Oqy7Jkx14VQGYbuw_NxHbFO-DBE02qxfeD2oy3bU7512lAsCz2rThoh7XgFHETevZQurAJlh2hRkopneaXtegXU-uoCZlFG908VhFqBEfcLDWmpmBiF0o4HTiPdRGmGEo5HJCRzoS_yGKiTSQTi6TQg-dS--bqnvIES50cI1FIuq0kFmQJmfXRtBN8dnlIa6BRCngOEx6fDHvn2xh-DGXylBZ9OpsKmVNSYLYrnT8C46j4YGVIfxeTf6fzh7wwgkjaBCxJCJY_oFvGadzuM7S_WbNHd11c4W4H1JIKt6JbBJiWM3I4F4L1g";
    
                atHashExpected = "88MJoEakHlgkQo-M6cxf7A";
                cHashExpected = "gWdBbfOYCwR3_TZRoGczMg";
                sHashExpected = "Ty65ewKog9Ho3zwMT5I7cw";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
    
                // START
                alg = "EdDSA";
                code = "iQ0MosXT8UkeC9Wt1WUCiRAp6_VY3Sa9gknHJu4sS9k";
                state = "should-be-random-string"; 
                accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Imp3dC1vYXV0aC1zZXJ2ZXItc2lnbi0wMSJ9.eyJzdWIiOiIxMDAxIiwiZ3JhbnRfdHlwZSI6ImltcGxpY2l0Iiwic2NvcGUiOiJvcGVuaWQgYWxsX21ldGhvZHMiLCJhdXRoX3RpbWUiOjE3MjM5NjQ0MDgsImlzcyI6Imh0dHBzOi8vYXBpLmF1dGhsZXRlLmNvbS9hcGkvYXV0aC9hdXRob3JpemF0aW9uL2RpcmVjdC8xMTEwOTM3Njk4ODY4MTEiLCJleHAiOjE3MjQwNTgxMzksImlhdCI6MTcyMzk3MTczOSwiY2xpZW50X2lkIjoidGVzdC1vYXV0aC1qd3QtMDEuYXBwIiwianRpIjoiUE5rbHBCNVREOWQ2QXFmbTBsa1NZQnViYnFwc3JSSHlmcUgxLTFJVGVkbyJ9.Ehp2oPiNbB3UcdV-VKPA9jSpUOtBSrVDoKwF7edTd23-zQr_Gppdp6uEMbBmsJkOApGpDp9Fl81PRvV0QlGO3Lm6bSjhz-rB9LWCmeiNJ-5cJAb3s-fw-BsOhY-f3rgq2MoVxf3MG8Ft5h-jgpKH-ZFPtWmo7ZQx03xqKSWY11BP1Tl4HeCS2LNUfZwqHiOWxKVYAtGwLGfV10-YiMYqUhva4fG1v1EUJtXrAeDthcrf2LrmVocM7nujKzTpq5ChpwItg_TiOrefZvdvfzc5JU9v8Vfi9Oji3zOfYQ3KRiy3doIRZFSv73KICbgJSlaqdkFre8hDVj8DP26D8joucw";
    
                atHashExpected = "Q9wwPUetdKjNyzH-GMQ-5hquAnQuHpvxl7dZ3g_-r3M";
                cHashExpected = "sSG7lj0x6yFFrGojP_e-rMpgUfbVf825owOzQtjOp_k";
                sHashExpected = "pvvYRKe25WWuwzLURAZzJUZ09TX47jFSGMLeOWzsFz8";
    
                atHash = generateTruncatedHash(accessToken, alg);
                cHash = generateTruncatedHash(code, alg);
                sHash = generateTruncatedHash(state, alg);
    
                System.out.println("############ " + alg + " ############");
                System.out.println("atHash " + atHash.equals(atHashExpected));
                System.out.println("cHash " + cHash.equals(cHashExpected));
                System.out.println("sHash " + sHash.equals(sHashExpected));
                System.out.println();
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

  2. Brian Campbell

    Thanks for sharing the code Arjun.

    Unfortunately, EDDSA is even more not obvious than that. See https://lists.openid.net/pipermail/openid-specs-ab/2020-June/007781.html which points to https://bitbucket.org/openid/connect/issues/1125/_hash-algorithm-for-eddsa-id-tokens for example.

    Then we have the well-intentioned but arguably too ambitious in scope and and after the horse has left the barn anyway https://datatracker.ietf.org/doc/draft-ietf-jose-fully-specified-algorithms/

    Good times.

  3. Log in to comment