php эквивалент openssl_seal в Node.js - PullRequest
1 голос
/ 29 января 2020

У меня есть фрагмент кода в php, в который я хотел бы перейти node.js, но я не могу найти правильный способ сделать это.

class  EncryptService
{
    const PUBLIC_CERT_PATH = 'cert/public.cer';
    const PRIVATE_CERT_PATH = 'cert/private.key';
    const ERROR_LOAD_X509_CERTIFICATE = 0x10000001;
    const ERROR_ENCRYPT_DATA = 0x10000002;

    public $outEncData = null;
    public $outEnvKey = null;
    public $srcData;

    public function encrypt()
    {
        $publicKey = openssl_pkey_get_public(self::PUBLIC_CERT_PATH);

        if ($publicKey === false) {
            $publicKey = openssl_pkey_get_public("file://".self::PUBLIC_CERT_PATH);
        }
        if ($publicKey === false) {
            $errorMessage = "Error while loading X509 public key certificate! Reason:";

            while (($errorString = openssl_error_string())) {
                $errorMessage .= $errorString . "\n";
            }
            throw new Exception($errorMessage, self::ERROR_LOAD_X509_CERTIFICATE);
        }

        $publicKeys = array($publicKey);
        $encData = null;
        $envKeys = null;
        $result = openssl_seal($this->srcData, $encData, $envKeys, $publicKeys);
        if ($result === false)
        {
            $this->outEncData = null;
            $this->outEnvKey = null;
            $errorMessage = "Error while encrypting data! Reason:";
            while (($errorString = openssl_error_string()))
            {
                $errorMessage .= $errorString . "\n";
            }
            throw new Exception($errorMessage, self::ERROR_ENCRYPT_DATA);
        }
        $this->outEncData = base64_encode($encData);
        $this->outEnvKey = base64_encode($envKeys[0]);
    }
};

Проблема в том, что я не могу найти реализацию openssl_sign в Javascript где угодно. Мне нужно сохранить эту структуру, потому что я использую outEncData и outEnvKey.

Мне удалось найти эквивалентную реализацию openssl_sign с пакетом crypto, но ничего для openssl_seal.

LE добавил рабочее решение в качестве ответа

Ответы [ 2 ]

1 голос
/ 01 февраля 2020

ОК. Я потратил некоторое время, чтобы понять это, короче говоря, теперь он находится в репо: ivarprudnikov / node-crypto-rc4-encrypt-decrypt . Но мы хотим следовать таким правилам здесь.

Ниже предполагается, что у вас есть открытый ключ c для подписи сгенерированного ключа и закрытый ключ для проверки, если все хорошо.

  1. Случайно сгенерированный секретный ключ, используемый для шифрования:
const crypto = require('crypto');

const generateRandomKeyAsync = async () => {
    return new Promise((resolve, reject) => {
        crypto.scrypt("password", "salt", 24, (err, derivedKey) => {
            if (err) reject(err);
            resolve(derivedKey.toString('hex'));
        });
    });
}
Шифрование данных сгенерированным ключом, а затем шифрование этого ключа с помощью данного открытого ключа c. Мы хотим отправить обратно как зашифрованные данные, так и зашифрованный ключ, поскольку мы ожидаем, что пользователь на другой стороне будет иметь закрытый ключ.
const crypto = require('crypto');
const path = require('path');
const fs = require('fs');

const encryptKeyWithPubAsync = async (text) => {
    return new Promise((resolve) => {
        fs.readFile(path.resolve('./public_key.pem'), 'utf8', (err, publicKey) => {
            if (err) throw err;
            const buffer = Buffer.from(text, 'utf8');
            const encrypted = crypto.publicEncrypt(publicKey, buffer);
            resolve(encrypted.toString('base64'));  
        });
    });
}

const encryptStringAsync = async (clearText) => {
    const encryptionKey = await generateRandomKeyAsync();
    const cipher = await crypto.createCipheriv("RC4", encryptionKey, null);
    const encryptedKey = await encryptKeyWithPubAsync(encryptionKey);
    return new Promise((resolve, reject) => {
        let encryptedData = '';
        cipher.on('readable', () => {
          let chunk;
          while (null !== (chunk = cipher.read())) {
            encryptedData += chunk.toString('hex');
          }
        });
        cipher.on('end', () => {
          resolve([encryptedKey, encryptedData]); // return value
        });
        cipher.write(clearText);
        cipher.end();   
    });
}
Так что теперь мы можем зашифровать детали:
encryptStringAsync("foo bar baz")
   .then(details => {
        console.log(`encrypted val ${details[1]}, encrypted key ${details[0]}`);
    })

Напечатает что-то вроде:

encrypting foo bar baz
encrypted val b4c6c7a79712244fbe35d4, encrypted key bRnxH+/pMEKmYyvJuFeNWvK3u4g7X4cBaSMnhDgCI9iii186Eo9myfK4gOtHkjoDKbkhJ3YIErNBHpzBNc0rmZ9hy8Kur8uiHG6ai9K3ylr7sznDB/yvNLszKXsZxBYZL994wBo2fI7yfpi0B7y0QtHENiwE2t55MC71lCFmYtilth8oR4UjDNUOSrIu5QHJquYd7hF5TUtUnDtwpux6OnJ+go6sFQOTvX8YaezZ4Rmrjpj0Jzg+1xNGIIsWGnoZZhJPefc5uQU5tdtBtXEWdBa9LARpaXxlYGwutFk3KsBxM4Y5Rt2FkQ0Pca9ZZQPIVxLgwIy9EL9pDHtm5JtsVw==
Чтобы проверить вышеприведенные предположения, необходимо сначала расшифровать ключ с помощью частного:
const decryptKeyWithPrivateAsync = async (encryptedKey) => {
    return new Promise((resolve) => {
        fs.readFile(path.resolve('./private_key.pem'), 'utf8', (err, privateKey) => {
            if (err) throw err;
            const buffer = Buffer.from(encryptedKey, 'base64')
            const decrypted = crypto.privateDecrypt({
                key: privateKey.toString(),
                passphrase: '',
            }, buffer);
            resolve(decrypted.toString('utf8'));
        });
    });
}
После расшифровки ключа можно расшифровать сообщение:
const decryptWithEncryptedKey = async (encKey, encVal) => {
    const k = await decryptKeyWithPrivateAsync(encKey);
    const decipher = await crypto.createDecipheriv("RC4", k, null);
    return new Promise((resolve, reject) => {
        let decrypted = '';
        decipher.on('readable', () => {
          while (null !== (chunk = decipher.read())) {
            decrypted += chunk.toString('utf8');
          }
        });
        decipher.on('end', () => {
          resolve(decrypted); // return value
        });
        decipher.write(encVal, 'hex');
        decipher.end();
    });
}

Надеюсь, это ответит на вопрос.

0 голосов
/ 04 февраля 2020

Окончательная и рабочая версия, которая работала для меня. Моя проблема заключалась в том, что я использовал 128-битный случайный ключ для шифрования данных, вместо этого в итоге работал 256-битный.

Шифрование работает в JS, и его можно расшифровать в php с помощью openssl_open, используя ваш закрытый ключ, о чем я и просил в исходном вопросе.

const crypto = require('crypto');
const path = require('path');
const fs = require('fs');

const encryptMessage = (message) => {
  const public_key = fs.readFileSync(`${appDir}/certs/sandbox.public.cer`, 'utf8');
  const rc4Key = Buffer.from(crypto.randomBytes(32), 'binary');
  const cipher = crypto.createCipheriv('RC4', rc4Key, null);

  let data = cipher.update(message, 'utf8', 'base64');
  cipher.final();

  const encryptedKey = crypto.publicEncrypt({
    key: public_key,
    padding: constants.RSA_PKCS1_PADDING
  }, rc4Key);

  return {
    'data': data,
    'env_key': encryptedKey.toString('base64'),
  };
};
...