tiagogoddard/cryptoapi

此包已被废弃,不再维护。未建议替代包。

提供用于实现与客户端软件加密API的类

v1.0.4 2015-10-07 18:19 UTC

This package is not auto-updated.

Last update: 2020-11-17 16:47:37 UTC


README

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

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

这正是CryptoApi能帮到你的地方。

密码学基础

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

加密交互应该如何实现?

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

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

服务器端实现

包安装

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

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

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

如果你不使用上述包,你应该手动注册服务提供程序。要这样做,请将以下元素添加到你的app/config/app.php中的"providers"部分:'TiagoGoddard\Cryptoapi\CryptoapiServiceProvider',

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

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

生成密钥对

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

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

默认情况下,密钥大小为1024,但您可以通过指定--keySize=XXXX选项来覆盖默认值,用于上面的artisan命令。但是,对于大多数情况,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对象。目前,它创建的是实现了TiagoGoddard\Cryptoapi\Cryptography\CryptographyInterface接口的TiagoGoddard\Cryptoapi\Cryptography\RsaAesCryptography对象。此对象包含我们将需要的所有加密、解密和签名方法。

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

还要注意,/api/init的响应不是加密的(以防初始化失败)。响应消息也不是“说话”的。客户端应该知道例如“ERROR 1”代表什么。这是为了不告诉可能的黑客API的内部结构。

最后,如果上述代码符合您的需求,您可以使用预先准备好的特性来做到这一点,即TiagoGoddard\Cryptoapi\Traits\RsaAesControllerTrait,它已经包含了此postInit方法。

use TiagoGoddard\Cryptoapi\Traits\RsaAesControllerTrait;

或者如果您太懒了,或者您的IDE没有自动插入特性的完整名称,您可以直接

use RsaAesControllerTrait;

特性的别名已在包的ServiceProvider中注册。

特定于应用的API

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

在继续编码之前,我应该提醒,所有来自客户端的数据都是加密的(使用AES)。为了方便解密传入的数据,我实现了DecryptedInput Facade,它与Laravel的Input Facade几乎相同,只是它允许从请求中获取解密后的值。您无需担心将DecryptedInput添加为别名——它已自动在包的ServiceProvider中为您添加。

让我们为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——此方法的输出不应加密。

如果对加密和签名的工作方式感到好奇,请查看TiagoGoddard\Cryptoapi\Filters\OutgoingCryptFilter.php

客户端示例

以下是客户端的C#示例:https://github.com/TiagoGoddard/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。为此,您可以使用预制的特性

class ApiController extends BaseController {

    use \TiagoGoddard\Cryptoapi\Traits\RsaAesControllerTrait;
    use \TiagoGoddard\Cryptoapi\Traits\TestsControllerTrait;

}

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

密钥

如果您还没有做,请生成一个密钥对:php artisan cryptoapi:generatekeys

运行测试

启动服务器

首先确保您在项目的根目录中(其中包含composer.jsonartisan)。

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

测试

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

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

如果您一切都做得正确,您应该在最后看到绿色消息

OK (1 test, 6 assertions)

创建自己的测试

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

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