3

I have a bunch passwords for an internal app that were encrypted with node.js and stored (irrelevantly) in mongodb. I would like to convert this application to Go, but I'm getting tripped up on the node.js encryption. I've looked through the node.js source code for this, and it's using the OpenSSL evpBytesToKey method. I've found a Golang implementation of this on the web, but I still can't decrypt the password in Go that was encrypted with node.js. "algo" is "aes256"

this.encrypt = function(s){
var cipher = crypto.createCipher(algo, key);

var i = 0;
var encrypted = "";
while (i < s.length){
    var end = Math.min(s.length-i, 15);
    var chunk = s.substring(i, end+i);
    encrypted += cipher.update(chunk, "utf8", "base64");
    i+= end;
}

encrypted += cipher.final("base64");
encrypted = encrypted.replace(/\//g,"_").replace(/\+/g, "-"); // base64 url encode
return encrypted;
}

And the Go code:

func evpBytesToKey(password string, keyLen int) (key []byte, iv []byte) {
const md5Len = 16

cnt := (keyLen-1)/md5Len + 1
m := make([]byte, cnt*md5Len)
key = make([]byte, keyLen)
iv = make([]byte, md5Len)


copy(m, md5sum([]byte(password)))

// Repeatedly call md5 until bytes generated is enough.
// Each call to md5 uses data: prev md5 sum + password.
d := make([]byte, md5Len+len(password))
start := 0
for i := 1; i < cnt; i++ {
    start += md5Len
    copy(d, m[start-md5Len:start])
    copy(d[md5Len:], password)
    copy(m[start:], md5sum(d))
}
return m[:keyLen], iv
}

And the decrypt function

func Decrypt(key string, b64 string) string {
text := decodeBase64(b64)   // base64.StdEncoding.DecodeString helper method
block, err := aes.NewCipher(evpBytesToKey(key,32))
if err != nil {
    panic(err)
}
if len(text) < aes.BlockSize {
    panic("ciphertext too short")
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]

fmt.Println(iv)
fmt.Println(text)

cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(text, text)
return string(text)
}

Here is some sample data:

8PTcrR0Nf0sltmdtvLUrFg== (you tell ME what it decrypts to :)

The key is var key = "random array of characters"; literally.

This is above my personal encryption experience and training. It is my last roadblock... Thanks for any help and guidance you can provide.

5
  • 1
    I guessing it's something to do with your key+iv generation. You don't have an iv at all here, since your evpBytesToKey function returns an empty iv, and your ciphertext isn't long enough to have an iv prepended. Commented Oct 22, 2015 at 14:34
  • 1
    I forgot I was in the middle of editing the code and stepped away from it for like a month. Crap. Yeah that evpBytesToKey returns two byte slices, but I'm calling it like it still returns 1. SHITE. I may have to scrap this question and work on it alone for a while again. Commented Oct 22, 2015 at 14:54
  • I've noticed that the cipher text that the encrypt method creates is a lot longer than the ones that were created by node.js ... Thanks for your help @JimB I will continue digging. Commented Oct 22, 2015 at 16:03
  • The other route I can go is to update the node.js code to mimic the Go code, and batch update all passwords from the old method to the new method. Use that going forward until the application is rewritten in Go. Then the Go program can just pick up where the Node one left off Commented Oct 22, 2015 at 16:13
  • Yeah I was gonna point out that if you just do a one off decryption with node you could migrate to a new algorithm in Go. Not sure what the pros and cons of that would be vs this approach, but something to consider maybe. Commented Oct 22, 2015 at 17:00

1 Answer 1

1

In OpenSSL 'aes256' is equivalent to 'aes-256-cbc'. So your use of NewCFBDecrypter seems off. Other than that the IV/key generation needed to be tweaked a little to actually generate an IV.

This test script gives me an output that's at least ASCII.

http://play.golang.org/p/zsPMd8QN0b

I found the IV / key to look for by having OpenSSL generate it for me (made debugging genIvAndKey a little easier).

Note that you still need to determine where to split between the actual password and the padding. (unicode.IsPrint?)

Sign up to request clarification or add additional context in comments.

8 Comments

Wow awesome. Yeah it's a password, like not a word, but random hard to remember one. I will try this when I can get to a computer. It seemed like there was something up with the length, that would explain it!! Thank you, I will let you know soon. Right now I forget the pw and don't have access to that system from outside the network, so even if you told me what it decrypted to, I couldn't verify
Yes! Thank you! That did it. I tested with another stored password and it decrypted fine! I don't have enough rep to upvote but it is marked as the answer.
Interesting, yet smart. Investigating the value of the padded bytes, the number is how many bytes it is padded by. For example, it might decrypt to "Go Decryption" and that's 13 characters, so the last 3 bytes will just be [3,3,3] . For one that's 12, it'll be [4,4,4,4]. It's embarrassing how little knowledge I have in encryption, maybe I'll read a book or something...
Ah, I see. But what happens then when the cleartext is 0123456789abcde\x01? That will decrypt to 0123456789abcde.
Unless you add a new block with just padding.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.