src/Factory/CertificateFactory.php
<?php
/**
* This file is part of MayMeow/encrypt project
* Copyright (c) 2017 Charlotta Jung
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
* @copyright Copyright (c) Charlotta MayMeow Jung
* @link http://maymeow.click
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*
* @project may-encrypt
* @file CertificateFactory.php
*/
namespace MayMeow\Factory;
use MayMeow\Cryptography\Authority\CertificateAuthorityConfigurationInterface;
use MayMeow\Cryptography\Authority\CertificateAuthorityInterface;
use MayMeow\Cryptography\Authority\CertificateConfigArgs;
use MayMeow\Cryptography\Cert\X509Certificate2;
use MayMeow\Cryptography\Cert\X509CertificateWriterInterface;
use MayMeow\Interfaces\WriterInterface;
use MayMeow\Model\AltNames;
use MayMeow\Model\DomainName;
use MayMeow\Model\KeyPairInterface;
use MayMeow\Model\SignedCertificate;
use MayMeow\Cryptography\RSA\RSACryptoServiceProvider;
use Symfony\Component\Yaml\Yaml;
use MayMeow\Model\KeyPair;
use MayMeow\Model\EncryptConfiguration;
/**
* Class CertificateFactory
* @package MayMeow\Factory
*/
class CertificateFactory implements CertificateFactoryInterface, CertificateAuthorityInterface
{
/**
* @deprecated
*/
const TYPE_INTERMEDIATE = "intermediate";
/**
* @deprecated
*/
const TYPE_CODE_SIGN = "code_sign";
/**
* @deprecated
*/
const TYPE_CERTIFICATION_AUTHORITY = "ca";
/**
* @deprecated
*/
const TYPE_SERVER = "server";
/**
* @deprecated
*/
const TYPE_USER = "user";
/** @var KeyPairInterface $ca */
protected $ca;
/**
* Private key file name
*/
const PRIV_KEY_FILENAME = 'key.pem';
/**
* Public key file name
*/
const PUB_KEY_FILENAME = 'cert.crt';
/**
* @var DomainName
*/
protected $domainName;
/**
* Alternative names for certificate
* DNS, IP, URL
*
* @var AltNames $altNames
*/
protected $altNames;
/**
* Loaded config from encrypt.yml
*
* @var
*/
protected $config;
/**
* Configuration for certificate based on certificate type
*
* @var array
*/
protected $certConfigure = [];
/**
* Certification Authority name
*
* @var string caName
*/
protected $caName;
/**
* Password for CA's private key
*
* @var string caPassword
*/
protected $caPassword;
/**
* Name for certificate files
*
* @var
*/
protected $fileName;
/**
* Type of certificate
* User, server, ca or intermediate
*
* @var
*/
protected $type;
/**
* Certificate model
* Here will be stored all required variables, keys, csr and certificate
*
* @var X509Certificate2
*/
protected X509Certificate2 $crt;
/**
* Default template with certificate configurations
*
* @var array typeConfigurations
*/
protected $typeConfigurations;
/**
* Path where stored all certificates
* @var string $caDataRoot
*/
protected $caDataRoot;
/**
* Path to template store
* @var string $templateRootPath
*/
protected $templateRootPath;
/**
* Configuration for certificates
* @var EncryptConfiguration $_configure
*/
private $_config;
public function __construct(EncryptConfiguration $encryptConfiguration, $templateRootPath = null)
{
$this->templateRootPath = $templateRootPath;
$this->caDataRoot = WWW_ROOT;
if ($this->_config == null)
$this->_config = $encryptConfiguration;
$this->config = $this->_config->parse();
//$this->_setDataPath(WWW_ROOT);
//$this->_setTemplatesPath($templateRootPath);
}
/**
* Set path to CA root data folder
* @deprecated
* @param $rootPath
*/
protected function _setDataPath($rootPath)
{
$this->caDataRoot = $rootPath;
}
/**
* Set paths for all templates
* @deprecated
* @param $templateRootPath
*/
protected function _setTemplatesPath($templateRootPath)
{
$this->typeConfigurations = [
'ca' => $templateRootPath . 'ca_certificate.cnf',
'user' => $templateRootPath . 'intermediate_certificate.cnf',
'server' => $templateRootPath . 'intermediate_certificate.cnf',
'code_sign' => $templateRootPath . 'intermediate_certificate.cnf',
'intermediate' => $templateRootPath . 'intermediate_certificate.cnf'
];
}
/**
* Returns key pair
*/
public function getKeyPair($file = false, $passphrase = null)
{
$rsa = new RSACryptoServiceProvider(4096);
$keypair = $rsa->generateKeyPair($passphrase, $this->certConfigure);
$keys = KeyPair::initialize();
$keys->setPrivateKey($keypair->getPrivateKey())->setPublicKey($keypair->getPublicKey());
if ($file) {
file_put_contents($this->fileName . static::PRIV_KEY_FILENAME, $keys->getPrivateKey());
file_put_contents($this->fileName . static::PUB_KEY_FILENAME, $keys->getPublicKey());
}
return $keys;
}
/**
* @deprecated
* @param $path
* @return $this
*/
public function setDataPath($path)
{
$this->_setDataPath($path);
return $this;
}
/**
* @deprecated
* @param $path
* @return $this
*/
public function setConfigPath($path)
{
$this->_setConfig($path);
return $this;
}
/**
* @reprecated
* @param $path
* @return $this
*/
public function setTemplatesPath($path)
{
$this->_setTemplatesPath($path);
return $this;
}
/**
* Sets type of certificate
*
* @param $type
* @param null $options
* @return $this
*/
public function setType($type, $options = null)
{
$this->type = $type;
$this->certConfigure = CertificateConfigArgs::getInstance($this)->getArgs($this->type);
return $this;
}
/**
* Return certificates setting
* @deprecated
* @param null $key
* @return mixed
*/
protected function _getConfig($key = null)
{
return $this->config['certificates'][$this->type][$key];
}
/**
* Function getConfig
* returns loaded configuration
*
* @return mixed
*/
public function getConfig()
{
return $this->config;
}
/**
* @deprecated
* Load Default configuration
*/
protected function _setConfig($path = null)
{
$this->config = Yaml::parse($path);
}
/**
* Function SetCa
* Functions to set CA name to use for signing certificate
*
* @deprecated look setCaFrom
*
* @param null $name
* @param null $password
* @return $this
*/
public function setCa($name = null, $password = null)
{
$this->caName = $name;
$this->caPassword = $password;
return $this;
}
/**
* @param KeyPairInterface $kp
* @return $this
*/
public function setCaFrom(KeyPairInterface $kp)
{
$this->ca = $kp;
return $this;
}
/**
* Method getAltNames
* returns AltNames
*
* @return AltNames
*/
public function getAltNames()
{
if (!$this->altNames) {
$this->altNames = new AltNames();
}
return $this->altNames;
}
/**
* Function SetName
* Set name of certificate file
*
* @param null $name
* @return $this
*/
public function setName($name = null)
{
$this->fileName = $this->caDataRoot . $name . DS;
// Create folder for future certificate
if (!file_exists($this->fileName)) {
mkdir($this->fileName, 0777, true);
}
return $this;
}
/**
* Sign certificate file and export tem to disk
*/
public function sign($digest_alg = null)
{
// Write Alternative configurations before you create CSR
$this->_GenerateAltConfiguration();
if (null !== $digest_alg) {
$this->certConfigure['digest_alg'] = $digest_alg;
}
// Create CSR
$this->crt = new X509Certificate2($this->domainName, $this->certConfigure);
// Sign CSR
if (!$this->caName == null) {
// If CA name is not null sign certificate with loaded CA certificate
$this->crt->sign($this->ca->getPublicKey(), $this->ca->getPrivateKey(), $this->_getConfig('daysvalid'), $this->certConfigure);
} else {
/**
* Else self sign certificate
* Its important for ROOT Certification authority certificate
*/
$this->crt->selfSigned($this->_getConfig('daysvalid'), $this->certConfigure);
}
return $this;
}
/**
* Method _altConfiguration
*
* Create alternative configuration based on altNames
* CNF file will have name based on certificate name - certificate-name.crt -> certificate-name.cnf
*/
protected function _GenerateAltConfiguration()
{
$cnfFile = file_get_contents($this->certConfigure['config']);
if ($this->altNames) {
$cnfFile .= $this->altNames->toString();
$altFileName = $this->fileName . DS . 'config.cnf';
$this->certConfigure['config'] = $altFileName;
file_put_contents($altFileName, $cnfFile);
}
}
/**
* Function DomainName
* Returns Domain name
*
* @return DomainName
*/
public function domainName()
{
if (!$this->domainName) {
$this->domainName = new DomainName();
}
return $this->domainName;
}
/**
* Load CA Certificate from file
* @deprecated
*
* @return string
*/
protected function _getCaCert()
{
return file_get_contents($this->caDataRoot . $this->caName . DS . 'cert.crt');
}
/**
* Load Ca Private key from file
*
* @deprecated
*
* @return array
*/
protected function _getCaKey()
{
return self::getPrivateKey($this->caName, $this->caPassword);
}
/**
* Method getCaKey
* Returns array for encrypted keys and string othervise
*
* @deprecated
*
* @param $caName
* @param $caPassword
* @return array|string
*/
public function getPrivateKey($caName = null, $caPassword = null)
{
// if password is set
if ($caPassword !== null) {
return [
file_get_contents($this->caDataRoot . $caName . DS . static::PRIV_KEY_FILENAME),
$caPassword
];
}
return file_get_contents($this->caDataRoot . $caName . DS . static::PRIV_KEY_FILENAME);
}
/**
* Create request for server signing
* For Client App
*
* @return string
*/
public function createRequest()
{
$this->crt = new X509Certificate2();
$privKey = $this->crt->getPrivateKey();
$this->crt->setCsr(openssl_csr_new($this->domainName()->get(), $privKey, $this->certConfigure));
$request = json_encode([
'csr' => $this->crt->getCsr()
]);
// Send CSR to server and wait for signing
$response = $this->signWithServer($request);
$response = json_decode($response);
$this->crt->setSignedCert(openssl_x509_read($response->certificate));
return $this;
}
/**
* Sign certificate from client request
* For Server app
* @param $request
* @return false|string
*/
public function signWithServer($request)
{
//server
$clientRequest = json_decode($request);
$this->crt->setSignedCert(openssl_csr_sign($clientRequest->csr, $this->_getCaCert(), $this->_getCaKey(), $this->_getConfig('daysvalid'), $this->certConfigure, time()));
// return signed file to user
openssl_x509_export($this->crt->getSignedCert(), $clientCertificate);
return json_encode(['certificate' => $clientCertificate]);
}
/**
* @param $writerInterfaceName
* @return WriterInterface
* @throws \Exception
*/
public function writeTo($certificateWriter)
{
//$certificateWriter->write($this->crt, $this->fileName);
$wi = new $certificateWriter();
if ($wi instanceof WriterInterface) {
$wi->setCert($this->crt);
$wi->setName($this->fileName);
$wi->setCertConfiguration($this->certConfigure);
return $wi->write();
}
throw new \Exception('Wrong inferface');
}
/**
* @deprecated
* @see using()
* @param bool $decryptPK
* @param bool $pcks12
*/
public function toFile($decryptPK = false, $pcks12 = false)
{
file_put_contents($this->fileName . 'code.txt', $this->crt->getEncryptionPass());
file_put_contents($this->fileName . 'req.pem', $this->crt->getCsr());
openssl_x509_export_to_file($this->crt->getSignedCert(), $this->fileName . static::PUB_KEY_FILENAME);
openssl_pkey_export_to_file($this->crt->getPrivateKey(), $this->fileName . static::PRIV_KEY_FILENAME, $this->crt->getEncryptionPass(), $this->certConfigure);
if ($decryptPK) {
openssl_pkey_export_to_file($this->crt->getPrivateKey(), $this->fileName . 'unenc.key.pem', null, $this->certConfigure);
}
if ($pcks12) {
openssl_pkcs12_export_to_file($this->crt->getSignedCert(), $this->fileName . 'cert.pfx', $this->crt->getPrivateKey(), $this->crt->getEncryptionPass(), $this->certConfigure);
}
}
/**
* Method getPublicKey
* @deprecated
* @param null $caName
* @return bool|string
*/
public function getPublicKey($caName = null)
{
return file_get_contents($this->caDataRoot . $caName . DS . static::PUB_KEY_FILENAME);
}
/**
* @inheritDoc
*/
public function getDefaultConfiguration(): CertificateAuthorityConfigurationInterface
{
return $this->_config->defaultConfiguration();
}
}