Skip to content

Commit 85c0499

Browse files
authored
Merge pull request #46 from spotlibs/feature/security-helper
Feature/security helper
2 parents 02c2f08 + 209f893 commit 85c0499

File tree

3 files changed

+136
-3
lines changed

3 files changed

+136
-3
lines changed

src/Libraries/Security.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/**
4+
* PHP version 8
5+
*
6+
* @category Library
7+
* @package Libraries
8+
* @author Made Mas Adi Winata <m45adiwinata@gmail.com>
9+
* @license https://mit-license.org/ MIT License
10+
* @version GIT: 0.5.0
11+
* @link https://github.com/spotlibs
12+
*/
13+
14+
declare(strict_types=1);
15+
16+
namespace Spotlibs\PhpLib\Libraries;
17+
18+
/**
19+
* Security
20+
*
21+
* Security helper
22+
*
23+
* @category Library
24+
* @package Security
25+
* @author Made Mas Adi Winata <m45adiwinata@gmail.com>
26+
* @license https://mit-license.org/ MIT License
27+
* @link https://github.com/spotlibs
28+
*/
29+
class Security
30+
{
31+
/**
32+
* Encrypting sensitive string data
33+
*
34+
* @param string $plaintext string to encrypt
35+
*
36+
* @throws \Exception
37+
* @return bool|string
38+
*/
39+
public static function encrypt(string $plaintext): string
40+
{
41+
$charset = array_merge(
42+
range('0', '9'),
43+
range('a', 'z'),
44+
range('A', 'Z'),
45+
);
46+
$ivArr = [];
47+
for ($i = 0; $i < 16; $i++) {
48+
$ivArr[] = $charset[random_int(0, 61)];
49+
}
50+
$iv = implode('', $ivArr);
51+
$ecrypted = openssl_encrypt($plaintext, "AES-128-CBC", env('SECURITY_KEY'), OPENSSL_RAW_DATA, $iv);
52+
if (!$ecrypted) {
53+
throw new \Exception("failed to encrypt string");
54+
}
55+
return bin2hex($iv . $ecrypted);
56+
}
57+
58+
/**
59+
* Decrypt encrypted string
60+
*
61+
* @param string $encrypted string to decrypt
62+
*
63+
* @throws \Exception
64+
* @return bool|string
65+
*/
66+
public static function decrypt(string $encrypted): string
67+
{
68+
69+
$ivHex = substr($encrypted, 0, 32);
70+
$iv = hex2bin($ivHex);
71+
$encrypted = substr($encrypted, 32);
72+
$decrypted = openssl_decrypt(hex2bin($encrypted), "AES-128-CBC", env('SECURITY_KEY'), OPENSSL_RAW_DATA, $iv);
73+
if (!$decrypted) {
74+
throw new \Exception("failed to decrypt string");
75+
}
76+
return $decrypted;
77+
}
78+
}

tests/Libraries/ClientExternalTest.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Tests\Libraries;
66

7+
use GuzzleHttp\Exception\ConnectException;
78
use GuzzleHttp\Exception\GuzzleException;
89
use GuzzleHttp\HandlerStack;
910
use GuzzleHttp\Psr7\MultipartStream;
@@ -31,7 +32,11 @@ public function testCallEksternal1(): void
3132
['content-type' => 'application/json'],
3233
json_encode(['message' => 'hello world'])
3334
);
34-
$client = new ClientExternal();
35+
$mock = new MockHandler([
36+
new Response(200, ['Content-Type' => 'application/json'], json_encode(['status' => 'ok', 'message' => 'hello world'])),
37+
]);
38+
$handlerStack = new HandlerStack($mock);
39+
$client = new ClientExternal(['handler' => $handlerStack]);
3540
$response = $client->call($request);
3641
$contents = $response->getBody()->getContents();
3742
$contents_arr = json_decode($contents, true, 512);
@@ -50,6 +55,11 @@ public function testCallExternalMultipartSuccess(): void
5055
$f = fopen('public/docs/hello.txt', 'w');
5156
fwrite($f, 'hello world');
5257
fclose($f);
58+
$mock = new MockHandler([
59+
new Response(200, ['Content-Type' => 'application/json'], json_encode(['id' => '101', 'status' => 'ok', 'message' => 'well done'])),
60+
]);
61+
$handlerStack = new HandlerStack($mock);
62+
$client = new ClientExternal(['handler' => $handlerStack]);
5363
$request = new Request(
5464
'POST',
5565
'https://jsonplaceholder.typicode.com/posts',
@@ -61,7 +71,6 @@ public function testCallExternalMultipartSuccess(): void
6171
]
6272
])
6373
);
64-
$client = new ClientExternal();
6574
$resp = $client
6675
->injectRequestHeader(['X-Unit-Test' => ['clover']])
6776
->injectResponseHeader(['X-Unit-Test-Response' => ['clover-response']])
@@ -88,7 +97,11 @@ public function testCallExternalMultipartError(): void
8897
]
8998
])
9099
);
91-
$client = new ClientExternal();
100+
$mock = new MockHandler([
101+
new ConnectException('simulated error', $request)
102+
]);
103+
$handlerStack = new HandlerStack($mock);
104+
$client = new ClientExternal(['handler' => $handlerStack]);
92105
$client->call($request);
93106
}
94107

tests/Libraries/SecurityTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Libraries;
6+
7+
use Laravel\Lumen\Testing\TestCase;
8+
use Spotlibs\PhpLib\Libraries\Security;
9+
10+
class SecurityTest extends TestCase
11+
{
12+
public function createApplication()
13+
{
14+
return require __DIR__.'/../../bootstrap/app.php';
15+
}
16+
17+
public function testEncrypt1(): void
18+
{
19+
putenv('SECURITY_KEY=123456789ABCDefg');
20+
$plain = 'beautiful soup';
21+
$encrypted = Security::encrypt($plain);
22+
$decrypted = Security::decrypt($encrypted);
23+
$this->assertEquals($plain, $decrypted);
24+
}
25+
26+
public function testDecrypt1(): void
27+
{
28+
putenv('SECURITY_KEY=0123456789abcdef');
29+
$encrypted = '69687168694E496177653970746B6834383021D52B533A55ECBA5BACC753055AD59F65DD091541A32FA262B3116CFDC3';
30+
$decrypted = Security::decrypt($encrypted);
31+
$this->assertEquals('AES CBC with secure random IV', $decrypted);
32+
}
33+
34+
public function testEncryptError(): void
35+
{
36+
$this->expectException(\Exception::class);
37+
putenv('SECURITY_KEY=0123456789abcd');
38+
$x = Security::encrypt('beautiful soup');
39+
putenv('SECURITY_KEY=0123456789abcd321');
40+
Security::decrypt($x);
41+
}
42+
}

0 commit comments

Comments
 (0)