chore: promote method

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
This commit is contained in:
Vitor Mattos 2025-11-13 14:14:21 -03:00
parent 456e27f0f5
commit d5b7cb39f3
No known key found for this signature in database
GPG key ID: 6FECE2AD4809003A
4 changed files with 92 additions and 120 deletions

View file

@ -26,6 +26,7 @@ use OCP\ITempManager;
use OCP\IURLGenerator;
use OpenSSLAsymmetricKey;
use OpenSSLCertificate;
use Psr\Log\LoggerInterface;
use ReflectionClass;
/**
@ -79,6 +80,7 @@ abstract class AEngineHandler implements IEngineHandler {
protected CertificatePolicyService $certificatePolicyService,
protected IURLGenerator $urlGenerator,
protected CaIdentifierService $caIdentifierService,
protected LoggerInterface $logger,
) {
$this->appData = $appDataFactory->get('libresign');
}
@ -759,4 +761,90 @@ abstract class AEngineHandler implements IEngineHandler {
return 'validation_error';
}
}
#[\Override]
public function generateCrlDer(array $revokedCertificates, string $instanceId, int $generation, int $crlNumber): string {
$configPath = $this->getConfigPathByParams($instanceId, $generation);
$caCertPath = $configPath . DIRECTORY_SEPARATOR . 'ca.pem';
$caKeyPath = $configPath . DIRECTORY_SEPARATOR . 'ca-key.pem';
$crlDerPath = $configPath . DIRECTORY_SEPARATOR . 'crl.der';
if (!file_exists($caCertPath) || !file_exists($caKeyPath)) {
throw new \RuntimeException('CA certificate or private key not found. Run: occ libresign:configure:openssl');
}
try {
$caCert = file_get_contents($caCertPath);
$caKey = file_get_contents($caKeyPath);
if (!$caCert || !$caKey) {
throw new \RuntimeException('Failed to read CA certificate or private key');
}
$issuer = new \OCA\Libresign\Vendor\phpseclib3\File\X509();
$issuer->loadX509($caCert);
$caPrivateKey = \OCA\Libresign\Vendor\phpseclib3\Crypt\PublicKeyLoader::load($caKey);
if (!$caPrivateKey instanceof \OCA\Libresign\Vendor\phpseclib3\Crypt\Common\PrivateKey) {
throw new \RuntimeException('Loaded key is not a private key');
}
$issuer->setPrivateKey($caPrivateKey);
$utc = new \DateTimeZone('UTC');
$now = (new \DateTime())->setTimezone($utc);
$nextWeek = (new \DateTime('+7 days'))->setTimezone($utc);
$revokedList = [];
foreach ($revokedCertificates as $cert) {
$revokedList[] = [
'userCertificate' => new \OCA\Libresign\Vendor\phpseclib3\Math\BigInteger($cert->getSerialNumber(), 16),
'revocationDate' => ['utcTime' => $cert->getRevokedAt()->format('D, d M Y H:i:s O')],
];
}
$crlStructure = [
'tbsCertList' => [
'version' => 'v2',
'signature' => ['algorithm' => 'sha256WithRSAEncryption'],
'issuer' => $issuer->getSubjectDN(\OCA\Libresign\Vendor\phpseclib3\File\X509::DN_ARRAY),
'thisUpdate' => ['utcTime' => $now],
'nextUpdate' => ['utcTime' => $nextWeek],
'revokedCertificates' => $revokedList,
],
'signatureAlgorithm' => ['algorithm' => 'sha256WithRSAEncryption'],
];
$crl = new \OCA\Libresign\Vendor\phpseclib3\File\X509();
$crl->loadCRL($crlStructure);
$crl->setSerialNumber($crlNumber);
$crl->setStartDate(new \DateTime('-1 minute'));
$crl->setEndDate(new \DateTime('+7 days'));
$signedCrl = $crl->signCRL($issuer, $crl, 'sha256WithRSAEncryption');
if ($signedCrl === false) {
throw new \RuntimeException('Failed to sign CRL with phpseclib3');
}
if (!isset($signedCrl['signatureAlgorithm'])) {
$signedCrl['signatureAlgorithm'] = ['algorithm' => 'sha256WithRSAEncryption'];
}
$crlDerData = $crl->saveCRL($signedCrl, \OCA\Libresign\Vendor\phpseclib3\File\X509::FORMAT_DER);
if ($crlDerData === false) {
throw new \RuntimeException('Failed to save CRL in DER format');
}
if (file_put_contents($crlDerPath, $crlDerData) === false) {
throw new \RuntimeException('Failed to write CRL DER file');
}
return $crlDerData;
} catch (\Exception $e) {
$this->logger->error('CRL generation failed: ' . $e->getMessage(), ['exception' => $e]);
throw new \RuntimeException('Failed to generate CRL: ' . $e->getMessage(), 0, $e);
}
}
}

View file

@ -66,6 +66,7 @@ class CfsslHandler extends AEngineHandler implements IEngineHandler {
$certificatePolicyService,
$urlGenerator,
$caIdentifierService,
$logger,
);
$this->cfsslServerHandler->configCallback(fn () => $this->getCurrentConfigPath());
@ -488,38 +489,6 @@ class CfsslHandler extends AEngineHandler implements IEngineHandler {
return $return;
}
#[\Override]
public function generateCrlDer(array $revokedCertificates, string $instanceId, int $generation, int $crlNumber): string {
try {
$queryParams = [];
$queryParams['expiry'] = '168h'; // 7 days * 24 hours
$response = $this->getClient()->request('GET', 'crl', [
'query' => $queryParams
]);
$responseData = json_decode((string)$response->getBody(), true);
if (!isset($responseData['success']) || !$responseData['success']) {
$errorMessage = isset($responseData['errors'])
? implode(', ', array_column($responseData['errors'], 'message'))
: 'Unknown CFSSL error';
throw new \RuntimeException('CFSSL CRL generation failed: ' . $errorMessage);
}
if (isset($responseData['result']) && is_string($responseData['result'])) {
return $responseData['result'];
}
throw new \RuntimeException('No CRL data returned from CFSSL');
} catch (RequestException|ConnectException $e) {
throw new \RuntimeException('Failed to communicate with CFSSL server: ' . $e->getMessage());
} catch (\Throwable $e) {
throw new \RuntimeException('CFSSL CRL generation error: ' . $e->getMessage());
}
}
/**
* Get Authority Key Identifier from certificate (needed for CFSSL revocation)
*

View file

@ -40,8 +40,8 @@ class OpenSslHandler extends AEngineHandler implements IEngineHandler {
protected IURLGenerator $urlGenerator,
protected SerialNumberService $serialNumberService,
protected CaIdentifierService $caIdentifierService,
protected CrlMapper $crlMapper,
protected LoggerInterface $logger,
protected CrlMapper $crlMapper,
) {
parent::__construct(
$config,
@ -52,6 +52,7 @@ class OpenSslHandler extends AEngineHandler implements IEngineHandler {
$certificatePolicyService,
$urlGenerator,
$caIdentifierService,
$logger,
);
}
@ -398,90 +399,4 @@ class OpenSslHandler extends AEngineHandler implements IEngineHandler {
protected function getSetupErrorTip(): string {
return 'Run occ libresign:configure:openssl --help';
}
#[\Override]
public function generateCrlDer(array $revokedCertificates, string $instanceId, int $generation, int $crlNumber): string {
$configPath = $this->getConfigPathByParams($instanceId, $generation);
$caCertPath = $configPath . DIRECTORY_SEPARATOR . 'ca.pem';
$caKeyPath = $configPath . DIRECTORY_SEPARATOR . 'ca-key.pem';
$crlDerPath = $configPath . DIRECTORY_SEPARATOR . 'crl.der';
if (!file_exists($caCertPath) || !file_exists($caKeyPath)) {
throw new \RuntimeException('CA certificate or private key not found. Run: occ libresign:configure:openssl');
}
try {
$caCert = file_get_contents($caCertPath);
$caKey = file_get_contents($caKeyPath);
if (!$caCert || !$caKey) {
throw new \RuntimeException('Failed to read CA certificate or private key');
}
$issuer = new \OCA\Libresign\Vendor\phpseclib3\File\X509();
$issuer->loadX509($caCert);
$caPrivateKey = \OCA\Libresign\Vendor\phpseclib3\Crypt\PublicKeyLoader::load($caKey);
if (!$caPrivateKey instanceof \OCA\Libresign\Vendor\phpseclib3\Crypt\Common\PrivateKey) {
throw new \RuntimeException('Loaded key is not a private key');
}
$issuer->setPrivateKey($caPrivateKey);
$utc = new \DateTimeZone('UTC');
$now = (new \DateTime())->setTimezone($utc);
$nextWeek = (new \DateTime('+7 days'))->setTimezone($utc);
$revokedList = [];
foreach ($revokedCertificates as $cert) {
$revokedList[] = [
'userCertificate' => new \OCA\Libresign\Vendor\phpseclib3\Math\BigInteger($cert->getSerialNumber(), 16),
'revocationDate' => ['utcTime' => $cert->getRevokedAt()->format('D, d M Y H:i:s O')],
];
}
$crlStructure = [
'tbsCertList' => [
'version' => 'v2',
'signature' => ['algorithm' => 'sha256WithRSAEncryption'],
'issuer' => $issuer->getSubjectDN(\OCA\Libresign\Vendor\phpseclib3\File\X509::DN_ARRAY),
'thisUpdate' => ['utcTime' => $now],
'nextUpdate' => ['utcTime' => $nextWeek],
'revokedCertificates' => $revokedList,
],
'signatureAlgorithm' => ['algorithm' => 'sha256WithRSAEncryption'],
];
$crl = new \OCA\Libresign\Vendor\phpseclib3\File\X509();
$crl->loadCRL($crlStructure);
$crl->setSerialNumber($crlNumber);
$crl->setStartDate(new \DateTime('-1 minute'));
$crl->setEndDate(new \DateTime('+7 days'));
$signedCrl = $crl->signCRL($issuer, $crl, 'sha256WithRSAEncryption');
if ($signedCrl === false) {
throw new \RuntimeException('Failed to sign CRL with phpseclib3');
}
if (!isset($signedCrl['signatureAlgorithm'])) {
$signedCrl['signatureAlgorithm'] = ['algorithm' => 'sha256WithRSAEncryption'];
}
$crlDerData = $crl->saveCRL($signedCrl, \OCA\Libresign\Vendor\phpseclib3\File\X509::FORMAT_DER);
if ($crlDerData === false) {
throw new \RuntimeException('Failed to save CRL in DER format');
}
if (file_put_contents($crlDerPath, $crlDerData) === false) {
throw new \RuntimeException('Failed to write CRL DER file');
}
return $crlDerData;
} catch (\Exception $e) {
$this->logger->error('CRL generation failed: ' . $e->getMessage(), ['exception' => $e]);
throw new \RuntimeException('Failed to generate CRL: ' . $e->getMessage(), 0, $e);
}
}
}

View file

@ -63,8 +63,8 @@ final class OpenSslHandlerTest extends \OCA\Libresign\Tests\Unit\TestCase {
$this->urlGenerator,
$this->serialNumberService,
$this->caIdentifierService,
$this->crlMapper,
$this->logger,
$this->crlMapper,
);
}