{"id":2064,"date":"2024-11-23T00:19:27","date_gmt":"2024-11-22T16:19:27","guid":{"rendered":"https:\/\/blog.quantoyo.com\/?p=2064"},"modified":"2024-11-23T00:19:27","modified_gmt":"2024-11-22T16:19:27","slug":"aes-%e4%bd%bf%e7%94%a8-c-%e5%8f%af%e7%9b%b4%e6%8e%a5%e4%bd%bf%e7%94%a8%e7%89%88%e6%9c%ac","status":"publish","type":"post","link":"https:\/\/blog.quantoyo.com\/?p=2064","title":{"rendered":"AES \u4f7f\u7528 C++ \u53ef\u76f4\u63a5\u4f7f\u7528\u7248\u672c"},"content":{"rendered":"\n<p>\u6b64\u7248\u672c\u4f7f\u7528 AES\/ECB\/PKCS5Padding\uff0c\u6703\u5c07\u5b57\u4e32\u52a0\u5bc6\u5f8c\u8f38\u51fa base64 \uff0c\u89e3\u5bc6\u5247\u662f\u5c07base64\u5b57\u4e32\u89e3\u5bc6\u5f8c\u8f38\u51fa\u539f\u5b57\u4e32<\/p>\n\n\n\n<p>\u9700\u8981\u7684\u4eba\u8acb\u76f4\u63a5\u8907\u88fd\u4f7f\u7528<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>\u7a0b\u5f0f\u78bc<\/p>\n\n\n\n<pre class=\"wp-block-code has-cyan-bluish-gray-background-color has-background has-small-font-size\"><code>#include &lt;iostream>\n#include &lt;iomanip> \/\/ \u70ba\u4e86\u4f7f\u7528 std::setw \u548c std::setfill\nusing namespace std;\n\n#define BYTE unsigned char\n#define AES_BLOCK_SIZE 16\n\t\n\/******************************************************************************\/\n\t\n\/\/ The following lookup tables and functions are for internal use only!\nBYTE AES_Sbox&#91;] = {99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,\n\t\t118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,\n\t\t147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,\n\t\t7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,\n\t\t47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,\n\t\t251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,\n\t\t188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,\n\t\t100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,\n\t\t50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,\n\t\t78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,\n\t\t116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,\n\t\t158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,\n\t\t137,13,191,230,66,104,65,153,45,15,176,84,187,22};\n\t\nBYTE AES_ShiftRowTab&#91;] = {0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11};\n\t\nBYTE AES_Sbox_Inv&#91;256];\nBYTE AES_ShiftRowTab_Inv&#91;16];\nBYTE AES_xtime&#91;256];\nbool initialized = false;\n\n\nvoid printBytes(BYTE b&#91;], int len) {\n    int i;\n    for (i=0; i&lt;len; i++)\n        std::cout &lt;&lt; std::hex &lt;&lt; std::setw(2) &lt;&lt; std::setfill('0')&lt;&lt;(int)b&#91;i] &lt;&lt; \" \";\n    std::cout &lt;&lt; std::endl;\n}\n\nvoid AES_SubBytes(BYTE state&#91;], BYTE sbox&#91;]) {\n\t\tint i;\n\t\tfor(i = 0; i &lt; 16; i++)\n\t\t\t\tstate&#91;i] = sbox&#91;state&#91;i]];\n}\n\t\nvoid AES_AddRoundKey(BYTE state&#91;], BYTE rkey&#91;]) {\n\t\tint i;\n\t\tfor(i = 0; i &lt; 16; i++)\n\t\t\t\tstate&#91;i] ^= rkey&#91;i];\n}\n\t\nvoid AES_ShiftRows(BYTE state&#91;], BYTE shifttab&#91;]) {\n\t\tBYTE h&#91;16];\n\t\tmemcpy(h, state, 16);\n\t\tint i;\n\t\tfor(i = 0; i &lt; 16; i++)\n\t\t\t\tstate&#91;i] = h&#91;shifttab&#91;i]];\n}\n\t\nvoid AES_MixColumns(BYTE state&#91;]) {\n\tint i;\n\tfor(i = 0; i &lt; 16; i += 4) {\n\t\tBYTE s0 = state&#91;i + 0], s1 = state&#91;i + 1];\n\t\tBYTE s2 = state&#91;i + 2], s3 = state&#91;i + 3];\n\t\tBYTE h = s0 ^ s1 ^ s2 ^ s3;\n\t\tstate&#91;i + 0] ^= h ^ AES_xtime&#91;s0 ^ s1];\n\t\tstate&#91;i + 1] ^= h ^ AES_xtime&#91;s1 ^ s2];\n\t\tstate&#91;i + 2] ^= h ^ AES_xtime&#91;s2 ^ s3];\n\t\tstate&#91;i + 3] ^= h ^ AES_xtime&#91;s3 ^ s0];\n\t}\n}\n\t\nvoid AES_MixColumns_Inv(BYTE state&#91;]) {\n\tint i;\n\tfor(i = 0; i &lt; 16; i += 4) {\n\t\tBYTE s0 = state&#91;i + 0], s1 = state&#91;i + 1];\n\t\tBYTE s2 = state&#91;i + 2], s3 = state&#91;i + 3];\n\t\tBYTE h = s0 ^ s1 ^ s2 ^ s3;\n\t\tBYTE xh = AES_xtime&#91;h];\n\t\tBYTE h1 = AES_xtime&#91;AES_xtime&#91;xh ^ s0 ^ s2]] ^ h;\n\t\tBYTE h2 = AES_xtime&#91;AES_xtime&#91;xh ^ s1 ^ s3]] ^ h;\n\t\tstate&#91;i + 0] ^= h1 ^ AES_xtime&#91;s0 ^ s1];\n\t\tstate&#91;i + 1] ^= h2 ^ AES_xtime&#91;s1 ^ s2];\n\t\tstate&#91;i + 2] ^= h1 ^ AES_xtime&#91;s2 ^ s3];\n\t\tstate&#91;i + 3] ^= h2 ^ AES_xtime&#91;s3 ^ s0];\n\t}\n}\n\nvoid AES_Init() {\n    if(initialized){\n        return;\n    }\n    int i;\n    for(i = 0; i &lt; 256; i++)\n            AES_Sbox_Inv&#91;AES_Sbox&#91;i]] = i;\n        \n    for(i = 0; i &lt; 16; i++)\n            AES_ShiftRowTab_Inv&#91;AES_ShiftRowTab&#91;i]] = i;\n\n    for(i = 0; i &lt; 128; i++) {\n            AES_xtime&#91;i] = i &lt;&lt; 1;\n            AES_xtime&#91;128 + i] = (i &lt;&lt; 1) ^ 0x1b;\n    }\n    initialized = true;\n}\n\nvoid AES_Done() {}\n\t\t\nint AES_ExpandKey(BYTE key&#91;], int keyLen) {\n\t\tint kl = keyLen, ks, Rcon = 1, i, j;\n\t\tBYTE temp&#91;4], temp2&#91;4];\n\t\tswitch (kl) {\n\t\t\t\tcase 16: ks = 16 * (10 + 1); break;\n\t\t\t\tcase 24: ks = 16 * (12 + 1); break;\n\t\t\t\tcase 32: ks = 16 * (14 + 1); break;\n\t\t\t\tdefault: \n\t\t\t\t\t\tprintf(\"AES_ExpandKey: Only key lengths of 16, 24 or 32 bytes allowed!\");\n\t\t}\n\t\tfor(i = kl; i &lt; ks; i += 4) {\n\t\t\t\tmemcpy(temp, &amp;key&#91;i-4], 4);\n\t\t\t\tif (i % kl == 0) {\n\t\t\t\t\t\ttemp2&#91;0] = AES_Sbox&#91;temp&#91;1]] ^ Rcon;\n\t\t\t\t\t\ttemp2&#91;1] = AES_Sbox&#91;temp&#91;2]];\n\t\t\t\t\t\ttemp2&#91;2] = AES_Sbox&#91;temp&#91;3]];\n\t\t\t\t\t\ttemp2&#91;3] = AES_Sbox&#91;temp&#91;0]];\n\t\t\t\t\t\tmemcpy(temp, temp2, 4);\n\t\t\t\t\t\tif ((Rcon &lt;&lt;= 1) >= 256)\n\t\t\t\t\t\t\t\tRcon ^= 0x11b;\n\t\t\t\t}\n\t\t\t\telse if ((kl > 24) &amp;&amp; (i % kl == 16)) {\n\t\t\t\t\t\ttemp2&#91;0] = AES_Sbox&#91;temp&#91;0]];\n\t\t\t\t\t\ttemp2&#91;1] = AES_Sbox&#91;temp&#91;1]];\n\t\t\t\t\t\ttemp2&#91;2] = AES_Sbox&#91;temp&#91;2]];\n\t\t\t\t\t\ttemp2&#91;3] = AES_Sbox&#91;temp&#91;3]];\n\t\t\t\t\t\tmemcpy(temp, temp2, 4);\n\t\t\t\t}\n\t\t\t\tfor(j = 0; j &lt; 4; j++)\n\t\t\t\t\t\tkey&#91;i + j] = key&#91;i + j - kl] ^ temp&#91;j];\n\t\t}\n\t\treturn ks;\n}\n\t\n\/\/ AES_Encrypt: encrypt the 16 byte array 'block' with the previously expanded key 'key'.\nvoid AES_Encrypt(BYTE block&#91;], BYTE key&#91;], int keyLen) {\n\t\tint l = keyLen, i;\n\t\tAES_AddRoundKey(block, &amp;key&#91;0]);\n\t\tfor(i = 16; i &lt; l - 16; i += 16) {\n\t\t\tAES_SubBytes(block, AES_Sbox);\n\t\t\tAES_ShiftRows(block, AES_ShiftRowTab);\n\t\t\tAES_MixColumns(block);\n\t\t\tAES_AddRoundKey(block, &amp;key&#91;i]);\n\t\t}\n\t\tAES_SubBytes(block, AES_Sbox);\n\t\tAES_ShiftRows(block, AES_ShiftRowTab);\n\t\tAES_AddRoundKey(block, &amp;key&#91;i]);\n}\n\t\n\/\/ AES_Decrypt: decrypt the 16 byte array 'block' with the previously expanded key 'key'.\nvoid AES_Decrypt(BYTE block&#91;], BYTE key&#91;], int keyLen) {\n\t\tint l = keyLen, i;\n\t\tAES_AddRoundKey(block, &amp;key&#91;l - 16]);\n\t\tAES_ShiftRows(block, AES_ShiftRowTab_Inv);\n\t\tAES_SubBytes(block, AES_Sbox_Inv);\n\t\tfor(i = l - 32; i >= 16; i -= 16) {\n\t\t\t\tAES_AddRoundKey(block, &amp;key&#91;i]);\n\t\t\t\tAES_MixColumns_Inv(block);\n\t\t\t\tAES_ShiftRows(block, AES_ShiftRowTab_Inv);\n\t\t\t\tAES_SubBytes(block, AES_Sbox_Inv);\n\t\t}\n\t\tAES_AddRoundKey(block, &amp;key&#91;0]);\n}\n\nvoid convertToByte(const uint8_t* key_bytes, BYTE* key, size_t size) {\n    for (size_t i = 0; i &lt; size; ++i) {\n        key&#91;i] = (key_bytes&#91;i]);  \/\/ \u5c07\u6bcf\u500b uint8_t \u503c\u8f49\u63db\u70ba bitset&lt;8>\n    }\n}\n\nstd::string base64_encode(const unsigned char* data, size_t len) {\n    static const char encoding_table&#91;] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/\";\n    std::string output;\n    size_t i = 0;\n\n    \/\/ \u6bcf3\u500b\u5b57\u7bc0\u8f49\u63db\u70ba4\u500b\u5b57\u5143\n    while (i &lt; len) {\n        uint32_t octet_a = i &lt; len ? data&#91;i++] : 0;\n        uint32_t octet_b = i &lt; len ? data&#91;i++] : 0;\n        uint32_t octet_c = i &lt; len ? data&#91;i++] : 0;\n\n        uint32_t triple = (octet_a &lt;&lt; 0x10) + (octet_b &lt;&lt; 0x08) + octet_c;\n\n        output.push_back(encoding_table&#91;(triple >> 3 * 6) &amp; 0x3F]);\n        output.push_back(encoding_table&#91;(triple >> 2 * 6) &amp; 0x3F]);\n        output.push_back(encoding_table&#91;(triple >> 1 * 6) &amp; 0x3F]);\n        output.push_back(encoding_table&#91;(triple >> 0 * 6) &amp; 0x3F]);\n    }\n\n    \/\/ \u6dfb\u52a0 '=' \u586b\u5145\n    size_t mod = len % 3;\n    if (mod > 0) {\n        for (size_t j = 0; j &lt; (3 - mod); ++j) {\n            output&#91;output.size() - 1 - j] = '=';\n        }\n    }\n\n    return output;\n}\n\nstd::vector&lt;uint8_t> base64_decode(const std::string&amp; encoded) {\n    const std::string BASE64_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/\";\n    std::vector&lt;uint8_t> decoded_data;\n\n    int val = 0, valb = -8;\n    for (unsigned char c : encoded) {\n        if (BASE64_CHARS.find(c) == std::string::npos) {\n            break;\n        }\n\n        val = (val &lt;&lt; 6) + BASE64_CHARS.find(c);\n        valb += 6;\n\n        if (valb >= 0) {\n            decoded_data.push_back((val >> valb) &amp; 0xFF);\n            valb -= 8;\n        }\n    }\n\n    return decoded_data;\n}\n\nstd::vector&lt;uint8_t> byteArrayToVector(const BYTE* key, size_t size) {\n    std::vector&lt;uint8_t> result;\n    result.reserve(size); \/\/ \u9810\u5148\u5206\u914d\u7a7a\u9593\u63d0\u9ad8\u6548\u7387\n    for (size_t i = 0; i &lt; size; ++i) {\n        result.push_back(static_cast&lt;uint8_t>(key&#91;i])); \/\/ \u63d0\u53d6 bitset \u7684\u503c\u4e26\u5b58\u5165 vector\n    }\n    return result;\n}\nvoid appendStateToVector(const BYTE state&#91;AES_BLOCK_SIZE], std::vector&lt;uint8_t>&amp; data) {\n    for (int i = 0; i &lt; AES_BLOCK_SIZE; ++i) {\n        data.push_back(static_cast&lt;uint8_t>(state&#91;i]));\n    }\n}\n\nvoid remove_padding(std::vector&lt;uint8_t>&amp; data) {\n    if (data.empty()) {\n        throw std::runtime_error(\"Data is empty, cannot remove padding.\");\n    }\n\n    \/\/ \u8b80\u53d6\u586b\u5145\u503c\n    uint8_t padding_value = data.back();\n    if(padding_value == 0){\n        padding_value = 16;\n    }\n\n    \/\/ \u9a57\u8b49\u586b\u5145\u503c\u662f\u5426\u5408\u7406\n    if (padding_value > data.size()) {\n        throw std::runtime_error(\"Invalid padding value.\");\n    }\n\n    \/\/ \u79fb\u9664\u586b\u5145\u503c\n    data.resize(data.size() - padding_value);\n}\n\nstd::string vectorToString(const std::vector&lt;uint8_t>&amp; data) {\n    return std::string(data.begin(), data.end());\n}\n\n\/\/ AES ECB \u6a21\u5f0f\u52a0\u5bc6\nstd::string aes_encrypt_ecb(const std::string text_to_encrypt, const std::string key_str) {\n\tBYTE key&#91;16 * (14 + 1)];\n    BYTE state&#91;AES_BLOCK_SIZE] = {0};\n\tint keyLen = 16;\n    size_t len = text_to_encrypt.size();\n    size_t padded_len = (len \/ AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;\n    std::vector&lt;uint8_t> output(padded_len, 0);\n    AES_Init();\n    \/\/\u8b80\u53d6\u91d1\u9470\n    memcpy(key, key_str.data(), AES_BLOCK_SIZE);\n\n\t\/\/ std::cout &lt;&lt; \"\u539f\u59cb\u91d1\u9470\uff1a\";\n    \/\/ printBytes(key, keyLen);\n\n\tint expandKeyLen = AES_ExpandKey(key, keyLen);\t\/\/4* 44 = 176\n    \/\/ std::cout &lt;&lt; \"\u5c55\u958b\u91d1\u9470\u9577\u5ea6\uff1a\" &lt;&lt; std::dec &lt;&lt; expandKeyLen&lt;&lt; std::endl;\n    \/\/ std::cout &lt;&lt; \"\u5c55\u958b\u91d1\u9470\uff1a\";\n    \/\/ printBytes(key, expandKeyLen);\n\n    \/\/ \u4f7f\u7528 std::vector \u66ff\u4ee3\u8b8a\u6578\u5927\u5c0f\u9663\u5217\n    std::vector&lt;uint8_t> input(padded_len, 0);\n    memcpy(input.data(), text_to_encrypt.data(), len);\n    \/\/ PKCS#5 \u586b\u5145\n    uint8_t padding_value = padded_len - len;\n    std::fill(input.begin() + len, input.end(), padding_value);\n    \n    for (size_t i = 0; i &lt; padded_len; i += AES_BLOCK_SIZE) {\n        convertToByte(input.data() + i, state, AES_BLOCK_SIZE);\n\t\t\/\/ std::cout &lt;&lt; \"\u52a0\u5bc6\u524d\uff1a\";\n\t\t\/\/ printBytes(state, AES_BLOCK_SIZE);\n\t\tAES_Encrypt(state, key, expandKeyLen);\n\t\t\/\/ std::cout &lt;&lt; \"\u52a0\u5bc6\u5b8c\u5f8c\uff1a\";\n    \t\/\/ printBytes(state, AES_BLOCK_SIZE);\n        std::vector&lt;uint8_t> tmp = byteArrayToVector(state, AES_BLOCK_SIZE);\n        memcpy(output.data() + i, tmp.data(), AES_BLOCK_SIZE);  \n    }\n    std::string en64 = base64_encode(output.data(), output.size());\n    return en64;\n}\n\n\nstd::string aes_decrypt_ecb(const std::string text_to_decrypt,  const std::string key_str) {\n    std::string output = \"\";\n    std::vector&lt;uint8_t> outputData;\n    std::vector&lt;uint8_t> de64 = base64_decode(text_to_decrypt);\n    BYTE key&#91;16 * (14 + 1)];\n    BYTE state&#91;AES_BLOCK_SIZE] = {0};\n\tint keyLen = 16;\n    size_t len = de64.size();\n    AES_Init();\n    \/\/\u8b80\u53d6\u91d1\u9470\n    memcpy(key, key_str.data(), AES_BLOCK_SIZE);\n    \/\/\u64f4\u5c55\u91d1\u9470\n    int expandKeyLen = AES_ExpandKey(key, keyLen);\t\/\/4* 44 = 176\n\n    \/\/ \u4f7f\u7528 std::vector \u66ff\u4ee3\u8b8a\u6578\u5927\u5c0f\u9663\u5217\n    std::vector&lt;uint8_t> input(len, 0);\n    memcpy(input.data(), de64.data(), len);\n\n    for (size_t i = 0; i &lt; len; i += AES_BLOCK_SIZE) {\n        convertToByte(input.data() + i, state, AES_BLOCK_SIZE);\n\t\tAES_Decrypt(state, key, expandKeyLen);\n        appendStateToVector(state, outputData);\n    }\n\n    remove_padding(outputData);\n    output= vectorToString(outputData);\n    return output;\n}\n\nint main(){\n\tAES_Init();\n    std::string key_str = \"1234567890ABCDEF\";\n\tstd::string text_to_encrypt = \"1234567890ABCDEF\";\n\n    std::string en64 = aes_encrypt_ecb(text_to_encrypt, key_str);\n    cout &lt;&lt; \"en64\uff1a\" &lt;&lt; endl &lt;&lt; en64 &lt;&lt; endl;\n    std::string de64 = aes_decrypt_ecb(en64, key_str);\n    cout &lt;&lt; \"de64\uff1a\" &lt;&lt; endl &lt;&lt; de64 &lt;&lt; endl;\n    return 0;    \n}<\/code><\/pre>\n\n\n\n<p>\u7de8\u8b6f\u65b9\u5f0f<\/p>\n\n\n\n<pre class=\"wp-block-code has-cyan-bluish-gray-background-color has-background has-small-font-size\"><code>g++ -std=c++11 -o aes aes.cpp\n.\/aes<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>\u672c\u6587\u5927\u90e8\u5206\u7a0b\u5f0f\u78bc\u4f86\u81ea\u4e0b\u9762\u7db2\u7ad9\uff0c\u6211\u53ea\u662f\u6dfb\u52a0\u4e86 base64 \u8207 padding \u90e8\u5206\u65b9\u4fbf\u6211\u81ea\u5df1\u4f7f\u7528<\/p>\n\n\n\n<p>\u53c3\u8003\u7db2\u7ad9\uff1a<a href=\"https:\/\/www.codedata.com.tw\/social-coding\/aes\/\">https:\/\/www.codedata.com.tw\/social-coding\/aes\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6b64\u7248\u672c\u4f7f\u7528 AES\/ECB\/PKCS5Padding\uff0c\u6703\u5c07\u5b57\u4e32\u52a0\u5bc6\u5f8c\u8f38\u51fa base64 \uff0c\u89e3\u5bc6\u5247\u662f\u5c07base6 &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/blog.quantoyo.com\/?p=2064\" class=\"more-link\">\u95b1\u8b80\u5168\u6587<span class=\"screen-reader-text\">\u3008AES \u4f7f\u7528 C++ \u53ef\u76f4\u63a5\u4f7f\u7528\u7248\u672c\u3009<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[310,307,313,47,315,317,309,316,308],"class_list":["post-2064","post","type-post","status-publish","format-standard","hentry","category-technology","tag-aes","tag-aes-ecbpkcs5padding-c-wasm-java","tag-base64","tag-c","tag-decode","tag-decrypt","tag-ecb","tag-encrypt","tag-pkcs5padding"],"_links":{"self":[{"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=\/wp\/v2\/posts\/2064","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2064"}],"version-history":[{"count":1,"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=\/wp\/v2\/posts\/2064\/revisions"}],"predecessor-version":[{"id":2065,"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=\/wp\/v2\/posts\/2064\/revisions\/2065"}],"wp:attachment":[{"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2064"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2064"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.quantoyo.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2064"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}