mgazelle/cryptoapi

提供类以实现与客户端软件的加密API交互

v1.0.4 2014-05-25 09:18 UTC

This package is not auto-updated.

Last update: 2024-10-02 11:01:35 UTC


README

提供实现与客户端应用程序加密交互的类

想象你有一个客户端应用程序,比如用C#编写的,它需要与服务器API进行安全的(加密)交互。

这就是CryptoApi能帮助你的地方。

密码学基础

我先说,我对密码学不是很擅长,但我理解了基础知识,这帮助我编写了这个包。

如何实现加密交互?

该算法基于两种密码学算法:对称和非对称。在这种情况下,AES用于对称,RSA用于非对称。如果你不知道这是什么意思 - 你应该在Google上搜索 :) 互联网上有大量关于这个主题的文章。

  1. 首先,服务器有自己的私钥。客户端有自己的对应公钥(或证书)
  2. 客户端为对称算法生成一个随机会话密钥。
  3. 然后客户端使用非对称算法加密这个密钥并发送给服务器。
  4. 服务器接收密钥,并响应客户端,告诉它是否成功接收密钥。服务器还将此密钥存储在会话中,以供未来与客户端通信。
  5. 后来,客户端发送给服务器的任何数据都使用对称算法加密,使用生成的密钥。
  6. 服务器发送给客户端的所有响应也使用对称算法加密,同时使用服务器私钥签名,以便客户端可以验证数据实际上来自预期的服务器。

服务器端实现

包安装

将以下内容添加到Laravel项目的composer.json中的"require"部分: "amegatron/cryptoapi": "1.0.*"

之后,在控制台中运行composer update,并确保已下载包。

如果你使用rtablada/package-installer,你可以运行php artisan package:install amegatron/cryptoapi来自动将包的Service Provider添加到应用程序配置中。

如果你不使用上述包,你应该手动注册Service Provider。为此,在app/config/app.php的"providers"部分中添加以下元素: 'Amegatron\Cryptoapi\CryptoapiServiceProvider',

此外,你还必须“注册”包提供的输出加密过滤器。在app/filters.php中添加以下内容:

Route::filter('cryptOut', 'Amegatron\Cryptoapi\Filters\OutgoingCryptFilter');

生成密钥对

接下来,你需要生成一个用于非对称加密的密钥对。要生成密钥对,请在控制台中运行以下命令:php artisan cryptoapi:generatekeys

默认情况下,将在app/keys文件夹中创建两个文件:private.keypublic.crtpublic.crt仅由客户端需要,因此可以在将其嵌入客户端软件后删除。但private.key应该保持安全。因为你的服务器指向网站中的public文件夹,所以该文件不可通过互联网访问。它也不应该。永远。保密此文件。除了你的服务器外,没有人应该知道这个密钥。

默认情况下,密钥大小为1024,但可以通过指定上面 artisan 命令的 --keySize=XXXX 选项来覆盖默认值。但1024对于大多数情况已经足够了。此外,我没有生成2048大小的密钥 - 它花了我太长时间,所以我取消了进程 :)

实现服务器端API

现在一切准备就绪,可以编写您的API了。

为了简化,我建议所有的服务器API逻辑都包含在一个名为ApiController的单个控制器中。因此,在app/controllers中创建ApiController.php,并在app/routes.php中为其创建一个路由。

Route::controller('api', 'ApiController');

正如您可能已经猜到的,我们所有的加密交互都将被导向http://youdomain.com/api

现在,让我们开始实现控制器。

初始化

首先,我们需要实现算法中列出的第3和第4点。AES算法的密钥由两部分组成:keyiv。它们都应该通过POST方式发送到/api/init

为此,我建议以下代码

public function postInit() {
    if (!(Input::has('key') && Input::has('iv'))) {
        return 'ERROR 1';
    }

    $crypt = App::make('CryptographyInterface');

    extract(Input::only('key', 'iv'));
    $key = $crypt->asymmetricDecrypt($key);
    $iv = $crypt->asymmetricDecrypt($iv);

    if (!($key && $iv)) {
        return 'ERROR 2';
    }

    $crypt->initSymmetric(array(
        'key'   => $key,
        'iv'    => $iv,
    ));

    return 'OK';
}

这里需要注意什么?首先,通过Laravel IoC创建$crypt对象。当前,它创建了一个实现接口Amegatron\Cryptoapi\Cryptography\CryptographyInterfaceAmegatron\Cryptoapi\Cryptography\RsaAesCryptography对象。该对象包含我们将需要的所有加密、解密和签名方法。

其次,是初始化对称“驱动”($crypt->initSymmetric(...);)。它将AES密钥存储在会话中以供以后使用。

还要注意的是,来自/api/init的响应未加密(以防初始化失败)。此外,响应消息不是“说话的”。客户端应该知道例如“ERROR 1”是什么意思。这是为了不向可能的黑客透露API的内部结构。

最后,如果您喜欢以下代码,可以使用预先制作的特性Amegatron\Cryptoapi\Traits\RsaAesControllerTrait,它已经包含了这个postInit方法。

use Amegatron\Cryptoapi\Traits\RsaAesControllerTrait;

或者,如果您太懒或者您的IDE没有自动插入特性完整名称,您只需

use RsaAesControllerTrait;

特性的别名已在包服务提供者中注册。

特定应用的API

为了演示目的,让我们假设我们的服务器应用的目的在于验证客户端的许可证:客户端向服务器发送许可证密钥,服务器响应,告知该许可证密钥是否有效以及是否允许客户端软件进一步使用。

在继续编码之前,我应该提醒,所有来自客户端的数据都是加密的(使用AES)。为了方便解密传入数据,我实现了一个DecryptedInput门面,它几乎与Laravel的Input门面相同,只是它允许从请求中获取解密值。您不必担心为DecryptedInput添加别名 - 它已在包服务提供者中自动添加。

让我们为api/checklicense路由创建一个方法

public function postChecklicense() {

    $licenseKey = DecryptedInput::get('licenseKey');

    // Perform some logic to determine whether the received license key is valid or has not expired for example
    // Most probably retreiving this info from database.
    $licenseIsValid     = true;
    $licenseExpiresAt   = '2014-12-31 23:59:59';

    $response = array(
        'isValid'   => $licenseIsValid,
        'liceseExpiresAt'   => $licenseExpiresAt;
    );

    return json_encode($response);
}

请注意,postChecklicense不关心加密传出数据。为此,我们将使用前面提到的预制cryptOut过滤器。为此,让我们为我们的ApiController创建一个构造函数

public function __construct() {
    $this->afterFilter(
        'cryptOut',
        array(
            'except'   => array('postInit'),
        )
    );
}

cryptOut过滤器做两件事:首先,加密传出数据;其次,签名数据。总结来说,它向客户端发送JSON编码的对象,包含两个字段

  1. data - 加密数据
  2. sign - 加密数据的签名

请注意,此过滤器应用于所有控制器方法,除了我之前提到的postInit - 此方法的输出不应加密。

如果您对此感兴趣,请参阅Amegatron\Cryptoapi\Filters\OutgoingCryptFilter.php了解加密和签名是如何进行的。

客户端示例

这是一个C#客户端的示例:https://github.com/Amegatron/CryptoApiExample

测试您的CryptoApi

您可以使用CodeCeption测试CryptoApi。我已经提供了一个演示测试,该测试测试了一个简单的“echo”API方法:测试向服务器发送加密的message,并期望在响应中收到它,也是加密和签名的。

要运行此测试,您需要做以下操作

先决条件

您需要在您的composer.json中添加以下内容

    "require-dev": {
        "codeception/codeception": "1.8.5",
        "guzzle/plugin": "3.9.1"
    },

之后,在控制台中运行 composer update 命令。这将安装 CodeCeption 本身以及额外的 guzzle/plugin,这是进行测试所必需的。

服务器API

确保你有一个如前所述的 ApiController。对于这个测试,这个控制器必须有两个方法:postInitpostTestEcho。为此,你可以简单地使用预制的 Traits。

class ApiController extends BaseController {

    use \Amegatron\Cryptoapi\Traits\RsaAesControllerTrait;
    use \Amegatron\Cryptoapi\Traits\TestsControllerTrait;

}

同时确保你有一个对应于此控制器的 api 路由以及一个 cryptOut 过滤器(如前所述)。

密钥

如果你还没有这样做,生成一个密钥对:php artisan cryptoapi:generatekeys

运行测试

启动服务器

首先确保你处于项目的根目录(其中包含 composer.jsonartisan)。

之后启动服务器:php artisan serve &。它将在端口 8000 上启动服务器。这可能需要一些时间,只需等待显示服务器已启动的消息。

测试

现在切换到包目录:cd vendor/amegatron/cryptoapi

现在你可以运行测试。执行以下命令:../..bin/codecapt run 等待它完成。

如果你一切操作正确,你应该在最后看到绿色消息

OK (1 test, 6 assertions)

创建自己的测试

如果你是 CodeCeption 的新手,请访问其官方网站:http://codeception.com/

目前我无法提供任何用于测试的 API,但你应该研究现有的测试 tests/cryptoapi/CheckEchoCept.php 和它使用的辅助类:tests/_helpers/CryptoApiHelper.php。在那里你可以找到测试使用的加密算法的实现,这些算法使用的是与 Laravel 一起提供的 phpseclib