mgazelle / cryptoapi
提供类以实现与客户端软件的加密API交互
Requires
- php: >=5.3.7
- illuminate/support: 4.1.*
This package is not auto-updated.
Last update: 2024-10-02 11:01:35 UTC
README
提供实现与客户端应用程序加密交互的类
想象你有一个客户端应用程序,比如用C#编写的,它需要与服务器API进行安全的(加密)交互。
这就是CryptoApi能帮助你的地方。
密码学基础
我先说,我对密码学不是很擅长,但我理解了基础知识,这帮助我编写了这个包。
如何实现加密交互?
该算法基于两种密码学算法:对称和非对称。在这种情况下,AES用于对称,RSA用于非对称。如果你不知道这是什么意思 - 你应该在Google上搜索 :) 互联网上有大量关于这个主题的文章。
- 首先,服务器有自己的私钥。客户端有自己的对应公钥(或证书)
- 客户端为对称算法生成一个随机会话密钥。
- 然后客户端使用非对称算法加密这个密钥并发送给服务器。
- 服务器接收密钥,并响应客户端,告诉它是否成功接收密钥。服务器还将此密钥存储在会话中,以供未来与客户端通信。
- 后来,客户端发送给服务器的任何数据都使用对称算法加密,使用生成的密钥。
- 服务器发送给客户端的所有响应也使用对称算法加密,同时使用服务器私钥签名,以便客户端可以验证数据实际上来自预期的服务器。
服务器端实现
包安装
将以下内容添加到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.key
和public.crt
。 public.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算法的密钥由两部分组成:key
和iv
。它们都应该通过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\CryptographyInterface
的Amegatron\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编码的对象,包含两个字段
- data - 加密数据
- 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
。对于这个测试,这个控制器必须有两个方法:postInit
和 postTestEcho
。为此,你可以简单地使用预制的 Traits。
class ApiController extends BaseController {
use \Amegatron\Cryptoapi\Traits\RsaAesControllerTrait;
use \Amegatron\Cryptoapi\Traits\TestsControllerTrait;
}
同时确保你有一个对应于此控制器的 api
路由以及一个 cryptOut
过滤器(如前所述)。
密钥
如果你还没有这样做,生成一个密钥对:php artisan cryptoapi:generatekeys
。
运行测试
启动服务器
首先确保你处于项目的根目录(其中包含 composer.json
和 artisan
)。
之后启动服务器: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
。