Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/CMS/PrivateKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
/**
* PrivateKey
*
* @author Alexander Danilov <adapik@yandex.ru>
* @copyright 2025 Alexander Danilov
* @license https://en.wikipedia.org/wiki/MIT_License MIT License
* @link https://github.com/Adapik/CMS
*/

namespace Adapik\CMS;

use Adapik\CMS\Interfaces\CMSInterface;
use Adapik\CMS\Interfaces\PEMConvertable;
use FG\ASN1\ASN1ObjectInterface;
use FG\ASN1\Universal\OctetString;
use FG\ASN1\Universal\Sequence;

/**
* Class PrivateKey
*
* @see Maps\PrivateKeyInfo
* @package Adapik\CMS
*/
class PrivateKey extends CMSBase implements PEMConvertable
{
const PEM_HEADER = "BEGIN PRIVATE KEY";
const PEM_FOOTER = "END PRIVATE KEY";

/**
* @param string $content
*
* @return PrivateKey
* @throws Exception\FormatException
*/
public static function createFromContent(string $content): CMSInterface
{
return new self(self::makeFromContent($content, Maps\PrivateKeyInfo::class, Sequence::class));
}

/**
* @return OctetString|ASN1ObjectInterface
*/
public function getKey(): OctetString
{
/** @var OctetString $octetString */
$octetString = $this->object->getChildren()[2];

return $octetString->detach();
}

/**
* @return AlgorithmIdentifier
*/
public function getKeyAlgorithm(): AlgorithmIdentifier
{
return new AlgorithmIdentifier($this->object->getChildren()[1]);
}

/**
* @return int
*/
public function getVersion(): int
{
/** @var \FG\ASN1\Universal\Integer $version */
$version = $this->object->getChildren()[0];

return (int)$version->__toString();
}

/**
* @return string
*/
public function getPEMHeader(): string
{
return self::PEM_HEADER;
}

/**
* @return string
*/
public function getPEMFooter(): string
{
return self::PEM_FOOTER;
}
}
64 changes: 64 additions & 0 deletions tests/CMS/PrivateKeyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/**
* PrivateKeyTest
*
* @author Alexander Danilov <adapik@yandex.ru>
* @copyright 2025 Alexander Danilov
* @license https://en.wikipedia.org/wiki/MIT_License MIT License
* @link https://github.com/Adapik/CMS
*/

declare(strict_types=1);

namespace Adapik\Test\CMS;

use Adapik\CMS\AlgorithmIdentifier;
use Adapik\CMS\Exception\FormatException;
use Adapik\CMS\PEMConverter;
use Adapik\CMS\PrivateKey;
use Exception;
use FG\ASN1\Universal\OctetString;
use PHPUnit\Framework\TestCase;

class PrivateKeyTest extends TestCase
{
public function testBase()
{
$privateKey = PrivateKey::createFromContent(base64_decode(file_get_contents(__DIR__ . '/../fixtures/private_key.key')));

self::assertInstanceOf(OctetString::class, $privateKey->getKey());
self::assertInstanceOf(AlgorithmIdentifier::class, $privateKey->getKeyAlgorithm());
self::assertIsInt($privateKey->getVersion());
self::assertEquals(0, $privateKey->getVersion());

$binary = $privateKey->getBinary();
$newPrivateKey = PrivateKey::createFromContent($binary);

self::assertEquals($binary, $newPrivateKey->getBinary());
}

/**
* @throws FormatException
* @throws Exception
*/
public function testGetPEM()
{
$privateKey = PrivateKey::createFromContent(base64_decode(file_get_contents(__DIR__ . '/../fixtures/private_key.key')));

$pem = PEMConverter::toPEM($privateKey);
preg_match('/-+([^-]+)-+(.*?)-+([^-]+)-+/ms', $pem, $matches);
self::assertSame($privateKey->getPEMHeader(), $matches[1]);
self::assertSame($privateKey->getPEMFooter(), $matches[3]);
self::assertSame($privateKey->getBase64(false), str_replace(["\r", "\n", "\r\n"], "", $matches[2]));
}

public function testKeyAlgorithm()
{
$privateKey = PrivateKey::createFromContent(base64_decode(file_get_contents(__DIR__ . '/../fixtures/private_key.key')));
$algorithm = $privateKey->getKeyAlgorithm();

self::assertInstanceOf(AlgorithmIdentifier::class, $algorithm);
// RSA encryption OID is 1.2.840.113549.1.1.1
self::assertStringContainsString('1.2.840.113549.1.1.1', $algorithm->getAlgorithmOid());
}
}
22 changes: 22 additions & 0 deletions tests/fixtures/private_key.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbls8JdwiYQzIwUA3xIxLg20AA
D8f8YU0SZqB0Mlrj+OpMYq1POzbj2ZGRhs5vGaSqbEM9aRyOYs97SC7IgQpkIf9L9joUy97i5v0c
gR+F3qIGsJU4UrTOr3XWbrmnuTGTvlNuYUyn956ZNctVuWud4O6Uf6CI+KJyg+U6faORBY6g5hPx
+XpOcpncXzO1m/1NYk4bHLVBhgWjILjAj5SnYKzukefh7VDJL1f981nDUTe5caCGOHQ1m2UMX+J8
hhEc9cGqDDX4va9psU2bMX6hHaRV0HFyVo6W/loyFnMclPqpKVJi18igZsuWNysXddSsIdf1yry3
ouk+zk0FAek/AgMBAAECggEAKVoBfAlP84h365Qvw/C0bear6zcNlI/oOSZdYfRXnGW0DZVpiKmg
gRDoMkz+E/rywOspUGe6am/eHZuuv7BZOhkS4AWq016/BuCz2onQW1/dCEB5oUUl6mQtXyezy4t6
9BUb1gX5rwnY8oYoV+jWhMEmfI16OEEGKmV/JeAiDL1zU7HK/kjDl1+UqOVlZJhRIpbQRkKioNgK
Kz67ks48xiexSkVOE474+tDqHDm2nyooPHeM78BwHa7QF6LU5edBAZSwCtowsq3CfsBP+UH4rF9T
AHqbbzUAlROhqCcVVg2Ur5J5/MuCVAOnLaSEF4idUSoftWQjX523QmmtPuvXhQKBgQD0MrPStybW
lugq3ggZuqqaIq/7Skb/XjEFh1HwboRRY+ylpw4ccDSccqnQ2zry2ocNSWRR0sAmTa/WyB1QV6DF
s6/Ux+bUgzCLrUpJcji3agZ/yxpP22USnWueJ4X4fAdPA7icjnuKps2It4QzL3Akyj8uCR5YYHot
PaZaIu0NCwKBgQDmM6LisR1rspRhEKe5Ps9Km+cWEXt4ZVCPp6DMingMQVaQaYgKMdekUYEEji7m
UMTI9rMJKgd/AzWFBtTHALohVmHyU91kGKTq+PHlgBqfMdz7XvufQ9vCpQ0iXv5teSdcZ5zBwEqg
8yiKJa18w60YwNfOs5onQeKizJA9y2WtHQKBgQDbqGv2xAbsiivmu3gf5LgnE8Km0MaSWODRr0p2
coMVT0QEMgPQcuHwRHgaRcl9wDIxMm5J58At9D2SfslXhqs4iwxD41jwmd1mFwe2j0cTPQ0BkOln
IduF5hJy3ex1euN8IMXEFaptKuPA8gq5nBdNEJvKRiuEpavg3A6yD66ZxwKBgAkuihKsqdW/27c7
aNU0siPxr/frXpdgwyPXtcwywbMLcB52dxyg/E7riU/bM/evxQmW/XEiIgZo5FuX+Civ2Ly3q1R1
D+GMMhfFMjDLOUScQoosMbEajctntexnCZLoQC+pWlFIEDH3QOixtAfD7kWMe48dDsr+D8Iakq5E
TJvpAoGAY554roLw1gJNTDheBI54tkZtrJGxMEgE2BTxzc1OWN6Rd6haAzt/7TEwWn9lbvp0yj56
XE75lr+IshtTYwCPBpYgRYp81sWO/Ip2+yybgH11BH2gNa8SZk25f+JUO3o9JDf7pZ8HFHVOBsYE
6AcYXjZYbHrKbgyxymTmYkXU/ig=