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 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
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.
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?
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.
I've tried to brute force every possible evp in the roEvpCipher object, but none was able to decrypt the payload. First, i try to decrypt the simmetric key passing the private key and initialization vector, forming a list of every decrypted simmetric key. And then, with every assembled key i try to decrypt the payload. Here is the code:
evpList = ["bf-cbc","bf","bf-cfb","bf-ecb","bf-ofb","des-cbc","des","des-cfb","des-ecb","des-ofb","des-ede-cbc",
"des-ede","des-ede-cfb","des-ede-ofb","des-ede3-cbc","des-ede3","des3","des-ede3-cfb","des-ede3-ofb","desx","desx-cbc", "aes-256-cbc", "aes-256", "aes-256-cfb", "aes-256-cfb1", "aes-256-cfb8", "aes-256-ecb", "aes-256-ofb","aes-192-cbc", "aes-192", "aes-192-cfb", "aes-192-cfb1", "aes-192-cfb8", "aes-192-ecb", "aes-192-ofb","aes-128-cbc", "aes-128", "aes-128-cfb", "aes-128-cfb1", "aes-128-cfb8", "aes-128-ecb", "aes-128-ofb"]
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"
jweArray = jwe.split(".")
decryptedKeyList = []
for each evp in evpList
privateKeyBa = CreateObject("roByteArray")
enkba = CreateObject("roByteArray")
ivba = CreateObject("roByteArray")
privateKey = "q2nj7sc1m30as24m4nf1mnv[]q981.,a"
enk = jweArray[1]
iv = jweArray[2]
enk = base64UrlToBase64(enk)
iv = base64UrlToBase64(iv)
privateKeyBa.fromAsciiString(privateKey)
enkba.fromBase64String(enk)
ivba.fromBase64String(iv)
pkHex = uCase(privateKeyBa.toHexString())
ivHex = uCase(ivba.toHexString())
cipher = CreateObject("roEvpCipher")
setupResult = cipher.setup(false, evp,pkHex, ivHex, 0)
if setupResult = 0
decryptedKey = cipher.process(enkba)
if(decryptedKey <> invalid)
decryptedKeyList.push(decryptedKey)
end if
end if
end for
for each key in decryptedKeyList
for each evp in evpList
payloadBa = CreateObject("roByteArray")
newKey = CreateObject("roByteArray")
newKey.append(key)
payload = jweArray[3]
payload = lCase(base64UrlToBase64(payload))
payloadBa.fromBase64String(payload)
cipher = CreateObject("roEvpCipher")
setupResult = cipher.setup(false, evp, uCase(newKey.toHexString()), "", 0)
if setupResult = 0
decryptedPayload = cipher.process(payloadBa)
if decryptedPayload <> invalid
? decryptedPayload.toAsciiString()
end if
end if
end for
end for
I've tried to decrypt the jwe using a public lib like jose and it was able to perform the job without a sweat. Am i missing something? Has anyone done something similar before? Maybe roku does not have support for this key unwrap algorithm. If that's the case, building one from scratch would be too much troublesome. Maybe the solution would be to request for a new endpoint to the backend team that uses an algorithm roku supports.
I don't know anything about JWE, so I don't think I can be much help. Have you tried the steps you're trying to perform on your Roku device using something like openssl instead of a package that understands JWE?
Yea, i made a very simple code using a lib called cryptography, which is a python wrapper for OpenSSL, pretty much the same as roEVPcipher, and it was able to get results. Here is the code if interested:
from cryptography.hazmat.primitives.keywrap import aes_key_unwrap
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import base64
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"
key = "q2nj7sc1m30as24m4nf1mnv[]q981.,a"
array = jwe.split('.')
#converts key from base64url to base64, then converts to byte array
simmetricKey = base64.urlsafe_b64decode(array[1] + '=' * (4 - len(array[1]) % 4))
#unwraps the simmetric key
result = aes_key_unwrap(str.encode(key), simmetricKey)
#last 16 bits of key
newKey = result[-16:]
#converts iv from base64url to base64, then converts to byte array
iv = modes.CBC(base64.urlsafe_b64decode(array[2] + '=' * (4 - len(array[2]) % 4)))
#creates a Cipher instance, equivalent of roEVPcipher
cipher = Cipher(algorithms.AES128(newKey), iv)
decryptor = cipher.decryptor()
#converts payload from base64url to base64, then converts to byte array
payload = base64.urlsafe_b64decode(array[3] + '=' * (4 - len(array[3]) % 4))
#equivalent of roEvp .process method
data = decryptor.update(payload)
result = data.decode('utf-8')
print(result)
What is seems to me is that roku is missing a key unwrap algorithm, or i can't find it in the docs. Does anyone know how to solve this ? I think it would be too much to write this algorithm with native brightscript if there is already a wrapper for OpenSSL.
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
Nice! I figured you'd have to go that route. Thanks for sharing!