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) вызывается с тремя аргументами:

  1. ids - - Массив Буферов, каждый из которых содержит 64-битный ID ключа PGP. Эти ключи могут относиться к подключам, которые часто используются при шифровании и подписи сообщений.
  2. ops - - Какие криптографические параметры требуются для этого ключа; побитовое ИЛИ констант из kbpgp.const.ops, а именно:
  • зашифровать: 0x1
  • расшифровать: 0x2
  • проверить: 0x4
  • знак: 0x8
  1. 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, чтобы продолжить со средним.