Skip to content

Commit

Permalink
Merge pull request #11 from mikemunger/master
Browse files Browse the repository at this point in the history
Add support to get certificate chain as separate certificates
  • Loading branch information
bakkerpeter authored Apr 28, 2020
2 parents fb33b4b + 32b9d43 commit a6ea5ab
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 31 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ file_put_contents('certificate.cert', $certificate->getCertificate());
file_put_contents('private.key', $certificate->getPrivateKey());
```

>To get a seperate intermediate certificate and domain certificate:
>```php
>$domainCertificate = $certificate->getCertificate(false);
>$intermediateCertificate = $certificate->getIntermediate();
>```
### Who is using it?
Are you using this package, would love to know. Please send a PR to enlist your project or company.
Expand Down
38 changes: 19 additions & 19 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public function createOrder(array $domains): Order
foreach ($domains as $domain) {
$identifiers[] =
[
'type' => 'dns',
'type' => 'dns',
'value' => $domain,
];
}
Expand Down Expand Up @@ -331,8 +331,8 @@ public function getCertificate(Order $order): Certificate
$data['certificate'],
$this->signPayloadKid(null, $data['certificate'])
);
$certificate = $str = preg_replace('/^[ \t]*[\r\n]+/m', '', (string)$certificateResponse->getBody());
return new Certificate($privateKey, $csr, $certificate);
$chain = $str = preg_replace('/^[ \t]*[\r\n]+/m', '', (string)$certificateResponse->getBody());
return new Certificate($privateKey, $csr, $chain);
}


Expand Down Expand Up @@ -383,8 +383,8 @@ protected function getHttpClient()
protected function getSelfTestClient()
{
return new HttpClient([
'verify' => false,
'timeout' => 10,
'verify' => false,
'timeout' => 10,
'connect_timeout' => 3,
'allow_redirects' => true,
]);
Expand Down Expand Up @@ -459,9 +459,9 @@ protected function selfDNSTest(Authorization $authorization, $maxAttempts)
protected function getSelfTestDNSClient()
{
return new HttpClient([
'base_uri' => 'https://cloudflare-dns.com',
'base_uri' => 'https://cloudflare-dns.com',
'connect_timeout' => 10,
'headers' => [
'headers' => [
'Accept' => 'application/dns-json',
],
]);
Expand Down Expand Up @@ -511,7 +511,7 @@ protected function tosAgree()
$this->getUrl(self::DIRECTORY_NEW_ACCOUNT),
$this->signPayloadJWK(
[
'contact' => [
'contact' => [
'mailto:' . $this->getOption('username'),
],
'termsOfServiceAgreed' => true,
Expand Down Expand Up @@ -590,7 +590,7 @@ protected function request($url, $payload = [], $method = 'POST'): ResponseInter
{
try {
$response = $this->getHttpClient()->request($method, $url, [
'json' => $payload,
'json' => $payload,
'headers' => [
'Content-Type' => 'application/jose+json',
]
Expand Down Expand Up @@ -650,9 +650,9 @@ protected function getAccountKey()
protected function getJWKHeader(): array
{
return [
'e' => Helper::toSafeString(Helper::getKeyDetails($this->getAccountKey())['rsa']['e']),
'e' => Helper::toSafeString(Helper::getKeyDetails($this->getAccountKey())['rsa']['e']),
'kty' => 'RSA',
'n' => Helper::toSafeString(Helper::getKeyDetails($this->getAccountKey())['rsa']['n']),
'n' => Helper::toSafeString(Helper::getKeyDetails($this->getAccountKey())['rsa']['n']),
];
}

Expand All @@ -671,10 +671,10 @@ protected function getJWK($url): array
$this->nonce = $response->getHeaderLine('replay-nonce');
}
return [
'alg' => 'RS256',
'jwk' => $this->getJWKHeader(),
'alg' => 'RS256',
'jwk' => $this->getJWKHeader(),
'nonce' => $this->nonce,
'url' => $url
'url' => $url
];
}

Expand All @@ -691,10 +691,10 @@ protected function getKID($url): array
$nonce = $response->getHeaderLine('replay-nonce');

return [
"alg" => "RS256",
"kid" => $this->account->getAccountURL(),
"alg" => "RS256",
"kid" => $this->account->getAccountURL(),
"nonce" => $nonce,
"url" => $url
"url" => $url
];
}

Expand All @@ -720,7 +720,7 @@ protected function signPayloadJWK($payload, $url): array

return [
'protected' => $protected,
'payload' => $payload,
'payload' => $payload,
'signature' => Helper::toSafeString($signature),
];
}
Expand All @@ -746,7 +746,7 @@ protected function signPayloadKid($payload, $url): array

return [
'protected' => $protected,
'payload' => $payload,
'payload' => $payload,
'signature' => Helper::toSafeString($signature),
];
}
Expand Down
36 changes: 29 additions & 7 deletions src/Data/Certificate.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ class Certificate
*/
protected $privateKey;

/**
* @var string
*/
protected $chain;

/**
* @var string
*/
protected $certificate;

/**
* @var string
*/
protected $intermediateCertificate;

/**
* @var string
*/
Expand All @@ -31,15 +41,16 @@ class Certificate
* Certificate constructor.
* @param $privateKey
* @param $csr
* @param $certificate
* @param $chain
* @throws \Exception
*/
public function __construct($privateKey, $csr, $certificate)
public function __construct($privateKey, $csr, $chain)
{
$this->privateKey = $privateKey;
$this->csr = $csr;
$this->certificate = $certificate;
$this->expiryDate = Helper::getCertExpiryDate($certificate);
$this->chain = $chain;
list($this->certificate, $this->intermediateCertificate) = Helper::splitCertificate($chain);
$this->expiryDate = Helper::getCertExpiryDate($chain);
}

/**
Expand All @@ -61,12 +72,23 @@ public function getExpiryDate(): \DateTime
}

/**
* Return the certificate as a multi line string
* Return the certificate as a multi line string, by default it includes the intermediate certificate as well
*
* @param bool $asChain
* @return string
*/
public function getCertificate($asChain = true): string
{
return $asChain ? $this->chain : $this->certificate;
}

/**
* Return the intermediate certificate as a multi line string
* @return string
*/
public function getCertificate(): string
public function getIntermediate(): string
{
return $this->certificate;
return $this->intermediateCertificate;
}

/**
Expand Down
33 changes: 28 additions & 5 deletions src/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace Afosto\Acme;

use Afosto\Acme\Data\Authorization;
use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Exception\ClientException;

/**
Expand Down Expand Up @@ -92,11 +90,11 @@ public static function getCsr(array $domains, $key): string
file_put_contents($fn, implode("\n", $config));
$csr = openssl_csr_new([
'countryName' => 'NL',
'commonName' => $primaryDomain,
'commonName' => $primaryDomain,
], $key, [
'config' => $fn,
'config' => $fn,
'req_extensions' => 'SAN',
'digest_alg' => 'sha512',
'digest_alg' => 'sha512',
]);
unlink($fn);

Expand Down Expand Up @@ -140,4 +138,29 @@ public static function getKeyDetails($key): array

return $accountDetails;
}

/**
* Split a two certificate bundle into separate multi line string certificates
* @param string $chain
* @return array
* @throws \Exception
*/
public static function splitCertificate(string $chain): array
{
preg_match(
'/^(?<domain>-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----)\n'
. '(?<intermediate>-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----)$/s',
$chain,
$certificates
);

$domain = $certificates['domain'] ?? null;
$intermediate = $certificates['intermediate'] ?? null;

if (!$domain || !$intermediate) {
throw new \Exception('Could not parse certificate string');
}

return [$domain, $intermediate];
}
}

0 comments on commit a6ea5ab

Please sign in to comment.