Forum Discussion

danielFav's avatar
danielFav
Channel Surfer
3 years ago
Solved

How to handle encrypted content

Hi everyone, im new to the roku community.

For the last couple of days i've been trying to decrypt a JWE without any progress so far. The Web service response comes in the following format: aaaa.bbbb.cccc.dddd.eeee

  • a: The JWE header, it comes with these algorithm and encryption methods: {"alg":"A256KW","enc":"A128CBC-HS256"}
  • b: The key
  • c: iv
  • d: payload
  • e: Authenticated Tag

A made a simple code to try and break the encryption, but i couldn't get past the setup stage, because it is always returning -1. The documentation says it needs to be a 0 for a successful atempt. Here is the pseudo code:

myPrivateKey = "..." 

responseKey = "..."
responseIv = "..."

responseKey = base64UrlToBase64(responseKey)
responseIv = base64UrlToBase64(responseIv)

pkba = CreateObject("roByteArray")
ivba = CreateObject("roByteArray")

pkba.fromBase64String(responseKey)
ivba.fromBase64String(responseIv)

cipher = CreateObject("roEVPcipher")
result = cipher.setup(false, "aes-256", UCase(pkba.toHexString()), UCase(ivba.toHexString(), 0)

print result

//it always shows -1

Does anyone have a hint about what i'm doing wrong and what could i do to try and get around this problem? If needed i can provide real values for testing

 

  • In the end, i decided to implement the key unwrap algorithm manually, and it was able to correctly decrypt the jwe. Here is the full solution: 

    jwe =
    "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.EoOZqNOs89BXG4KUq8VY4MuMw3iRjPvO2OfgTCXaWDV7CiOrT-RDrA.EEblXQ90Va5dRM2GagfX1A.2Cr-DulZOR68eZsewEfSAczf82EbPHVwQDhuSUA6KAihDZ1JZoHSExrhnMl1PqoEHyHe5c8oxty7MWACp4PC0Dlt7KqIyERfB0LURk7H_Btm25V0NqlghiTVd0YPmkiCuiEUduPMWGeUyJWJ3GcvYUxDNJKvc6Vm0cZbpiRnn-Se-vEDZQP9WNiHLSvrvjPlbk_m0rq5-_4rHlNwN4ubDnqJQv6PWvuE2gzxs6G2E5KYULClhcHyZOjyIBKO9sNZI0RTff9MfRQ2O6SLn9hBtLOFbf43P3693wBM2CzS8cVOJokH_jn22TD-og4nDPaDfzTcmR4379TW__0S-ZE1vVLMM5acZ2l-Its_St5Mu5-krKThJjW2tN8PS5h7pFiRWVQmSAeTTcsZidwh-ffIXikDkSa61ChADfaUx86yee438idA2RIR50ONSuoiBoD8Vpom4cHhvqiVY5CEqoVsDnMsN1lPXEBWthX9Iz0KJTg2SlTMQDy8vGZEu9HQTZZHiuLCNLcQN4OFKot0jvHLaFs61lOW7_nWX_BKI626WecF3SLsqOEPSaHXWKHU6oxbwG7VF3Kjhk9bGB61bkitRb6ijJopD1R3tUpFk4t3pv8P6DXlC1b0t_MO1F1ubiiHSL60eCZbXcaSM9SJ92YzXy0xZOp_mLfTL7yunzanYcB0mhwQeisXLwYlu_xLm8_OS1DZ3RZFlFNiv0ibf9vuc-Br6RhFbG3ICbCqwqWy7uMhe4_5Eg4FcS0gxpqaMuW8QoyNylP6HEDOvXedGhlCnACaGAHxhToygREI-jQuDza3vf5bBXI5E8kle5j2N5wmM5OXxGruHaOiKuuZYZFsFLhPROQU6xnS8TxaLUMcay4xAygiAfIch5VFm7jmVRXiasj-ZhBmGb4dg8PHuAxBxCjWy7H6BRFaFCz6-qoTCviKsujFPlZL50vW5qfBWymkFaOd1p9R3qB7Tmd5YfqdlK6hBogfwIFxm6ZUfP1mFVYDumXBgmc3ZLWAxfRx3kVbS3_vdZkpBhDqznQR5EO1gwmCNeWQa1s9Lm2w6oYBXA7X9WFOW2ubWTqUG-vpqcYWBaLgWwlWu_wUphdzBtduoNq-vRe1ms0OGzXzn8P78eGsKZ-C5KOFgpc8APhbm9SJPaXyv3ZsLImYELRPSLJob5wCLnAgbz6zvLfODCH3LasulgqriEw3i1nnmEgeyNSbrw-Z3KAAd4uPyObQrTeOcnKGQ2NUGudST9P7kjglpTLiTGsIs_QED2kaTyFVgLoXk0LLHKjANO0EtS9Ncb_5zJQdbu_IaG4b6PHdnMkzYHRCA4qHk66dcUlfzCcbHRn8Wrft8aN99Xv7n6zg0mbSqT-c2rgInslxCmkoDr3ZzpohADi9M_-6tmzLPw1FUl1aWBG6nflYq42cjAAz4GdvVaNh-Z8WHfsPZCeziEhZeBeiI-oj2RJBQcAQ-KvgxxJjQ_vBxclMCh4M9EZWzZ0d7t2WoqGHag24_pl9o30XppUhzRtl0z9evzg1d8-VsQT-KKgQK75E4Vn0KsLUBpU2z8_QyB-QNSzzSd4P3JRVOpxiPMRMC_vHRgvy1Td2k6RO9rK2sQ5Y7FhtYk1IiFPkMVZxpCxqnffB80vncWkxOhGmbXtP1_1vitLEXHX7RKIKefee3RVUo2jdDBrC2Ksfrd6fwiFKUQInuC_5PkOvz610PwwauAFGAOq5nNtS1hPh-LvdFVnf-8zB0LgSSm6tOVIbMsLyidF3D7gUiddv7sX0J07WIkZI8H_tQJAHo-LNHA3JPmGT8w7cI-XWKo-q7hMSO5Kt8GLJ4G_b7yYDcS3chnEDWpAMaN3mas6qq4JoymvHjYtCE5tuox8gBzgzlNhmXYzbyBiD4H5MlvkHya3I_TeVinl09RaNXi-sYoNpsisDobDn1QQBI2vjMull1lTwIJJlfekeKFhWzZl1NdacJwMapvopewpLs3VQmpAaRVu35Q4zpJXG3K57bVYKO8DIME-KyD1WvgS0DlsQVIomCKFUlWgOd68ITMOxe3jEmk_0ndDfVObhtFj-bVby2NbvGw2004o4nEF8-pwT0jCmcfkz8uytsAOkqfXIhzwxI8A-gK4oV4CpEbBpiEqa8K8hT9EnDy1xnMZssy-CiQbL9CP9iJIoidhq5y8yKcW0FTfZauOooagI6mRvx03TVJvjX-umAD0JQSEFE0hWSD5ELhFNF2M-GQ9gSQsBKhzwdwWS4_h_fc-JIXMiZOd01Si2TI1rfqjtENt8w3TEg4ATqsRsm1qUiA9rzTxleP9OqwHwEDqpOwsIkWd8xX0eyoeQBEMJS02UHNNLdm87IoW4ZJxkiGkde6wPj0n-K8gxL3_bl_dygptuVJ5J4Qx5JXjOHEhA451QtLKz0SXTR0wvjp6zKFYsObEV-bN1Q5gOJptzUMh98huNhRCANbx6rMugAArKTRDDygGqIbnMEeT84ap3dhSw0h-RV_QSpHvYxg9PFSVsl1X1F5ZcL9bkssR31qTAdXiVJONg_gtmRoZFDjAfOjsiJChT8kbGhNP-JqFAiiYH4ilwT-zT9oEPkTwHveV638ote3_NhvOPKsVxiY84jV0AYtCUF82eYTjrqe1knSt4k3xZ_chR34g5oRVzERdhA_wfibk0tpc6rhKQEXXlKXl1-Boh_Hw5KLbWGMEcIXW6ms4XAqMHjhsIeX4livD8l2C3bLmGzLwwa1h19g2ZUJr9HY3T0WrG23alMkH20DtFn5WGgZmuXyJSzgVsiUajw0AZKK9MREIX90JE_UcSnk9uXogmeE88P65H05Nw6AAezc8b-_ym8X9-0xp7W8hLZPIQ1S2-cZRIvJMBvVVoYLD7VGBqjkugc58SnoETXX4QNAooH8uBIiVC3vDM0zo2OzBIRVUSG0SCvKMG6Q-w3fnSWcnHGVfHuURJV2vFJypwjkF0-MzH9VD2ydbcOcD49KkSK6ILdtMMeQ4YmWi5jTpoJyDzo5u8FURQ0vAK3X0lG8kpqI-ib8GhgL89Or64XktayAcD-pNDWXQ87siPeqYFxQOm_WvWDOLo4RTk9aPsXdLj-4728J1sJHSKmN3PEFINMO2F3ldhUlazfhFv6Z9-GfFLX4fWSLGeH1wCqYOOdaiJWulIQCsxzP3QYyM5LdBynC2N4qSsIGndpiO0trC1BWrRawmefh3JbvzEU5poLyYYbImKU6fjshet4AwEyYbl4NFISD3OXfYdnIvRIpxosXJhWnQjgBRHlRMa7RcVD_S65eop49giaGxGpy7_-LkCw2rQilwPy7y9sZBqBJw6okZYvPfodxu5mPjqnghJx9t3pbi8eWMf1JAttfuUZd1anqHZFg3jRmwR2qX8gmmIk_6lLzwLNkVAsAnTaUlPnQzdLQBPsUpp3WJv0v-VpJE0OpoEkzuhHPXzIkEioxLhXJsjOXz1Vc5wohP-uWsT9atjNhe8lT0j_YV5O70h_qVjG39uZjNOJvXlH603tU1O-vo6PJVe3nYnxOw5GfxQ1bUCo2G0DxrjlQ.ubwItxMBO1_Qsnf6s5VRJQ"
    
    key = "q2nj7sc1m30as24m4nf1mnv[]q981.,a"
    
    result = jwe.split(".")
    
    resultingKey = aesKeyUnwrap(key, result[1])
    
    encodedIvBa = CreateObject("roByteArray")
    encodedIv = base64UrlToBase64(result[2])
    encodedIvBa.fromBase64String(encodedIv)
    
    payloadBa = CreateObject("roByteArray")
    payload = base64UrlToBase64(result[3])
    payloadBa.fromBase64String(payload)
    
    cipher = CreateObject("roEvpCipher")
    cipher.setup(false, "aes-128-cbc", resultingKey, encodedIvBa.toHexString(), 0)
    
    text = cipher.process(payloadBa)
    
    ? text.toAsciiString()

    Im not going to explain how the algorithm works. You can read about it in the RFC 3394. Below is the implementation for aes key unwrap

    function xor(p as object,q as object) as object
        pSize = p.count()
        qSize = q.count()
    
        counter = 0
        for i = pSize - 1 to pSize - qSize step -1
            p[i] = intxor(p[i], q[counter])
            counter ++
        end for
        return p
    end function
    
    function intxor(p as integer, q as integer) as integer
        bitwiseAnd = p and q
        bitwiseOr = p or q
        return bitwiseOr and not bitwiseAnd
    end function
    
    function unWrapCore(privateKey as object, a as object, r as object)
    
        cipher = CreateObject("roEvpCipher")
        dd = cipher.setup(false, "aes-256-ecb", privateKey.toHexString(), "", 0)
    
        if dd = 0
            n = r.count()
            for j = 5 to 0 step -1:
                for i = n - 1 to 0 step -1:
                    x = ((n * j) + i + 1)
                    ba = CreateObject("roByteArray")
                    ba.push(x)
                    y = xor(a, ba)
        
                    y.append(r[i])
        
                    b = cipher.update(y)
        
                    if b<> invalid
                        a = CreateObject("roByteArray")
                    
                        for l = 0 to 7 step 1:
                            a.push(b[l])
                        end for
            
                        r[i] = CreateObject("roByteArray")
                        for o = 8 to b.count() - 1 step 1:
                            r[i].push(b[o])
                        end for
            
                    end if
                   
                end for
            end for
        
            newArray = CreateObject("roByteArray")
    
            for each item in r
                newArray.append(item)
            end for
    
            result = CreateObject("roByteArray")
    
            for t = 16 to newArray.count() -1 step 1:
                result.push(newArray[t])
                
            end for
    
            return result.toHexString()
        end if
    
        return -1
        
    
    end function
    
    function aesKeyUnwrap(privateKey as string, encodedKey as string)
    
        privateKeyBa = CreateObject("roByteArray")
        privateKeyBa.fromAsciiString(privateKey)
    
        encodedKeyBa = CreateObject("roByteArray")
        encodedKey = base64UrlToBase64(encodedKey)
        encodedKeyBa.fromBase64String(encodedKey)
    
        array = []
    
        for i = 0 to encodedKeyBa.count() step 8
            ba = CreateObject("roByteArray")
            for j = i to i + 7 step 1
                ba.push(encodedKeyBa[j])
            end for
            array.push(ba)
        end for
    
        array.pop()
        a = array.shift()
        ba = CreateObject("roByteArray")
        ba.push(24)
    
        return unWrapCore(privateKeyBa, a, array)
    
    end function
  • renojim's avatar
    renojim
    Community Streaming Expert

    It's been a while since I've used the EVP stuff and I recall having difficulties when I first tried.  I would suspect something with the hex strings for the key and IV.  If they're not exactly the correct number of characters it won't work (e.g., "0" won't work for an IV that should be 8 hex values - "0000000000000000").  You might want to start by setting up an encoder with example key and IV, encode a string, and then decode it leaving out all the ba and base64 stuff.

    If you want to PM me some real data I'll see if I can rattle my brain into remembering how it's done.

    • danielFav's avatar
      danielFav
      Channel Surfer

      I've managed to get a real JWE example we will be using . This is just automated testing data and does not contain sensitive information. Below is an example of a JWE:

      eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.j1RBCCV2E6YRUD_RI1oPtQLNDjg2tWELjNit29PzUrSvwx060YcKeg.OJecPTIfpng9qZCIJh2-3g.AOYr-AJ_AqDgx7HjzNFPaiDJjhmQCMkwDX31bQO1Rh_Ui2MI3PECVow28Ym2RrUG6jxrUeSGA1Q6G_RdVk1P9-Op4NIFm26XgYx5dq57LXY9-9Be0WaRGq2HvXNFeb-LpmC1UjQ2BDReTwHynOrg98ETjz8j1XQunHSlmdhT1A42_PRsUTo1yyi7kRtIshPSXUlfxZA0hFubnJf2KQWy_hU42b01UUcBnuPEza6Ne_Y2Et-_qmGNRQDDj34R0afXRiByWJJ8YoUNAZrmVydvFEASYMIvB7HQE1dB8QfFhabfipA875bDi98ix5jdPprELykBnwG9KpOZS1RS1hfXBjKuZDuu7PKQdDbDmxe0CtwdA6mWkohDpqX1UeRdVqmcQZs906z0bQ3CvaOdHCx4dSMDRJIflIwsfw3PkzENSEpHLfOJVdi-CabE0iBeGjgcuffkEDqEf2llSj-wqiIfzwn9kyWH_8_rVWwWX6TTWSbaGIMWcxGgRogVKiusYCqqYFtQmtQc6SwkJOARkHQkscZGV9YE1wCQNl_ZtgD6QfRJRHYRU_LOMCb3HKrFyeyIlSfjFMDS5vY1nP5xWOZLqU_ELLMOOEeyBCWzJMwH-t-NMpg8NlUnK5mrcGlIVGmlSdQd1rh1GyfZGQHW6AmQvNSDOMydX-AM_r1ZkuLt3q5yvu6iwKphbZwFz4b9-CgxOGAKH2MI7pAeJHXbaklDisSNZyMJ1MIkyl8sxBQpe9DZRG1WXN1ySYJ4obOaqNahX9tz8-5a3uWscinI016gOQK3zG1dUx43ta3-Q6E7QOlV1SWYSAmrR-bTLT4q2BafWYuhNsR-9_9SYkAHUEFO6hu9yXqvkpbLV7HBz4m-nMQ-iBmrnhSdm1UZRxZQjJOpcaLEEscfD5h6FuliPCE0fdxMge6QM9emsGczonQ2CD2zlgMSHg_PX1VQ98wQy0H6tZBEHiReNTU7_ezUg_YR-ocfgvcQj2h0orvy62gNLL_4Ro2sU8dft0KRVeLF4qInrcnS2gUEs0Gjv-y3zFZ30XLPIGUxe7W5dkB4v1w7V3Ogvw6X8p8je3oMUyUXFlUr6e-TUf-C8PBFXazHaLDzyRCvqXHfFihaPx0bMGQ4V3H3RvmMG4I6wejoOXXIHzp8gmFLmUqV7j4P6Qop_h96icaT0SBXFLt_BZVcUXbuNrkeOEeDvs2w9DE14vTAja-qMn6VMEpZ1h573xtt6o-BIM9hJIdHxfH6qTFtFPINR5lYBfpek2Ljw6pFO3_yXPz1Vgqv2snX8cZ-SSykeefeoMTuyZVpoSEp0VJm6B89oPG6qOjnh6TSWJGvCAQo6Op-fcKhErMAwZc5DBcm1lwTny8K9HUmShiXYvnPr8FAmPxbYOOiVUJkfAHeyqoQ5F06PHjVqaslyuAS4O-gHuYnlhk1kMe32xZKkuKm5eBdxDSAQzASO9exhNQr-l8g4IXzOALZ_pzRJTOcCPsnhgH7XE-N8tyfKjrFMcHCDQvmpDVMf4UIAJUcULNPhVCx7B3rbhrd0lHnDLetwUDh_ytMq6NazQe8sIWDBJBLjB_IGrtUba532gFzMI3cZ0Vxr_2O_Mm2lWzRW9AmE4m3yP6VSeGBo8jCGlsqacKMM2RMalcbQuU8qD8e2XSJl_D2jigzpSYIQTYm0hOfTA7-g6I6Yfp1hwC5q_gU1RUBgxzA8WMzIEVxFqGWr-0Q8JQ_xiCsHE5KUMwnCWL1m8SSDFayw97bAo8o4vNr_wdUBZE9EQ4NU0FGYKyaSHEnSghNgB-RI7t723V7xaiyg78xwkcX39XQBZlvg7gp68_Cq34lK04gBnboZTREYGgNNak5O0V1D-077JXlaNOKqHcPGLcnAQyk_gweT1_U9oOrcOf1IVRXr6ke4Kzgc-KypowR80Cui5KY4L9rxlTPBTY15KxMaNo_uo0FP46WobeYzALcZuw81duDfXgJbqESyjmwx4R44orAr3xU2vjNBIgvfzWbwOW0_93Kr8AvRyqYrsJZfI9XDTAnmklKW-1QNgYljOiBESLCAY1_ueiCYYgT70jn9ADhmw6IFOrcGAFoKnwtgAhnHuc-_gg0ZpghJwhmPYbs5ZQhFthC6U55JixnkroDOfxTnxoGion4dX7d1svAiX5IfeuvoiXHJxNAysj4GuVgQctLsVwVUuDMvznTKwIFfmUl3AKuq1QPZz2EhWfpab632JoGWiVQvCVBwLlW3rBnN1AvJlMPpOydTie2Q1AC7d69QYEGpZQxWCk9H3TgaXh9Jf5ESGqexQEk8UjLALHDdAwZ8Lo_vnaQzIs0vAVDr6EJkjTQ1K9wVAMV5kEgSomAoIYwd4tw4K-hvRjrV38MEbv92DeRSVKsZIcjmfuxao-ymJJoKwD_cHxeH_REKIPyXTOknveP-d0pimW17mQO_-dYqZnPJZYhlVl61yDPbrmNaVcH4VaaWU8vqtQkydYiBTUd_HoF55xGZtGScc7FMOnbpVrGu7mYN6ShZH7FEZ2BY4pw_Iu2NrHxA99zUtSZTIeUgg4AUscGJH8V9C46jch7G1cGGHJZ0Qf2Dms97F2eOlGzVjMo1BuO15KL9gFXesA6_Ezk0KhLQP-UQZRvfwrmuxS0D0HhxHzJx4NunuZXuKTTM3OFHNxp0NoMoHw8dMXvfUWu2WcSs5ibz7KC_ioY9tew8-IyBn2ADrAlr5Eyb5StjD-IZn_xyVCP0X5rtxRpzGXStwWoOsi7D6k4HaqJFMlS5hv6T22OeuXFo09v5JC27225XpykBWdx8ondYIve72eCL6xlq9XguHUOPDNs9PD9zvFbDzH7YDpRUXOYwGNS3fP2zOEMDNtFL1nzv5wzUaWkyNt1q1ocBbYIq_kK-3iCpoYSh3ecQJdCKI6OTBtt5Ec8TaIguI2Ir7RY6L5JZXbhZBzZ6Uc430Bwba4I_c_DhewJzyj_GeNKjNNy7R5kCCWjyZ30l6rdshXl1kthne7R1TUXA9TKwKJhTiilSWMt0k31iCaZuZDVnjDHE6JN-yCIzI17qB5MjgrzAOwATGRbkvTtkF_X2lNjF1tPs9lSH0AVOP8UW50gL-0MNGimnK-qH6Lj4dD7Mxk4V-PeOyDuNxesKnRXohgaJcq4Hj5BtcIEw_0mQ5yAHBgWkPgEusAPh6c66F90ugBTrYdSbgbKAoQI1irZ4Fj8sAgbqkzRKNP-ZE1ChlfmePCPMohANWWT6z3L8jdLuULRTpwNtHOZ3H0O2qKXJZiu2_QdBnrl9P25FMHuZ3V7ZntFMod32gtIeXn-VpA9v-A2feZv6KXlsc7GakEroWpnTpR_2kLlIXfMVdSYEbzsyeXclQuzAOJqbX-tCwM_Y4_t_J30yh3pgyRRQR0St-IG54bA663x01BDg3XWaBF7VTcj3ztQOFUKeVKRy5ta3qmpREcVYo2jzIqbwPx0InvGzJT2SMgk2L4mMgMiFZIViMzOc7ywjTCBNruDjlMVho7jZgMt_lfFSblSPdr0Z2Vt-nhTNA_QUikWoJxFfAmZ2w.fsvwXsfwYGiuNLsoMTB-Vw

      The private key is the following: 

      q2nj7sc1m30as24m4nf1mnv[]q981.,a 

      A corresponding code example would be something like:

      privateKey = "q2nj7sc1m30as24m4nf1mnv[]q981.,a"
      
      jweKey = "j1RBCCV2E6YRUD_RI1oPtQLNDjg2tWELjNit29PzUrSvwx060YcKeg"
      jweIv = "OJecPTIfpng9qZCIJh2-3g"

      What is really bugging me is that everything has different bytes. The private key has 32, while jweKey 40 and jweIv 22. From what you're saying they should be the same. Should i complete the iv with empty zeros until it reach 32 or something like that?

      • renojim's avatar
        renojim
        Community Streaming Expert

        I'm not saying that they should all be the same.  I'd have to look up what the key and IV lengths should be, but they can certainly be different and depend on what cipher you're using.