13/01/2019 - PHP, SYMFONY
Bu örnekte, istemci verileri sunucuya göndermeden önce imzalamak için OpenSSL'yi kullanacak. Sunucu verileri aldığında, verilen imzayla imzalanıp imzalanmadığını kontrol etmek için OpenSSL kullanır. Doğrulama başarılı olduysa, istemci güvenilir olarak kabul edilir. Bu süreçte istemci "private key", sunucu doğrulama işlemi için istemcinin "public key" bilgisini kullanır. Daha fazla bilgi için Signing HTTP Messages, openssl_sign ve openssl_verify sayfalarını okuyun.
Burada biraz tembellik yapıp controller içinde çok fazla iş yapıyorum ama bunun nedeni yazıyı mümkün olduğunca kısa tutmak istememdir. Siz mantığı bir servis sınıfına taşıyabilirsiniz - sadece bir örnek.
namespace App\Controller;
use App\Util\SignatureUtil;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/signatures")
*/
class SignatureController
{
private $signatureUtil;
private $publicKey;
private $privateKey;
public function __construct(
SignatureUtil $signatureUtil,
string $publicKey,
string $privateKey
) {
$this->signatureUtil = $signatureUtil;
$this->publicKey = $publicKey;
$this->privateKey = $privateKey;
}
/**
* @Route("/client/create-keys", methods={"GET"})
*/
public function createKeys(): Response
{
$keys = $this->signatureUtil->createKeys();
file_put_contents($this->publicKey, $keys['public_key']);
file_put_contents($this->privateKey, $keys['private_key']);
return new JsonResponse('Done!');
}
/**
* @Route("/client/create-signature", methods={"POST"})
*/
public function createSignature(Request $request): Response
{
$privateKey = file_get_contents($this->privateKey);
$data = json_decode($request->getContent(), true)['data'];
$signature = $this->signatureUtil->createSignature($data, $privateKey);
return new JsonResponse($signature);
}
/**
* @Route("/server/verify-signature", methods={"POST"})
*/
public function verifySignature(Request $request): Response
{
$publicKey = file_get_contents($this->publicKey);
$data = json_decode($request->getContent(), true)['data'];
$signature = $request->headers->get('X-Signature');
$valid = $this->signatureUtil->verifySignature($data, $signature, $publicKey);
return new JsonResponse(['valid' => $valid]);
}
}
services:
...
App\Controller\SignatureController:
arguments:
$publicKey: '%kernel.project_dir%/config/ssl/public_key.pem'
$privateKey: '%kernel.project_dir%/config/ssl/private_key.pem'
/client/create-keys
- İstemcinin private ve public anahtarlarını programla oluşturmak için bu yolu kullanın. Daha sonra istemciye private, sunucuya ise public anahtarı verin. Not: Bu yolu sadece bilgi amaçlı ekledim, bu yüzden bunu gerçek dünyadaki durumlarda yapmamalısınız. Bunun yerine, bu anahtarları terminalde oluşturun./client/create-signature
- Verilerinizi imzalamak için bu yolu kullanın. Bu yol, asıl verilerle birlikte sunucuya gönderilecek olan imzayı içerecektir. Not: Bu yolun istemci API'sinde olduğunu varsayalım./server/verify-signature
- İstemci talebi gönderdiğinde, imza doğrulamanın yapıldığı yer bura olacaktır. Not: Bu yolun sunucu API'sinde olduğunu varsayalım.Lütfen koddaki yorumları okuyun.
namespace App\Util;
use RuntimeException;
class SignatureUtil
{
/**
* This is only relevant to server API.
*
* The "public_key" is shared with server.
* The "private_key" is kept securely by the client only.
*/
public function createKeys(): array
{
$keys = openssl_pkey_new(['private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA]);
openssl_pkey_export($keys, $privateKey);
$keyDetails = openssl_pkey_get_details($keys);
$publicKey = $keyDetails['key'];
return [
'public_key' => $publicKey,
'private_key' => $privateKey,
];
}
/**
* This is only relevant to client API.
*
* The client calls this method to create a "signature" based on the "data".
* The client then sends the "signature" and the "data" to the server.
*/
public function createSignature(string $data, string $privateKey): string
{
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}
/**
* This is only relevant to server API.
*
* The server checks "data" against the "signature" to see if the client is genuine or not.
*/
public function verifySignature(string $data, string $signature, string $publicKey): bool
{
$verify = openssl_verify($data, base64_decode($signature), $publicKey, 'sha256WithRSAEncryption');
if (1 == $verify) {
return true;
}
if (0 === $verify) {
return false;
}
throw new RuntimeException('An error occurred while verifying the signature.');
}
}
$ ls -l config/ssl/
total 0
$ curl -X GET \
> http://localhost/signatures/client/create-keys
"Done!"
$ ls -l config/ssl/
-rw-rw-rw- 1 501 dialout 1704 Jan 13 2019 private_key.pem
-rw-rw-rw- 1 501 dialout 451 Jan 13 2019 public_key.pem
$ curl -X POST \
http://localhost/signatures/client/create-signature \
-H 'Content-Type: application/json' \
-d '{
"data": "Hello World!"
}'
"S3PRm0yePp3fIgEhtIYXq+oQrFkbkxSHiJHbyESu0pLRILg7N2AWrdsvkK6LzK9F2WB1DKXrAAXD3L+R0au2penzBCnX9ERtJXZq71h2qVZYbkrxbY4jQsYU/aUg3KxakVQxTi4gehj0zeGvTV6OxkjFbKq0/KLgWD9AjYZxeWnmFhh8wbbGtmFMZzBkGU7iXSC+/5kKguI28WWt1ALYHJJxSsJWmZnccnAELo6tVhqBKxavuB/E6TZTEiWGeTC0wAWcjuqfEIFoA5b1fx73L6vcCGqf17Ov42NIJL50BRlmRcHkmnNJXVUUbSLmI+Cu+uzwE3KN9z/pVv8dfLIFAA=="
İmzayı X-Signature
kafasında kullanın.
$ curl -X POST \
> http://localhost/signatures/server/verify-signature \
> -H 'Content-Type: application/json' \
> -H 'X-Signature: S3PRm0yePp3fIgEhtIYXq+oQrFkbkxSHiJHbyESu0pLRILg7N2AWrdsvkK6LzK9F2WB1DKXrAAXD3L+R0au2penzBCnX9ERtJXZq71h2qVZYbkrxbY4jQsYU/aUg3KxakVQxTi4gehj0zeGvTV6OxkjFbKq0/KLgWD9AjYZxeWnmFhh8wbbGtmFMZzBkGU7iXSC+/5kKguI28WWt1ALYHJJxSsJWmZnccnAELo6tVhqBKxavuB/E6TZTEiWGeTC0wAWcjuqfEIFoA5b1fx73L6vcCGqf17Ov42NIJL50BRlmRcHkmnNJXVUUbSLmI+Cu+uzwE3KN9z/pVv8dfLIFAA==' \
> -d '{
> "data": "Hello World!"
> }'
{"valid":true}