Pretty Good Privacy (PGP) - это программа шифрования, которая обеспечивает криптографическую конфиденциальность и аутентификацию для передачи данных. PGP используется для подписи, шифрования и дешифрования текстов, сообщений электронной почты, файлов, каталогов и целых разделов диска, а также для повышения безопасности обмена сообщениями электронной почты. kbpgp - это реализация PGP на JavaScript в Keybase. . Он прост в использовании, разработан для параллелизма и стабилен как в Node.js, так и в браузере. Он активно поддерживается и навсегда принадлежит вам по лицензии BSD.
Получение это
Zip-файл (для браузера)
Для Node.js с NPM
npm установить kbpgp
Источник с github
git clone github.com/keybase/kbpgp
Начиная
Браузер
<script src="kbpgp-2.1.0.js"></script>
Node.js
var kbpgp = require('kbpgp');
KeyManager
Прежде чем вы сможете выполнять какие-либо операции шифрования, вам понадобится KeyManager.
KeyManager содержит открытый ключ и, возможно, секретный ключ и подключи для данного человека. Если у вас есть экземпляр KeyManager, вы можете выполнять действия с ключами внутри. Для действия «подписать и зашифровать» вам понадобятся два KeyManager: один содержит закрытый ключ (для подписывающей стороны), а другой - открытый ключ (для получателя).
Например, если у нас есть два экземпляра KeyManager, alice
и chuck
, мы можем выполнить шифрование.
var params = { encrypt_for: chuck, sign_with: alice, msg: "Hey Chuck - my bitcoin address is 1alice12345234..." };
kbpgp.box(params, function(err, result_string, result_buffer) { console.log(err, result_string, result_buffer); });
Всю работу выполняет box
функция kbpgp. Обратите внимание, что он выполняет обратный вызов как со строкой, так и с буфером. Буфер является либо буфером Node.js, либо удобным для браузера объектом с аналогичными функциями.
Довольно просто, правда? Итак, как получить KeyManager? Есть 2 способа:
- Загрузка ключа или пары ключей
- Создание новой пары ключей
KeyManager
Загрузка с открытого или закрытого ключа
В следующих примерах показано преобразование ключевой строки PGP (в классическом армированном формате) в KeyManager.
Пример 1 - KeyManager из открытого ключа
var alice_pgp_key = "-----BEGIN PGP PUBLIC ... etc.";
kbpgp.KeyManager.import_from_armored_pgp({ armored: alice_pgp_key }, function(err, alice) { if (!err) { console.log("alice is loaded"); } });
Пример 2 - KeyManager из закрытого ключа
Теперь предположим, что у нас есть закрытый ключ Алисы. Напомним, сюда входит ее открытый ключ, так что это все, что нам нужно.
var alice_pgp_key = "-----BEGIN PGP PRIVATE ... etc."; var alice_passphrase = "martian-dung-flinger";
kbpgp.KeyManager.import_from_armored_pgp({ armored: alice_pgp_key }, function(err, alice) { if (!err) { if (alice.is_pgp_locked()) { alice.unlock_pgp({ passphrase: alice_passphrase }, function(err) { if (!err) { console.log("Loaded private key with passphrase"); } }); } else { console.log("Loaded private key w/o passphrase"); } } });
Пример 3 - KeyManager из открытого ключа, затем добавление закрытого ключа
Приведенный выше пример (№2) можно выполнить в два этапа. Вы можете создать экземпляр KeyManager с открытым ключом Алисы, а затем добавить к нему ее закрытый ключ. Это вызовет ошибку, если ее закрытый ключ не совпадает с ее открытым ключом.
var alice_public_key = "-----BEGIN PGP PUBLIC ... etc."; var alice_private_key = "-----BEGIN PGP PRIVATE ... etc."; var alice_passphrase = "ovarian fred savage ";
kbpgp.KeyManager.import_from_armored_pgp({ armored: alice_public_key }, function(err, alice) { if (!err) { alice.merge_pgp_private({ armored: alice_private_key }, function(err) { if (!err) { if (alice.is_pgp_locked()) { alice.unlock_pgp({ passphrase: alice_passphrase }, function(err) { if (!err) { console.log("Loaded private key with passphrase"); } }); } else { console.log("Loaded private key w/o passphrase"); } } }); } });
Вы можете создать KeyManager и сгенерировать новые ключи одним махом.
В конце описанного ниже процесса у нас будет экземпляр KeyManager, alice
, который можно использовать для любого криптографического действия.
Пример 1 - RSA - с пользовательскими настройками
Чтобы проиллюстрировать общий вариант использования, мы создадим подключи как для подписи, так и для шифрования. И, кстати, когда kbpgp выполняет действия с KeyManager, он автоматически выбирает соответствующие подключи.
var F = kbpgp["const"].openpgp;
var opts = { userid: "User McTester (Born 1979) <[email protected]>", primary: { nbits: 4096, flags: F.certify_keys | F.sign_data | F.auth | F.encrypt_comm | F.encrypt_storage, expire_in: 0 // never expire }, subkeys: [ { nbits: 2048, flags: F.sign_data, expire_in: 86400 * 365 * 8 // 8 years }, { nbits: 2048, flags: F.encrypt_comm | F.encrypt_storage, expire_in: 86400 * 365 * 8 } ] };
kbpgp.KeyManager.generate(opts, function(err, alice) { if (!err) { // sign alice's subkeys alice.sign({}, function(err) { console.log(alice); // export demo; dump the private with a passphrase alice.export_pgp_private ({ passphrase: 'booyeah!' }, function(err, pgp_private) { console.log("private key: ", pgp_private); }); alice.export_pgp_public({}, function(err, pgp_public) { console.log("public key: ", pgp_public); }); }); } });
Пример 2 - RSA - с разумными значениями по умолчанию
Вышеуказанные параметры разумны. Если они вам нравятся, вы можете просто вызвать KeyManager::generate_rsa
ярлык:
kbpgp.KeyManager.generate_rsa({ userid : "Bo Jackson <[email protected]>" }, function(err, charlie) {
charlie.sign({}, function(err) {
console.log("done!");
});
});
Пример 3 - пары ключей ECC - пользовательский
Kbpgp поддерживает Elliptic Curve PGP (подробности см. В RFC-6637). Вы можете предоставить опцию ecc : true
для приведенного выше вызова generate для создания пары ключей ECC, а не стандартной пары ключей PGP. Однако имейте в виду, что большинство обычных клиентов GPG в настоящее время не поддерживают ECC.
var F = kbpgp["const"].openpgp;
var opts = { userid: "User McTester (Born 1979) <[email protected]>", ecc: true, primary: { nbits: 384, flags: F.certify_keys | F.sign_data | F.auth | F.encrypt_comm | F.encrypt_storage, expire_in: 0 // never expire }, subkeys: [ { nbits: 256, flags: F.sign_data, expire_in: 86400 * 365 * 8 // 8 years }, { nbits: 256, flags: F.encrypt_comm | F.encrypt_storage, expire_in: 86400 * 365 * 8 } ] };
kbpgp.KeyManager.generate(opts, function(err, alice) { // as before... });
Пример 4 - пары ключей ECC - с разумными значениями по умолчанию
Чтобы использовать эти параметры по умолчанию, мы также предоставляем ярлык:
kbpgp.KeyManager.generate_ecc({ userid : "<[email protected]>" }, function(err, charlie) {
charlie.sign({}, function(err) {
console.log("done!");
});
});
Мониторинг
Все функции kbpgp поддерживают передачу объекта ASync Package
(ASP) для мониторинга. Ваш ASP может иметь функцию progress_hook, которая будет вызываться с информацией о ходе выполнения. Это особенно важно при генерации ключей RSA, поскольку это может занять некоторое время. Если это есть в каком-либо клиентском приложении, вам нужно (а) показать какой-либо индикатор того, что вы выполняете работу, и (б) иметь кнопку отмены.
var my_asp = new kbpgp.ASP({ progress_hook: function(o) { console.log("I was called with progress!", o); } });
var opts = { asp: my_asp, userid: "[email protected]", primary: { nbits: 4096 }, subkeys: [] };
kbpgp.KeyManager.generate(opts, some_callback_function);
Отмена
Если вы передадите объект ASP, как описано выше, вы можете использовать его для отмены процесса.
kbpgp.KeyManager.generate(opts, some_callback_function);
// oh, heck, let's give up if it takes more than a second setTimeout((function() { my_asp.canceler.cancel(); }), 1000);
В приведенном выше примере, если генерация не завершилась в течение одной секунды, работа будет остановлена, и some_callback_function
будет немедленно вызван с err, null
.
Шифрование и / или подпись
Безопасность для масс
Шаги для шифрования, подписи или и того, и другого одинаковы в kbpgp. Разница лишь в том, какой KeyManager вам понадобится. Чтобы что-то подписать, вам понадобится KeyManager, содержащий закрытый ключ. А чтобы что-то зашифровать, вам понадобится ключ, содержащий открытый ключ получателя. Если ваши KeyManager содержат подключи, kbpgp автоматически использует соответствующие.
Пример 1 - только шифрование
Предположение: у нас есть экземпляр KeyManager chuck
для получателя.
var params = { msg: "Chuck chucky, bo-bucky!", encrypt_for: chuck };
kbpgp.box(params, function(err, result_string, result_buffer) { console.log(err, result_armored_string, result_raw_buffer); });
Пример 2 - только подпись
Аналогичным образом легко подписать сообщение открытым текстом. Просто укажите sign_with
KeyManager, но не используйте encrypt_for
.
var params = { msg: "Here is my manifesto", sign_with: alice };
kbpgp.box (params, function(err, result_string, result_buffer) { console.log(err, result_string, result_buffer); });
Пример 3 - подписать + зашифровать
Предположение: у нас также есть экземпляр KeyManager alice
для отправителя.
var params = { msg: "Chuck chucky, bo-bucky! This is Alice here!", encrypt_for: chuck, sign_with: alice };
kbpgp.box (params, function(err, result_string, result_buffer) { console.log(err, result_string, result_buffer); });
Пример 4 - использование буферов ввода и вывода
kbpgp может принимать в качестве входных данных буферы Node.js вместо строк. Следующее читает файл .png и записывает его новую зашифрованную копию. Для получения дополнительной информации ознакомьтесь с документацией по буферам kbpgp.
var kbpgp = require('kbpgp'); var fs = require('fs'); var buffer = fs.readFileSync('dirty_deeds.png'); var params = { msg: buffer, encrypt_for: chuck, sign_with: alice };
kbpgp.box (params, function(err, result_string, result_buffer) { fs.writeFileSync('dirty_deeds.encrypted', result_buffer); });
В браузере также доступны буферы для работы с файлами в формате HTML5. kbpgp.Buffer
предоставляет реализацию браузера, которая соответствует Node.js.
Пример 5 - хуки прогресса и отмена
Большинство функций kbpgp могут принимать объект kbpgp.ASP
, который используется для отслеживания прогресса и проверки запросов на отмену.
var asp = new kbpgp.ASP({ progress_hook: function(info) { console.log("progress...", info); } });
var params = { msg: "a secret not worth waiting for", encrypt_for: chuck, asp: asp };
kbpgp.box(params, function(err, result_string, result_buffer) { console.log("Done!", err, result_string, result_buffer); });
// sometime before it's done asp.canceler().cancel();
Расшифровка и проверка
Расшифровка и проверка немного сложнее, чем шифрование или подпись, потому что часто вы не знаете заранее, какие KeyManager необходимы. Для подписанных и зашифрованных сообщений PGP вы знаете только, какой ключ проверки требуется после успешного дешифрования. Кроме того, сообщения в PGP могут быть зашифрованы для нескольких получателей, и любой конкретный получатель может иметь доступ только к одному из многих возможных ключей дешифрования.
В kbpgp функция unbox
обрабатывает мельчайшие детали расшифровки и проверки. Вам необходимо передать ему сообщение PGP (зашифрованное, подписанное или и то, и другое), а также способ получения ключей в середине потока --- объект kbpgp.KeyFetcher
. Вы можете использовать один из наших готовых вариантов или создать собственный подкласс (скажем, если вы хотите получить ключи со своего сервера).
Из коробки: кольцо для ключей
Первым примером KeyFetcher, который мы рассмотрим, является KeyRing - объект, который вы можете заранее загрузить с помощью набора KeyManager.
var ring = new kbpgp.keyring.KeyRing();
var kms = [ alice, bob, charlie ];
for (var i in kms) {
ring.add_key_manager(kms[i]);
}
Для удобства класс KeyManager
также реализует интерфейс KeyFetcher. Если вы заранее знаете, что вам понадобится только один KeyManager для расшифровки / проверки, вы можете использовать его как KeyFetcher.
Пример расшифровки и проверки
Расшифруйте и проверьте с помощью функции unbox
. Передайте сообщение, KeyFetcher (например, ring
выше), ASP, если вы собираетесь отменить или отслеживать прогресс, и обратный вызов, который запускается, когда это будет сделано:
var ring = new kbpgp.keyring.KeyRing;
var kms = [alice, bob, charlie];
var pgp_msg = "---- BEGIN PGP MESSAGE ----- ....";
var asp = /* as in Encryption ... */;
for (var i in kms) {
ring.add_key_manager(kms[i]);
}
kbpgp.unbox({keyfetch: ring, armored: pgp_msg, asp }, function(err, literals) {
if (err != null) {
return console.log("Problem: " + err);
} else {
console.log("decrypted message");
console.log(literals[0].toString());
var ds = km = null;
ds = literals[0].get_data_signer();
if (ds) { km = ds.get_key_manager(); }
if (km) {
console.log("Signed by PGP fingerprint");
console.log(km.get_pgp_fingerprint().toString('hex'));
}
}
});
unbox
выполняет обратный вызов с двумя аргументами: ошибкой, если что-то пошло не так, и массивом Literals
, если нет. Объекты Literal
поддерживают методы toString(enc)
и toBuffer()
. Первый вызов принимает необязательный параметр, который является кодировкой; если ничего не указано, kbpgp будет использовать кодировку, указанную в сообщении PGP; вы можете указать 'utf8', 'ascii', 'binary', 'base64' или 'hex', если хотите переопределить эту кодировку.
В этом примере показано, что unbox
обрабатывает как дешифрование, так и проверку. Чтобы проверить, были ли подписаны части сообщения, выполните вызов get_data_signer
для каждого Literal
сообщения. Обратите внимание, что здесь отображается тот же KeyManager, который вы загрузили в свой KeyFetcher. Поэтому, если вы дополните этот KeyManager настраиваемыми полями, они будут доступны здесь.
Интерфейс KeyFetcher
В более общем сценарии расшифровки / проверки вам может потребоваться получить соответствующие ключи расшифровки и / или проверки из вторичного или удаленного хранилища. В этой ситуации вам не следует использовать KeyRing, описанный выше, вместо этого следует предоставить настраиваемый KeyFetcher.
Все используемые KeyFetcher должны реализовывать один метод: fetch
. Учитывая несколько идентификаторов ключей PGP и флаг, указывающий, какая операция запрашивается, метод выборки должен выполнить обратный вызов с KeyManager
, если он сможет его найти.
fetch(ids,ops,cb)
вызывается с тремя аргументами:
- ids - - Массив Буферов, каждый из которых содержит 64-битный ID ключа PGP. Эти ключи могут относиться к подключам, которые часто используются при шифровании и подписи сообщений.
- ops - - Какие криптографические параметры требуются для этого ключа; побитовое ИЛИ констант из
kbpgp.const.ops
, а именно:
- зашифровать: 0x1
- расшифровать: 0x2
- проверить: 0x4
- знак: 0x8
- cb - - Обратный вызов, который по завершении выполняет обратный вызов с тройным:
(err,km,i)
- err - Ошибка, объясняющая, что пошло не так, или
null
в случае успеха. - km - в случае успеха KeyManager, отвечающий заданным требованиям.
- i - в случае успеха целое число, указывающее, какой из ключей был найден при поиске. Если здесь возвращается
0
, тоids[0]
- это 64-битный идентификатор ключа внутриkm
.
Маленькие файлы и буферы
В большинстве примеров мы имели дело с открытыми текстами строк и зашифрованными текстами. Конечно, иногда вам нужно читать и записывать файлы и преобразовывать их в интересные строки, такие как шестнадцатеричный или base64.
Вспомните, когда мы шифровали, мы ожидали строку для сообщения:
var params = {
msg: "Chuck chucky, bo-bucky!",
encrypt_for: chuck // a KeyManager instance
};
В Node.js вместо этого мы можем передать буфер Node.js. Это могло быть из файла. Помните, что буфер и вывод этого файла должны легко помещаться в памяти. (Для файлов произвольно большого размера потоки скоро появятся в kbpgp.)
fs.readFile('foo.png', function(err, buff) {
var params = {
msg: buff,
encrypt_for: chuck
};
});
В браузере доступен аналогичный буфер kbpgp.Buffer
. Он ведет себя точно так же, как буфер Node.js, благодаря [native-buffer-browserify]
// using a string as a Buffer, in the browser
var params = {
msg: kbpgp.Buffer.from("Chuck chucky, bo-bucky!"),
encrypt_for: chuck
};
Буферы вывода
Функция записи kbpgp выполняет обратный вызов с помощью строки result_string (защищенной при шифровании или подписи) и result_buffer (только необработанные двоичные данные). Последний является либо буфером Node.js, как обсуждалось выше, либо в браузере kbpgp.Buffer
.
kbpgp.burn(params, function(err, result_string, result_buffer) {
console.log(result_buffer.toString('hex'));
console.log(result_buffer.toString('base64'));
// etc...
// ...these work in both the browser and Node.js
});
В браузере с HTML5
Если вы хотите поддерживать обработку файлов в браузере, вы можете использовать HTML5 FileReader
и преобразовать содержимое файла в буфер прямо на стороне клиента. В зависимости от браузера у вас будут ограничения памяти.
var f = some_html_5_file_object;
var r = new FileReader(); // modern browsers have this
r.readAsBinaryString(f);
r.onloadend = function(file) {
var buffer = kbpgp.Buffer.from(r.result);
// ... now process it using kbpgp
};
Шифрование
const fs = require('fs'); const openpgp = require('openpgp'); const async = require('async');
const encryptFuntion = () => { async.waterfall([ (a_cb) => { fs.readFile('pub.asc', 'utf8', (err, pubkey) => { if (!err) { a_cb(null, pubkey); } else { a_cb(err); } }); }, (pubkey, a_cb) => { fs.readFile('priv.asc', 'utf8', (err, privkey) => { if (!err) { const passphrase = `yourPassphrase`; let privKeyObj = (await openpgp.key.readArmored(privkey)).keys[0]; await privKeyObj.decrypt(passphrase); a_cb(null, pubkey, privKeyObj); } else { a_cb(err); } }); }, (pubkey, privKeyObj, a_cb) => { fs.readFile('test.txt', 'utf8', (err, rawMessage) => { if (!err) { a_cb(null, pubkey, privKeyObj, rawMessage); } else { a_cb(err); } }); }, (pubkey, privKeyObj, message, a_cb) => { let options = { message: openpgp.message.fromText(message), publicKeys: (await openpgp.key.readArmored(pubkey)).keys, privateKeys: [privKeyObj], armor: false }; openpgp.encrypt(options) .then((ciphertext) => { a_cb(null, ciphertext); }); } ], (err, ciphertext) => { if (!err) { fs.writeFile('test.txt.gpg', ciphertext.data, (err) => { if (!err) { console.log('Created GPG file!'); } else { console.log(err); } }); } else { console.log(err); } }); };
module.exports = { encryptFuntion };
Расшифровка
const fs = require('fs'); const openpgp = require('openpgp'); const async = require('async'); const decryptFuntion = () => { async.waterfall([ (a_cb) => { fs.readFile('test.txt.gpg', (err, encrypted) => { if (!err) { a_cb(null, encrypted); } else { a_cb(err); } }); }, (encrypted, a_cb) => { fs.readFile('pub.asc', 'utf8', (err, pubkey) => { if (!err) { a_cb(null, encrypted, pubkey); } else { a_cb(err); } }); }, (encrypted, pubkey, a_cb) => { fs.readFile('priv.asc', 'utf8', (err, privkey) => { if (!err) { const passphrase = `yourPassphrase`; let privKeyObj = (await openpgp.key.readArmored(privkey)).keys[0]; await privKeyObj.decrypt(passphrase); a_cb(null, encrypted, pubkey, privKeyObj); } else { a_cb(err); } }); }, (encrypted, pubkey, privKeyObj, a_cb) => { let options = { message: await openpgp.message.readArmored(encrypted), publicKeys: (await openpgp.key.readArmored(pubkey)).keys, privateKeys: [privKeyObj] }; openpgp.decrypt(options) .then((plaintext) => { a_cb(null, plaintext); }); } ], (err, plaintext) => { if (!err) { fs.writeFile('test-decrypted.txt', plaintext, 'utf8', (err) => { if (!err) { console.log('Created txt file!'); } else { console.log(err); } }); } else { console.log(err); } }); };
module.exports = { decryptFuntion };
Любите историю? Пожалуйста, поддержите меня, подарив мне Среднее членство или PayPal Me, чтобы продолжить со средним.