dongzheng8463 2017-01-07 12:55
浏览 40
已采纳

AES-CFB从Go到JS forge

I'm trying to write a little something that is not secure. The purpose is to encrypt server-side mp3s so they can't be just downloaded with wget or "save as" and used by the average Joe.

The idea is on the server side, load the mp3, aes-cfb encrypt it, send the key in the header, send the encrypted mp3 in the response body.

Server side is using Go's stdlib and AES-CFB encryption. As first with base64 encoding, then just plain output of the encrypted []byte-s.

Client side I'm using forge to decrypt. I send a xhr, read the arraybuffer, decrypt using forge and write output to the console.

The content of test.txt is "this is just a test and maybe it's working maybe not."

main.go

package main

import (
    "net/http"
    "io"
    "crypto/rand"
    "os"
    "crypto/aes"
    "crypto/cipher"
    "fmt"
)

var (
    key = "1234567890123456"
    fn = "test.txt"
)

func main() {

    http.Handle("/file/", http.HandlerFunc(serveFile))
    http.Handle("/", http.FileServer(http.Dir("public")))
    http.ListenAndServe(":8080", nil)
}

func serveFile(w http.ResponseWriter, r *http.Request) {
    file, e := os.Open(fn)
    if e != nil {
        fmt.Println(e.Error())
        return
    }
    defer file.Close()
    fi, _ := file.Stat()
    b := make([]byte, fi.Size())
    io.ReadFull(file, b)
    o := AESencrypt([]byte(key), b)
    w.Header().Set("Access-Control-Allow-Origin", "*")
    //w.Header().Set("Key", key)
    fmt.Println(o)
    fmt.Println(len(o))
    w.Write(o)
}

func AESencrypt(key []byte, content []byte) []byte {
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    ciphertext := make([]byte, aes.BlockSize + len(content))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }
    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], content)
    return ciphertext
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MP3 Player Demo</title>
</head>
<body>
<button onclick="loadFile('1')">Load</button>

<script src="node_modules/node-forge/js/forge.bundle.js"></script>
<script src="assets/reader.js"></script>
</body>
</html>

reader.js

function loadFile(filename) {
    //var context = new AudioContext || new webkitAudioContext();
    var request = new XMLHttpRequest();
    var url = "http://localhost:8080/file/";
    request.open("GET", url + filename, true);
    request.responseType = "arraybuffer";
    request.onload = function () {
        var rt = request.response;
        console.log(rt);
        var decipher = forge.cipher.createDecipher('AES-CFB', forge.util.createBuffer('1234567890123456'));
        decipher.start({iv: forge.util.createBuffer('1234567890123456')});
        decipher.update(forge.util.createBuffer(rt));
        decipher.finish();
        console.log(decipher.output);
        console.log(decipher.output.bytes());
        console.log('--------------');
    };

    request.send();
}

The result is weird.

It is "properly" decrypted however there's a prefix or garbage of random length with each decrypted result.

3 outputs:

ArrayBuffer { byteLength: 69 }  reader.js:10:9
Object { data: "3~æÿK¥=®ªÿÂßthis is just a test…", read: 0, _constructedStringLength: 69 }  reader.js:16:9
3~æÿK¥=®ªÿÂßthis is just a test and maybe it's working maybe not.  reader.js:17:9
--------------  reader.js:18:9
ArrayBuffer { byteLength: 69 }  reader.js:10:9
Object { data: "ÅJÇ9Ë54«ÚV«this is just a test…", read: 0, _constructedStringLength: 69 }  reader.js:16:9
ÅJÇ9Ë54«ÚV«this is just a test and maybe it's working maybe not.  reader.js:17:9
--------------  reader.js:18:9
ArrayBuffer { byteLength: 69 }  reader.js:10:9
Object { data: "ªÕxïÂ`zqA   \cýx#this is just a test…", read: 0, _constructedStringLength: 69 }  reader.js:16:9
ªÕxïÂ`zqA   \cýx#this is just a test and maybe it's working maybe not.  reader.js:17:9
--------------  reader.js:18:9

The output here is truncated but it's the same is the test.txt . As you can see it's always prefixed with random garbage.

What am I doing wrong? Is the forge implementation of AES-CFB wrong or Go's? Why are they incompatible? Or why is it that the decryption differs? And if AES-CFB is a standard why are there different implementations?

I also tried gopherjs as an alternative and that works just fine but a) the code size is too big (~3.7MB) and b) I wouldn't know how to play back the decrypted audio using gopherjs. But that's just as an aside.

  • 写回答

1条回答 默认 最新

  • dongse7261 2017-01-07 14:34
    关注

    You're forgetting to remove the IV. This means the IV gets "decrypted" as well, resulting in nonsense.

    The nonsense seems to be interpreted as UTF-8, where a character may have a multi-byte encoding, so the size of the IV you print out may differ somewhat.

    So remove the IV, and try printing out hexadecimals when debugging binary values.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 Matlab打开默认名称带有/的光谱数据
  • ¥50 easyExcel模板 动态单元格合并列
  • ¥15 res.rows如何取值使用
  • ¥15 在odoo17开发环境中,怎么实现库存管理系统,或独立模块设计与AGV小车对接?开发方面应如何设计和开发?请详细解释MES或WMS在与AGV小车对接时需完成的设计和开发
  • ¥15 CSP算法实现EEG特征提取,哪一步错了?
  • ¥15 游戏盾如何溯源服务器真实ip?需要30个字。后面的字是凑数的
  • ¥15 vue3前端取消收藏的不会引用collectId
  • ¥15 delphi7 HMAC_SHA256方式加密
  • ¥15 关于#qt#的问题:我想实现qcustomplot完成坐标轴
  • ¥15 下列c语言代码为何输出了多余的空格