Skip to content

Commit

Permalink
feature #9 Add parent to CA to for chain storage + make final (sstok)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.0-dev branch.

Discussion
----------

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  |yes
| BC breaks?    | yes
| Deprecations? | no
| Fixed tickets | 
| License       | MIT

Make CAResolverImp and CA model final, this shouldn't be extended but a custom implementation should be used instead.

Storing the CA directly isn't possible, and so it should be transformed to an application level entity instead.
Something like this.

```php
use Doctrine\Persistence\ObjectManager;
use App\TLS\CA;
use Rollerworks\Component\X509Validator\CA as CAInfo;
use Rollerworks\Component\X509Validator\CAResolverImpl;
use Rollerworks\Component\X509Validator\X509DataExtractor;

/**
 * @Final
 */
class CAResolver
{
    private X509DataExtractor $extractor;
    private CAResolverImpl $caResolver;

    /**
     * @param ObjectManager<CA> $objectManager
     */
    public function __construct(private ObjectManager $objectManager)
    {
        $this->extractor = new X509DataExtractor();
        $this->caResolver = new CAResolverImpl();
    }

    /**
     * @param array<string, string> $caList
     */
    public function resolve(string $certificate, array $caList): ?CA
    {
        $ca = $this->caResolver->resolve($certificate, $caList);

        if ($ca === null) {
            return null;
        }

        return $this->objectManager->find(CA::class, CA::getHash($ca->contents)) ?? $this->resolveCA($ca);
    }

    private function resolveCA(?CAInfo $ca): CA
    {
        /** @var array<int, string> $tree */
        $tree = [];

        while ($ca !== null) {
            $tree[] = $ca->contents;
            $ca = $ca->parent;
        }

        $parent = null;

        foreach (array_reverse($tree) as $contents) {
            $caEntity = $this->objectManager->find(CA::class, CA::getHash($contents));

            if ($caEntity === null) {
                $x509Info = $this->extractor->extractRawData($contents, '', true);
                $caEntity = new CA($contents, $x509Info->allFields, $parent);
                $this->objectManager->persist($caEntity);
            }

            $parent = $caEntity;
        }

        return $caEntity;
    }
}
```

Commits
-------

662dc7c Add parent to CA to for chain storage + make final
  • Loading branch information
sstok authored Feb 4, 2024
2 parents 1db0429 + 662dc7c commit 23a1484
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 15 deletions.
12 changes: 5 additions & 7 deletions src/CA.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@

namespace Rollerworks\Component\X509Validator;

class CA
final readonly class CA
{
public function __construct(private readonly string $contents) {}

public function getContents(): string
{
return $this->contents;
}
public function __construct(
public string $contents,
public ?self $parent = null
) {}
}
10 changes: 6 additions & 4 deletions src/CAResolverImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use Rollerworks\Component\X509Validator\Violation\TooManyCAsProvided;
use Rollerworks\Component\X509Validator\Violation\UnableToResolveParent;

class CAResolverImpl implements CAResolver
final class CAResolverImpl implements CAResolver
{
private readonly X509DataExtractor $extractor;

Expand Down Expand Up @@ -64,6 +64,8 @@ private function isSignatureValid(string $contents, string $pupKey): bool
/** @param array<string, string> $caList */
private function resolveCA(string $certificate, array $caList): CA
{
$parent = null;

foreach ($caList as $index => $contents) {
$data = $this->extractor->extractRawData($contents, $index, true);
$this->validateCA($data);
Expand All @@ -72,16 +74,16 @@ private function resolveCA(string $certificate, array $caList): CA
continue;
}

// Check if self signed, otherwise resolve it's parent
// Check if the CA is self signed, otherwise resolve it's parent
if (! $this->isSignatureValid($contents, $data->pubKey)) {
// THIS issuer cannot be the parent of another parent, so remove it
// from the list. This speeds-up the resolving process.
unset($caList[$index]);

$this->resolveCA($contents, $caList);
$parent = $this->resolveCA($contents, $caList);
}

return new CA($contents);
return new CA($contents, $parent);
}

$x509Info = $this->extractor->extractRawData($certificate);
Expand Down
2 changes: 1 addition & 1 deletion src/OCSPValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function validateStatus(string $certificate, array $caList = []): void
}

$certificateSeq = $this->certificateLoader->fromString($certificate);
$issuerCertificate = $this->certificateLoader->fromString($ca->getContents());
$issuerCertificate = $this->certificateLoader->fromString($ca->contents);

$ocspResponderUrl = $this->certificateInfo->extractOcspResponderUrl($certificateSeq);

Expand Down
7 changes: 5 additions & 2 deletions src/Violation/UnableToResolveParent.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@

final class UnableToResolveParent extends Violation
{
public function __construct(private readonly string $name, private readonly string $issuer, int $code = 1)
{
public function __construct(
public readonly string $name,
public readonly string $issuer,
int $code = 1
) {
parent::__construct(sprintf('Unable to resolve the parent CA of certificate "%s", issued by "%s".', $name, $issuer), $code);
}

Expand Down
3 changes: 2 additions & 1 deletion tests/CAResolverImplTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Rollerworks\Component\X509Validator\Violation\TooManyCAsProvided;
use Rollerworks\Component\X509Validator\Violation\UnableToResolveParent;
use Rollerworks\Component\X509Validator\Violation\UnprocessablePEM;
use Rollerworks\Component\X509Validator\X509Info;

/**
* @internal
Expand Down Expand Up @@ -501,7 +502,7 @@ public function it_resolves_a_valid_ca_list(): void
'DigiCert SHA2 Secure Server CA' => $ca1,
]);

$intermediateCA = new CA($ca1);
$intermediateCA = new CA($ca1, new CA($ca2));

self::assertEquals($intermediateCA, $ca);
}
Expand Down

0 comments on commit 23a1484

Please sign in to comment.