luewell/gpg 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

luewell/gpg

Composer 安装命令:

composer require luewell/gpg

包简介

This package contains the implementation of a simple GPG wrapper

关键字:

README 文档

README

Introduction

This package implements a wrapper around the PGP command line tool.

Please note that the ambition of this wrapper is not to be "complete". This wrapper has been developed in order to automat the GPG processing of a large number of files. Therefore, only the basic GPG functionalities have been wrapped (signing, encryption, decryption and signature verification).

License

This code is published under the following license:

Creative Common Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)

See the file LICENSE.TXT

Installation

From the command line:

composer require luewell/gpg

Or, from within the file composer.json:

"require": {
    "luewell/gpg": "*"
}

Synopsis

// Get the fingerprint of a key.

$fgp = Gpg::getPublicKeyFingerPrint('protected key');
$fgp = Gpg::getPrivateKeyFingerPrint('protected key');

// Remove a key from the keyring.

Gpg::removePublicKey($fgp);
Gpg::removePrivateKey($fgp);

// Import a key into the keyring.

Gpg::importPublicKey('open.pub');
Gpg::importPrivateKey('open.prv');

// Check that a key is in the keyring.

Gpg::isPublicKeyPresent($fgp);
Gpg::isPrivateKeyPresent($fgp);

// Sign (encrypt with a private key).

Gpg::signFile('/path/to/document', $fgp, 'my password (if any), or null', '/path/to/encrypted_file');
Gpg::signFile('/path/to/document', $fgp, null, '/path/to/encrypted_file');
Gpg::signString('AZERTY', $fgp, 'my password (if any), or null', '/path/to/encrypted_file');
Gpg::signString('AZERTY', $fgp, null, '/path/to/encrypted_file');

$encryptedString = Gpg::signFile('/path/to/document', $fgp, 'my password (if any), or null', null);
$encryptedString = Gpg::signFile('/path/to/document', $fgp, null, null);
$encryptedString = Gpg::signString('AZERTY', $fgp, 'my password (if any), or null', null);
$encryptedString = Gpg::signString('AZERTY', $fgp, null, null);

// Clear sign

Gpg::clearSignFile('/path/to/document', $fgp, 'my password (if any), or null', '/path/to/signed_document');
Gpg::clearSignFile('/path/to/document', $fgp, null, '/path/to/signed_document');
Gpg::clearSignString('AZERTY', $fgp, 'my password (if any), or null', '/path/to/signed_document');
Gpg::clearSignString('AZERTY', $fgp, null, '/path/to/signed_document');

$signedDocument = Gpg::clearSignFile('/path/to/document', $fgp, 'my password (if any), or null', null);
$signedDocument = Gpg::clearSignFile('/path/to/document', $fgp, null, null);
$signedDocument = Gpg::clearSignString('AZERTY', $fgp, 'my password (if any), or null', null);
$signedDocument = Gpg::clearSignString('AZERTY', $fgp, null, null);

// Detach sign

Gpg::detachSignFile('/path/to/document', $fgp, 'my password (if any), or null', '/path/to/signature');
Gpg::detachSignFile('/path/to/document', $fgp, null, '/path/to/signature');
Gpg::detachSignString('AZERTY', $fgp, 'my password (if any), or null', '/path/to/signature');
Gpg::detachSignString('AZERTY', $fgp, null, '/path/to/signature');

$signature = Gpg::detachSignFile('/path/to/document', $fgp, 'my password (if any), or null', null);
$signature = Gpg::detachSignFile('/path/to/document', $fgp, null, null);
$signature = Gpg::detachSignString('AZERTY', $fgp, 'my password (if any), or null', null);
$signature = Gpg::detachSignString('AZERTY', $fgp, null, null);

// Verify a "clear" signature (that is: a file that contains the document and it signature)

$warning = null;
$status = Gpg::verifyClearSignedFile('/path/to/signed_document', $warning); // true: valid signature, false: invalid signature.
$status = Gpg::verifyClearSignedString($signature, $warning); // true: valid signature, false: invalid signature.

// Verify a "detached" signature (against a document)

$warning = null;
$status = Gpg::verifyDetachedSignedFile('/path/to/signature', '/path/to/document', $warning);
$status = Gpg::verifyDetachedSignedString($signature, '/path/to/document', $warning);

// Encrypt with a public key

Gpg::encryptAsymmetricFile('/path/to/document', $fgp, '/path/to/encrypted_file');
Gpg::encryptAsymmetricString('AZERTY', $fgp, '/path/to/encrypted_file');

$encryptedString = Gpg::encryptAsymmetricFile('AZERTY', $fgp, null);
$encryptedString = Gpg::encryptAsymmetricString('AZERTY', $fgp, null);

// Decrypt a document

Gpg::decryptFile('/path/to/encrypted_file', 'my password (if any), or null', '/path/to/decrypted_file');
Gpg::decryptFile('/path/to/encrypted_file', null, '/path/to/decrypted_file');
Gpg::decryptString($encryptedString, 'my password (if any), or null', '/path/to/decrypted_file');
Gpg::decryptString($encryptedString, null, '/path/to/decrypted_file');

$decryptedString = Gpg::decryptFile('/path/to/encrypted_file', 'my password (if any), or null', null);
$decryptedString = Gpg::decryptFile('/path/to/encrypted_file', null, null);
$decryptedString = Gpg::decryptString($encryptedString, 'my password (if any), or null', null);
$decryptedString = Gpg::decryptString($encryptedString, null, null);

For a detailed description of the return codes, please consult this file.

Signing a document (using the private key)

To sign a document means: encrypt the document using the private key.

Command line

Command:

gpg --armor -u 03DEC874738344206A1A7D31E07D9D14954C8DC5 --output document.pgp --sign document

# For automation inside a script:

exec 3> /tmp/status; echo 'password' | gpg --batch --yes --always-trust --status-fd 3 --passphrase-fd 0 --armor -u 03DEC874738344206A1A7D31E07D9D14954C8DC5 --output document.pgp --sign document; echo $?; exec 3>&-

Then to decrypt the document (using the public key)

gpg --output document.decrypted --decrypt document.pgp
gpg --output - --decrypt document.pgp

Please note that you can use the sub key associated to the private key instead of the private key itself.

API

Sign:

static function signFile($inAPath, $inPrivateKeyFingerPrint, $inOptPassword=null, $inOptSignaturePath=null)
static function signString($inString, $inPrivateKeyFingerPrint, $inPassword=null, $inOptSignaturePath=null)

Decrypt:

static function decryptFile($inAbsolutePath, $inOptPassword=null, $inOptOutputFile=null)
static function decryptString($inString, $inOptPassword=null, $inOptOutputFile=null)

Clear signing a document (using the private key)

To "clear sign" a document means:

  • generate a hash of the document (using SHA1, for example).
  • encrypt the previously generated hash with the private key.
  • append the encrypted hash to the end of the document (which remains clear).

Command line

Command:

gpg --armor -u 03DEC874738344206A1A7D31E07D9D14954C8DC5 --output document.pgp --clearsign document

# For automation inside a script:

exec 3> /tmp/status; echo 'password' | gpg --batch --yes --always-trust --status-fd 3 --passphrase-fd 0 --armor -u 03DEC874738344206A1A7D31E07D9D14954C8DC5 --output document.pgp --clearsign document; echo $?; exec 3>&-

Verify the signature:

gpg --verify document.pgp

API

Sign:

static function clearSignFile($inPath, $inPrivateKeyFingerPrint, $inOptPassword=null, $inOptSignaturePath=null)
static function clearSignString($inString, $inPrivateKeyFingerPrint, $inPassword=null, $inOptSignaturePath=null)

Verify the signature:

static function verifyClearSignedFile($inFilePath, &$outWarning)
static function verifyClearSignedString($inString, &$outWarning) {

Creating a detached signature (using the private key)

Creating a "detached signature" means:

  • generate a hash of the document (using SHA1, for example).
  • encrypt the previously generated hash with the private key.
  • write the encrypted hash in a (specified) file.

Please note that a "detached signature" and a "clear signature" are identical. The difference between a "detached signature" and a "clear signature" is that the former is put into a separate file, whereas the latter is appended to the end of the signed document.

Command line

Command:

gpg --armor -u 03DEC874738344206A1A7D31E07D9D14954C8DC5 --output document.PGP --detach-sign document

# For automation inside a script:

exec 3> /tmp/status; echo 'password' | gpg --batch --yes --always-trust --status-fd 3 --passphrase-fd 0 --armor -u 03DEC874738344206A1A7D31E07D9D14954C8DC5 --output document.pgp --detach-sign document; echo $?; exec 3>&-

Verify the signature:

gpg --verify document.pgp document

API

Sign:

static function detachSignFile($inPath, $inPrivateKeyFingerPrint, $inOptPassword=null, $inOptSignaturePath=null)
static function detachSignString($inString, $inPrivateKeyFingerPrint, $inPassword=null, $inOptSignaturePath=null)

Verify a signature:

static function verifyDetachedSignedFile($inSignatureFilePath, $inDocument, &$outWarning)
static function verifyDetachedSignedString($inSignature, $inDocument, &$outWarning)

Encrypting a document (using a public key)

Please note that in GPG lingo, encryption using a private key is called "signing" (which, technically speaking, is an encryption).

Command line

Command:

gpg --armor --output encrypted_file --encrypt --recipient 03DEC874738344206A1A7D31E07D9D14954C8DC5 document

# For automation inside a script:

exec 3> /tmp/status; gpg --batch --yes --status-fd 3 --always-trust --armor --output document.pgp --encrypt --recipient 03DEC874738344206A1A7D31E07D9D14954C8DC5 document; echo $?; exec 3>&-

Decrypt the file (using a private key):

gpg --output document --decrypt document.pgp

API

static function encryptAsymmetricFile($inInputPath, $inPublicKeyFingerPrint, $inOptOutputFile=null)
static function encryptAsymmetricString($inString, $inPublicKeyFingerPrint, $inOptOutputFile=null)

Decrypt a encrypted file

Please note that the document may have been encrypted using a public key or a secret key (that is, signed).

  • If the document has been encrypted with a public key (probably yours), you will need a private key to decrypt it.
  • If the document has been signed with a private key, you will need a public key to decrypt it.

Command line

Command:

gpg --output document --decrypt document.pgp

For automation inside a script:

exec 3> /tmp/status; echo 'password' | gpg --batch --yes --status-fd 3 --passphrase-fd 0 --always-trust --output document --decrypt document.pgp; echo $?; exec 3>&-

API

static function decryptFile($inAbsolutePath, $inOptPassword=null, $inOptOutputFile=null)
static function decryptString($inString, $inOptPassword=null, $inOptOutputFile=null)

Key management

Except when calling the methods that returns the fingerprints (getPublicKeyFingerPrint and getPrivateKeyFingerPrint), the keys are identified by their fingerprints This ensures maximum security against "side effects" that may occur when specifying keys' IDs.

API

static function getPublicKeyFingerPrint($inPublicKey)
static function getPrivateKeyFingerPrint($inPrivateKey)
static function isPrivateKeyPresent($inPrivateKeyFingerPrint)
static function isPublicKeyPresent($inPublicKeyFingerPrint)
static function removePrivateKey($inPrivateKeyFingerPrint)
static function removePublicKey($inPublicKeyFingerPrint)
static function importPrivateKey($inPrivateKeyPath)
static function importPublicKey($inPublicKeyPath)

Other methods

static function version()
static function checkVersion()

Testing the package

This package contains two pairs of keys:

  • One pair which secret key is protected by a password.
  • One pair which secret key is not protected.

These keys are located in the directory tests/data:

  • open.prv / open.pub: this pair of keys is not protected.
  • protected.prv / protected.pub: this pair of keys is protected.

Importing these keys

cd tests/data
gpg --import open.prv; gpg --import open.pub; gpg --import protected.prv; gpg --import protected.pub

Getting IDs and fingerprints

For public keys:

gpg --batch --list-keys --fingerprint --with-colon

For private keys:

gpg --batch --list-secret-keys --fingerprint --with-colon

See this document for a detailed description of the output of the option --with-colon.

The Perl script list-keys.pl may be used to print the list of public keys.

gpg --list-keys --with-colon --fingerprint | perl list-keys.pl
gpg --list-secret-keys --with-colon --fingerprint | perl list-keys.pl

Example:

gpg --list-secret-keys --with-colon --fingerprint | perl list-keys.pl

Outputs:

6   sec E07D9D14954C8DC5 03DEC874738344206A1A7D31E07D9D14954C8DC5 0C185D728E760EC0 open key <ok@test.com>
6   sec 29A778386005B911 881C41F8B8FD138E86E7230929A778386005B911 6A492A01B27F4819 protected key <pk@test.com>

Whith:

Column 1: the total number of columns for the current line.
Column 2: the type of key (pub: public, sec: secret).
Column 3: the UID of the key.
Column 4: the fingerprint of the key.
Column 5: the UID of the associated sub key.
Column 6: the ID of the key.

Please note that

  • the last field of each line may have spaces (ex: PHP coder <php_coder@php.com>).
  • a key may have more than one sub key. Therefore, a line may have more than 6 columns.

Creating printed backups of private keys

You can produce graphical representations of your private keys. These representations can be printed on paper.

Please note that you should not need to produce graphical representations of your public keys. Indeed, public keys do not need to be protected. Therefore you can make copies of your public keys everywhere.

Install Data Matrix tools:

    $ sudo apt-get install  dmtx-utils

Generate a very long RSA keys:

    $ cat -n batch.txt
         1	%echo Generating a basic OpenPGP key
         2	Key-Type: RSA
         3	Key-Length: 8192
         4	Subkey-Type: RSA
         5	Subkey-Length: 8192
         6	Name-Real: Tester Long
         7	Name-Comment: This is for testing
         8	Name-Email: joe-long@foo.bar
         9	Expire-Date: 0
        10	Passphrase: abc
        11	# Do a commit here, so that we can later print "done" :-)
        12	%commit
        13	%echo done

    $ cat batch.txt | gpg --enable-large-rsa --batch --gen-key

Find the ID or the fingerprint of the generated keys:

By ID:

   $ gpg --list-keys
   sec   8192R/9BEF3AAC 2016-12-30
   uid                  Tester Long (This is for testing) <joe-long@foo.bar>
   ssb   8192R/3A57FB1C 2016-12-30

   $ gpg --export 9BEF3AAC > very-long-key.pub

   $ gpg --export-secret-key 9BEF3AAC > very-long-key.prv

By fingerprint:

    $ gpg --list-keys --fingerprint --with-colons
    pub:-:8192:1:FB9F45539BEF3AAC:2016-12-30:::-:Tester Long (This is for testing) <joe-long@foo.bar>::escaESCA:
    fpr:::::::::1335EF5C02BEF36A56DBA451FB9F45539BEF3AAC:
    sub:-:8192:1:1D4050C33A57FB1C:2016-12-30::::::esa:

Then generate images that represent the private key:

    $ gpg --export-secret-key 9BEF3AAC | paperkey --output-type raw | split -b 1500 - key-

    # Or:
    # gpg --export-secret-key 1335EF5C02BEF36A56DBA451FB9F45539BEF3AAC | paperkey --output-type raw | split -b 1500 - key-

    $ cat -n gen-images.sh
         1	#!/bin/bash
         2
         3	for K in key-*; do
         4	    dmtxwrite -e 8 $K > $K.png
         5	done

    $ ./gen-images.sh

    $ ls -1 *.png
    key-aa.png
    key-ab.png
    key-ac.png
    key-ad.png

The list of images that represents the private key is:

key-aa.png

key-ab.png

key-ac.png

key-ad.png

Regenerate keys from their graphical representations

    $ cat -n gen-key.sh
         1	#!/bin/bash
         2
         3	rm -f key.prv
         4	for K in key-*.png; do
         5	    echo $K
         6	    dmtxread $K >> key.prv
         7	done

    $ ./gen-key.sh

    $ paperkey --pubring ~/.gnupg/pubring.gpg --secrets key.prv > restore.raw

    # Or:
    # paperkey --pubring very-long-key.pub --secrets key.prv > restore.raw

Please note that you need the public key in order to regenerate the private key! The public key can be stored within the public keyring, or within a file.

Compare the restored key against the original:

    $ gpg --list-packets restore.raw > f1

    $ gpg --list-packets very-long-key.prv > f2

    $ diff f1 f2

If you try to import the restored private key:

    $ gpg --import restore.raw
    gpg: key 9BEF3AAC: already in secret keyring
    gpg: Total number processed: 1
    gpg:       secret keys read: 1
    gpg:  secret keys unchanged: 1

Sign a document with the original private key:

    $ gpg -u 9BEF3AAC --sign gen-key.sh

Remove the original private key from its keyring:

    $ gpg --delete-secret-keys 9BEF3AAC

Restore the secret key using the backup:

    $ gpg --import restore.raw
    gpg: key 9BEF3AAC: secret key imported
    gpg: key 9BEF3AAC: "Tester Long (This is for testing) <joe-long@foo.bar>" not changed
    gpg: Total number processed: 1
    gpg:              unchanged: 1
    gpg:       secret keys read: 1
    gpg:   secret keys imported: 1

Then, make sure that the restored secret key works as expected:

    $ gpg --output script.sh --decrypt gen-key.sh.gpg

    $ diff gen-key.sh script.sh

Useful links

https://www.void.gr/kargig/blog/2013/12/02/creating-a-new-gpg-key-with-subkeys/

http://www.spywarewarrior.com/uiuc/gpg/gpg-com-4.htm

luewell/gpg 适用场景与选型建议

luewell/gpg 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 19 次下载、GitHub Stars 达 0, 最近一次更新时间为 2019 年 09 月 25 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「gpg」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 luewell/gpg 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 luewell/gpg 我们能提供哪些服务?
定制开发 / 二次开发

基于 luewell/gpg 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 19
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 5
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2019-09-25