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