private function parseCertificate()
{
$now = new \DateTime();
// Parse the certificate
$certificateData = openssl_x509_parse(file_get_contents($this->getPemFile()));
if (false == $certificateData) {
throw new InvalidCertificateException('Unable to parse certificate "' . $this->getPemFile() . '", are you sure this is a valid PEM certificate?');
}
// Validate the "valid from" timestamp
if (isset($certificateData['validFrom_time_t'])) {
$validFrom = new \DateTime('@' . $certificateData['validFrom_time_t']);
if ($validFrom > $now) {
throw new InvalidCertificateException('Certificate "' . $this->getPemFile() . '" not yet valid, valid from ' . $validFrom->format(\DateTime::ISO8601) . '.');
}
$this->validFrom = $validFrom;
} else {
throw new InvalidCertificateException('Certificate "' . $this->getPemFile() . '" has no valid from timestamp.');
}
// Validate the "valid to" timestamp
if (isset($certificateData['validTo_time_t'])) {
$validTo = new \DateTime('@' . $certificateData['validTo_time_t']);
if ($validTo < $now) {
throw new InvalidCertificateException('Certificate "' . $this->getPemFile() . '" expired, was valid until ' . $validTo->format(\DateTime::ISO8601) . '.');
}
$this->validTo = $validTo;
} else {
throw new InvalidCertificateException('Certificate "' . $this->getPemFile() . '" has no valid to timestamp.');
}
// Check if the certificate was issued by Apple
if (!isset($certificateData['issuer']) || !isset($certificateData['issuer']['O']) || 'Apple Inc.' != $certificateData['issuer']['O']) {
throw new InvalidCertificateException('Certificate "' . $this->getPemFile() . '" does not list Apple Inc. as the issuer.');
}
// Check if the there is an environment hidden in the certificate
if (isset($certificateData['subject']) && isset($certificateData['subject']['CN'])) {
$this->description = $certificateData['subject']['CN'];
if (null === $this->endpointEnv) {
if (strpos($certificateData['subject']['CN'], 'Pass Type ID') === 0 || strpos($certificateData['subject']['CN'], 'Apple Push Services') === 0 || strpos($certificateData['subject']['CN'], 'Apple Production IOS Push Services') === 0 || strpos($certificateData['subject']['CN'], 'Apple Production Mac Push Services') === 0) {
// Passbook Pass certificate & APNS Production/hybrid certs, should be on production
$this->endpointEnv = self::ENDPOINT_ENV_PRODUCTION;
} else {
if (strpos($certificateData['subject']['CN'], 'Apple Development IOS Push Services') === 0 || strpos($certificateData['subject']['CN'], 'Apple Development Mac Push Services') === 0) {
// APNS Development, should always be on sandbox
$this->endpointEnv = self::ENDPOINT_ENV_SANDBOX;
} else {
throw new InvalidCertificateException('Could not detect APNS environment based on the CN string "' . $certificateData['subject']['CN'] . '" in certificate "' . $this->getPemFile() . '".');
}
}
}
} else {
throw new InvalidCertificateException('No APNS environment information found in certificate "' . $this->getPemFile() . '".');
}
// Validate the private key by loading it
$privateKey = openssl_pkey_get_private('file://' . $this->getPemFile(), $this->getPassphrase());
if (false === $privateKey) {
throw new InvalidCertificateException('Could not extract the private key from certificate "' . $this->getPemFile() . '", please check if the given passphrase is correct and if it contains a private key.');
}
// If a passphrase is given, the private key may not be loaded without it
if ($this->getPassphrase() != null) {
// Try to load the private key without the passphrase (should fail)
$privateKey = openssl_pkey_get_private('file://' . $this->getPemFile());
if (false !== $privateKey) {
throw new InvalidCertificateException('Passphrase given, but the private key in "' . $this->getPemFile() . '" is not encrypted, please make sure you are using the correct certificate/passphrase combination.');
}
}
}