问题
Problem: We have to encrypt a certain column on a certain table (Postgres). It has to be decryptable in SQL queries and in our nodejs/sequelize application layer. The encryption can happen in either layer, but it must be decodable from either.
The issue I'm running into (and I'm sure it's user error) is that if I encrypt in the db I can only decrypt in the db, and the same for node.
I've tried using PGP_SYM_ENCRYPT
and ENCRYPT
in postgres and crypto
and crypto-js/aes
in node. I've gotten it to the point where it's decrypting without an error, but returns gibberish.
A few things I've tried so far (test key is thirtytwocharsthirtytwocharsplus
):
set() {
this.setDataValue('field', seq.cast(seq.fn('PGP_SYM_ENCRYPT', val,
config.AES_KEY), 'text'))
}
This properly writes the field such that PGP_SYM_DECRYPT will decrypt it, but there's (apparently?) no way to tell Sequelize to wrap the field name with a function call so it's a lot of extra js that I feel is avoidable
const decipher = crypto.createDecipher('aes256', config.AES_KEY)
decipher.setAutoPadding(false);
return decipher.update(new Buffer(this.getDataValue('field', 'binary'), 'binary', 'ascii')) + decipher.final('ascii')
This will decode the field but returns gibberish (�Mq��8Ya�b
) instead of the value (test
)
aes.encrypt('test', config.AES_KEY)
aes.decrypt(field, config.AES_KEY).toString(CryptoJS.enc.Utf8)
This encrypts fine, decrypts fine, but Postgres errors when trying to decrypt (using either PGP_SYM_DECRYPT
or DECRYPT
). Casting the resulting field to ::TEXT
and pasting it into an online AES Decrypter returns the expected value.
I really want to avoid having to add a bunch of boilerplate to our node repositories/queries, and I really feel like this should work. Using the same crypto algorithm should yield the same results
Any nudge or pointer would be greatly appreciated
回答1:
Postgres has rather unclear documentation about the raw encryption functions. After a few tries and failures, I managed to replicate most of logic logic in nodejs.
Here is the program I used.
const crypto = require('crypto');
const iv = Buffer.alloc(16); // zeroed-out iv
function encrypt(plainText, algorithm, key) {
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(plainText, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
function decrypt(encrypted, algorithm, key) {
const decrypt = crypto.createDecipheriv(algorithm, key, iv);
let text = decrypt.update(encrypted, 'base64', 'utf8');
text += decrypt.final('utf8')
return text;
}
const originalText = "hello world";
const userKey = 'abcd'
const algorithm = 'aes-128-cbc';
const paddedKey = Buffer.concat([Buffer.from(userKey), Buffer.alloc(12)]); // make it 128 bits key
const hw = encrypt(originalText, algorithm, paddedKey);
console.log("original", originalText);
console.log("encrypted:", hw);
console.log("decoded: ", decrypt(hw, algorithm, paddedKey).toString());
Also here is a list of things not documented for the raw functions of postgres:
- the key will be auto padded to match one of the 3 lengths: 128 bits, 192 bits, 256 bits
- algorithm is automatically upgraded, when key length exceeds the limit. e.g. if key exceeds 128bits,
aes-192-cbc
will be used to encrypt - if key exceeds 256 bits, it will be truncated to 256 bits.
It would be easier to replicate it in application language (either Javascript or Java), if Postgres has proper documentation of these functions.
回答2:
Ok, I got it working, hopefully properly
What I did was:
Encrypting with crypto.createCipheriv('aes-256-cbc', new Buffer(config.AES_KEY), iv)
in node, encrypt_iv
in pgsql and storing as hex
in the db, and decrypting with crypto.createDecipheriv
/decrypt_iv
into text
/utf8
I don't know what part I was missing, but between specifying aes256
, using the iv
methods, and flipping juggling hex/text it seems to be working.
👍
来源:https://stackoverflow.com/questions/50475641/encrypt-decrypt-between-postgres-and-node