array(), ); protected $_has_one = array( 'ca'=>array('model'=>'SSL_CA','far_key'=>'ssl_ca_id','foreign_key'=>'id'), ); protected $_display_filters = array( 'csr'=>array( array('Model_SSL::subject_csr',array(':value')), ), 'cert'=>array( array('Model_SSL::subject_cert',array(':value')), ), ); protected $_save_message = TRUE; public function rules() { return Arr::merge(parent::rules(),array( 'csr'=>array( array(array($this,'isCSR')), ), )); } /** * Parse our AuthorityKeyIndentifier Extension to extract information * @param $key Return just that index */ protected function _aki($key=NULL) { $result = array(); $aki = $this->_extensions('authorityKeyIdentifier'); if (! $aki) return ''; foreach (explode("\n",preg_replace("/\n$/",'',$aki)) as $x) { if (! $x) continue; if (strstr($x,':')) { list($a,$b) = explode(':',$x,2); $result[strtolower($a)] = $b; } } return is_null($key) ? $result : Arr::get($result,$key); } /** * This function will convert a large decimal number into hex * @param $number Large decimal number */ protected static function _dec_to_hex($number) { $hex = array(); if ($number == 0) return '00'; while ($number > 0) { if ($number == 0) { array_push($hex, '0'); } else { $x = (int) ($number/16); array_push($hex,strtoupper(dechex((int)($number-($x*16))))); $number = $x; } } return preg_replace('/^:/','',preg_replace('/(..)/',":$1",implode(array_reverse($hex)))); } /** * Parse our Sign Certifcate to extract information * @param $key Return just that index */ protected function _details($key=NULL) { if (! $this->cert) return array(); return is_null($key) ? $this->_cert_details : Arr::get($this->_cert_details,$key,array()); } protected static function _dn(array $array) { $result = ''; $i = 0; foreach ($array as $k=>$v) { if ($i++) $result .= ','; $result .= sprintf('%s=%s',$k,$v); } return $result; } /** * Parse our Sign Certifcate Extensions to extract information * @param $key Return just that index */ protected function _extensions($key=NULL) { $result = Arr::get($this->_cert_details,'extensions'); return is_null($key) ? $result : Arr::get($result,$key); } // We want to inject the SSL object into this Model protected function _load_values(array $values) { parent::_load_values($values); $this->_cert_details = openssl_x509_parse($this->cert); return $this; } public function aki_dirname() { return $this->_aki('dirname'); } public function aki_keyid() { return $this->_aki('keyid'); } public function aki_serial() { return $this->_aki('serial'); } public function algorithm() { if (! $this->cert) return NULL; $e = ''; openssl_x509_export(openssl_x509_read($this->cert),$e,FALSE); // @todo There must be a nice way to get this? return (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match)) ? $match[1] : _('Unknown'); } public function download_button() { if ($this->valid_to() < time()) return ''; $passwd_len = Kohana::$config->load('ssl')->minpass_length; $output = Form::open(URL::link('user','ssl/download/'.$this->id),array('class'=>'form-inline','data-toggle'=>'validator','role'=>'form')); if ($passwd_len) { $output .= '
'; $output .= '
'; $output .= Form::password('passwd','',array('class'=>'form-control','placeholder'=>_('Choose a password'),'nocg'=>TRUE,'pattern'=>'.{'.$passwd_len.',}','data-error'=>"Minimum ${passwd_len} characters",'required')); $output .= ''; } $output .= Form::button('download','Download',array('class'=>'btn btn-default','nocg'=>TRUE)); if ($passwd_len) { $output .= ''; $output .= '
'; $output .= '
'; $output .= '
'; } $output .= Form::close(); return $output; } public function dn() { return $this->display($this->signed() ? 'cert' : 'csr'); } public function hash() { return Arr::get($this->_cert_details,'hash'); } public function isCSR() { return openssl_csr_get_subject($this->csr); } public function issuer() { return self::_dn(Arr::get($this->_cert_details,'issuer',array())); } public function serial() { return $this->_dec_to_hex(Arr::get($this->_cert_details,'serialNumber')); } /** * (Re)Sign an SSL Certificate */ public function sign($force=FALSE) { $ssl_config = Kohana::$config->load('ssl'); $ssl_conf = Kohana::find_file('config',$ssl_config->config,''); $ssl_conf = array_pop($ssl_conf); // If our certificate is not old enough skip if ($this->valid_to() > time()+$ssl_config->min_renew_days*86400 AND ! $force) return FALSE; $today = mktime(0,0,0,date('n'),date('j'),date('Y')); $days = (int)$this->account->renew; if (! $this->ca->pk) throw HTTP_Exception::factory(400,'Unable to sign, missing Private Key for CA :ca',array(':ca'=>$this->ca->subject_cn())); $res = openssl_csr_sign($this->csr,$this->ca->cert,$this->ca->pk,$days,array( 'config'=>$ssl_conf, 'x509_extensions'=>'client', 'digest_alg'=>'sha1', ),time()); if ($res AND openssl_x509_export($res,$cert)) { $this->cert = $cert; $this->save(); return TRUE; } else { /* echo Debug::vars(array( 'csr'=>$this->csr, 'ca'=>$this->ca->cert, 'caid'=>$this->ca->id, 'days'=>$days, 'ssl'=>$ssl_conf, 'x509e'=>'client', // 'command'=>sprintf('openssl ca -days %s -cert /tmp/ssl_ca.crt -keyfile /tmp/ssl_ca.key -out /tmp/ssl.crt -in /tmp/ssl.csr -extensions %s -config %s',$days,'client',$ssl_conf), )); file_put_contents('/tmp/ssl.csr',$this->csr); file_put_contents('/tmp/ssl_ca.key',$this->ca->pk); file_put_contents('/tmp/ssl_ca.crt',$this->ca->cert); */ throw HTTP_Exception::factory(501,'Error Creating SSL Certificate :error',array(':error'=>openssl_error_string())); } } protected function signed() { return $this->_cert_details ? TRUE : FALSE; } public function ski() { return $this->_extensions('subjectKeyIdentifier'); } public function issuer_cn() { return Arr::get($this->cert ? Arr::get($this->_cert_details,'issuer') : array(),'CN'); } public function subject_cn() { return Arr::get($this->cert ? Arr::get($this->_cert_details,'subject') : openssl_csr_get_subject($this->csr),'CN'); } public static function subject_cert($cert) { try { return self::_dn(Arr::get(openssl_x509_parse($cert),'subject')); } catch (exception $e) { return 'Invalid Cert'; } } public static function subject_csr($csr) { try { return self::_dn(openssl_csr_get_subject($csr)); } catch (exception $e) { return 'Invalid CSR'; } } public function validCA($format=FALSE) { return StaticList_YesNo::get(($this->_cert_details AND $this->ssl_ca_id AND $this->aki_keyid()==$this->ca->ski()) ? $this->ca->validParent() : FALSE,$format); } public function valid_from($format=FALSE) { $k = Arr::get($this->_cert_details,'validFrom_time_t'); if (! $k) return NULL; return $format ? Site::Date($k) : $k; } public function valid_to($format=FALSE) { $k = Arr::get($this->_cert_details,'validTo_time_t'); if (! $k) return NULL; return $format ? Site::Date($k) : $k; } // If we change the SSL certificate, we need to reload our SSL object public function values(array $values, array $expected = NULL) { parent::values($values,$expected); if (array_key_exists('cert',$this->_changed)) $this->_cert_details = openssl_x509_parse($this->cert); return $this; } public function version() { return Arr::get($this->_cert_details,'version'); } public function list_ca() { $result = array(); if (! $this->validCA()) return $result; $x = $this; while (! is_null($x->ssl_ca_id) AND $x->id != $x->ssl_ca_id AND $x=$x->ca) array_push($result,$x); return $result; } /** * Return all our CA Certs for this certificate */ public function list_ca_crts() { $result = array(); foreach ($this->list_ca() as $so) array_push($result,$so->cert); return $result; } } ?>