2014-10-09 12:23:02 +00:00
< ? php defined ( 'SYSPATH' ) or die ( 'No direct access allowed.' );
/**
* This class supports SSL Certificates
*
* @ package SSL
* @ category Models
* @ author Deon George
* @ copyright ( c ) 2009 - 2013 Deon George
* @ license http :// dev . leenooks . net / license . html
*/
abstract class lnApp_Model_SSL extends ORM {
protected $_cert_details = NULL ;
// Relationships
protected $_belongs_to = array (
'account' => 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 .= '<div class="form-group">' ;
$output .= '<div class="input-group">' ;
$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 .= '<span class="input-group-btn">' ;
}
$output .= Form :: button ( 'download' , 'Download' , array ( 'class' => 'btn btn-default' , 'nocg' => TRUE ));
if ( $passwd_len ) {
$output .= '</span>' ;
$output .= '</div>' ;
$output .= '<div class="help-block with-errors"></div>' ;
$output .= '</div>' ;
}
$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 ;
2014-10-11 11:51:35 +00:00
while ( ! is_null ( $x -> ssl_ca_id ) AND ( ! $x instanceof Model_SSL_CA OR $x -> id != $x -> ssl_ca_id ) AND $x = $x -> ca )
2014-10-09 12:23:02 +00:00
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 ;
}
}
?>