Improvements to SSL classes
This commit is contained in:
@@ -27,7 +27,7 @@ class Controller_Admin_SSL extends Controller_TemplateDefault_Admin {
|
||||
'id'=>array('label'=>'ID','url'=>'admin/ssl/view/'),
|
||||
'sign_cert'=>array('label'=>'Cert'),
|
||||
'issuer()'=>array('label'=>'Issuer'),
|
||||
'expires(TRUE)'=>array('label'=>'Expires'),
|
||||
'valid_to(TRUE)'=>array('label'=>'Expires'),
|
||||
),
|
||||
array(
|
||||
'page'=>TRUE,
|
||||
@@ -41,18 +41,32 @@ class Controller_Admin_SSL extends Controller_TemplateDefault_Admin {
|
||||
$so = ORM::factory('ssl_ca',$id);
|
||||
|
||||
if ($_POST) {
|
||||
if ($so->values($_POST)->check() AND $so->save())
|
||||
SystemMessage::add(array(
|
||||
'title'=>'SSL Certificate Saved',
|
||||
'type'=>'info',
|
||||
'body'=>'SSL Certificate successfully recorded.',
|
||||
));
|
||||
if ($so->values($_POST)->changed()) {
|
||||
try {
|
||||
$so->save();
|
||||
SystemMessage::add(array(
|
||||
'title'=>'SSL Certificate Saved',
|
||||
'type'=>'info',
|
||||
'body'=>'SSL Certificate successfully recorded.',
|
||||
));
|
||||
|
||||
} catch (ORM_Validation_Exception $e) {
|
||||
$errors = $e->errors('models');
|
||||
|
||||
SystemMessage::add(array(
|
||||
'title'=>'SSL Certificate NOT saved',
|
||||
'type'=>'error',
|
||||
'body'=>join("\n",array_values($errors)),
|
||||
));
|
||||
|
||||
$so->reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= Form::open();
|
||||
$output .= View::factory('ssl/admin/add_view')
|
||||
->set('so',$so)
|
||||
->set('mediapath',Route::get('default/media'));
|
||||
->set('o',$so);
|
||||
$output .= Form::submit('submit','submit',array('class'=>'form_button'));
|
||||
$output .= Form::close();
|
||||
|
||||
|
@@ -35,68 +35,51 @@ class Model_Service_Plugin_SSL extends Model_Service_Plugin {
|
||||
public function username_value() {} // Not used
|
||||
public function password_value() {} // Not used
|
||||
|
||||
public function service_view() {
|
||||
return View::factory('service/user/plugin/ssl/view')
|
||||
->set('so',$this);
|
||||
private $_so = NULL;
|
||||
|
||||
/**
|
||||
* Resolve any queries to certificate details
|
||||
*/
|
||||
public function __call($name,$args) {
|
||||
$m = 'get_'.$name;
|
||||
|
||||
if (method_exists($this->_so,$m))
|
||||
return $this->_so->{$m}($args);
|
||||
else
|
||||
throw new Kohana_Exception('Unknown method :method',array(':method'=>$name));
|
||||
}
|
||||
|
||||
// We want to inject the SSL object into this Model
|
||||
protected function _load_values(array $values) {
|
||||
parent::_load_values($values);
|
||||
|
||||
if ($this->cert)
|
||||
$this->_so = SSL::instance($this->cert);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// 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',$values))
|
||||
$this->_so = SSL::instance($this->cert);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function expire() {
|
||||
return $this->valid_to();
|
||||
return $this->_so->get_valid_to();
|
||||
}
|
||||
|
||||
public function name() {
|
||||
if ($this->cert) {
|
||||
return sprintf('%s:%s',$this->ssl_ca->subject(),$this->display('cert'));
|
||||
} else
|
||||
return $this->display('csr');
|
||||
return ($this->cert) ? sprintf('%s:%s',$this->ssl_ca->subject(),$this->display('cert')) : $this->display('csr');
|
||||
}
|
||||
|
||||
public function algorithm() {
|
||||
return SSL::algorithm($this->cert);
|
||||
}
|
||||
|
||||
public function dn() {
|
||||
return SSL::dn($this->cert);
|
||||
}
|
||||
|
||||
public function dnissuer() {
|
||||
return SSL::dnissuer($this->cert);
|
||||
}
|
||||
|
||||
public function issuer() {
|
||||
return SSL::issuer($this->cert);
|
||||
}
|
||||
|
||||
// @todo This needs to be validated for this model
|
||||
public function product() {
|
||||
if ($this->provided_adsl_plan_id)
|
||||
return $this->adsl_plan;
|
||||
else
|
||||
return $this->service->product->plugin();
|
||||
}
|
||||
|
||||
public function details() {
|
||||
return SSL::details($this->cert);
|
||||
}
|
||||
|
||||
public function valid_from($format=FALSE) {
|
||||
return SSL::from($this->cert,$format);
|
||||
}
|
||||
|
||||
public function valid_to($format=FALSE) {
|
||||
return SSL::expire($this->cert,$format);
|
||||
}
|
||||
|
||||
public function serial_num() {
|
||||
return SSL::serial($this->cert);
|
||||
}
|
||||
|
||||
public function hash() {
|
||||
return SSL::hash($this->cert);
|
||||
}
|
||||
|
||||
public function version() {
|
||||
return SSL::version($this->cert);
|
||||
public function service_view() {
|
||||
return View::factory('service/user/plugin/ssl/view')
|
||||
->set('so',$this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -14,9 +14,6 @@ class Model_SSL_CA extends ORM_OSB {
|
||||
protected $_updated_column = FALSE;
|
||||
|
||||
// Relationships
|
||||
protected $_belongs_to = array(
|
||||
);
|
||||
|
||||
protected $_has_many = array(
|
||||
'service'=>array('through'=>'service__ssl'),
|
||||
);
|
||||
@@ -27,44 +24,70 @@ class Model_SSL_CA extends ORM_OSB {
|
||||
),
|
||||
);
|
||||
|
||||
public function expires($format=FALSE) {
|
||||
return SSL::expire($this->sign_cert,$format);
|
||||
public function rules() {
|
||||
return array(
|
||||
'sign_cert'=>array(
|
||||
array(array($this,'isCert')),
|
||||
array(array($this,'isCA')),
|
||||
),
|
||||
'parent_ssl_ca_id'=>array(
|
||||
array(array($this,'Rule_ParentExists')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function issuer() {
|
||||
return SSL::issuer($this->sign_cert);
|
||||
public function filters() {
|
||||
return array(
|
||||
'parent_ssl_ca_id'=>array(
|
||||
array(array($this,'Filter_GetParent')),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function subject() {
|
||||
return SSL::subject($this->sign_cert);
|
||||
private $_so = NULL;
|
||||
|
||||
/**
|
||||
* Resolve any queries to certificate details
|
||||
*/
|
||||
public function __call($name,$args) {
|
||||
$m = 'get_'.$name;
|
||||
|
||||
if (method_exists($this->_so,$m))
|
||||
return $this->_so->{$m}($args);
|
||||
else
|
||||
throw new Kohana_Exception('Unknown method :method',array(':method'=>$name));
|
||||
}
|
||||
|
||||
public function save(Validation $validation = NULL) {
|
||||
// If our parent_ssl_ca_id is null, we'll need to work it out
|
||||
if (is_null($this->parent_ssl_ca_id)) {
|
||||
$i = SSL::issuer($this->sign_cert);
|
||||
// We want to inject the SSL object into this Model
|
||||
protected function _load_values(array $values) {
|
||||
parent::_load_values($values);
|
||||
|
||||
$po = NULL;
|
||||
foreach (ORM::factory('ssl_ca')->find_all() as $sco)
|
||||
if ($sco->subject() == $i) {
|
||||
$po = $sco;
|
||||
break;
|
||||
}
|
||||
if ($this->sign_cert)
|
||||
$this->_so = SSL::instance($this->sign_cert);
|
||||
|
||||
if (is_null($po)) {
|
||||
SystemMessage::add(array(
|
||||
'title'=>'Certificate NOT Recorded',
|
||||
'type'=>'warning',
|
||||
'body'=>sprintf('Parent Certificate is not available (%s)',$this->issuer()),
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
} else
|
||||
$this->parent_ssl_ca_id = $po->id;
|
||||
}
|
||||
// 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);
|
||||
|
||||
// Save the record
|
||||
return parent::save($validation);
|
||||
if (array_key_exists('sign_cert',$values))
|
||||
$this->_so = SSL::instance($this->sign_cert);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// @todo This could require some optimisation, by storing the keyid in the database and then getting the DB just to return that parent
|
||||
public function Filter_GetParent() {
|
||||
foreach (ORM::factory($this->_object_name)->find_all() as $sco)
|
||||
if ($sco->aki_keyid() == $this->aki_keyid())
|
||||
return $sco->id;
|
||||
}
|
||||
|
||||
public function Rule_ParentExists() {
|
||||
// Our parent_ssl_ca_id should have been populated by Filter_GetParent().
|
||||
return $this->parent_ssl_ca_id OR $this->isRoot();
|
||||
}
|
||||
|
||||
public function list_issued() {
|
||||
|
@@ -4,132 +4,214 @@
|
||||
* This class is for access to SSL information
|
||||
*
|
||||
* @package OSB
|
||||
* @subpackage System
|
||||
* @subpackage SSL
|
||||
* @category Helpers
|
||||
* @author Deon George
|
||||
* @copyright (c) 2010 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class SSL {
|
||||
public static function instance() {
|
||||
return new SSL;
|
||||
private $cert = '';
|
||||
private $_details = array();
|
||||
|
||||
public function __construct($cert) {
|
||||
$this->cert = $cert;
|
||||
}
|
||||
|
||||
public static function details($cert,$key=NULL) {
|
||||
$k = openssl_x509_parse($cert);
|
||||
|
||||
return is_null($key) ? $k : $k[$key];
|
||||
public static function instance($cert) {
|
||||
return new SSL($cert);
|
||||
}
|
||||
|
||||
public static function algorithm($cert,$key=NULL) {
|
||||
if (! $cert)
|
||||
/**
|
||||
* This function will convert a large decimal number into hex
|
||||
* @param $number Large decimal number
|
||||
*/
|
||||
private 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 AuthorityKeyIndentifier Extension to extract information
|
||||
* @param $key Return just that index
|
||||
*/
|
||||
private function _aki($key=NULL) {
|
||||
$return = array();
|
||||
|
||||
$aki = $this->_extensions('authorityKeyIdentifier');
|
||||
if (! $aki)
|
||||
return '';
|
||||
|
||||
$r = openssl_x509_read($cert);
|
||||
openssl_x509_export($r,$e,FALSE);
|
||||
foreach (explode("\n",preg_replace("/\n$/",'',$aki)) as $x) {
|
||||
if (! $x)
|
||||
continue;
|
||||
|
||||
if (strstr($x,':')) {
|
||||
list($a,$b) = explode(':',$x,2);
|
||||
$return[strtolower($a)] = $b;
|
||||
}
|
||||
}
|
||||
|
||||
return is_null($key) ? $return : (isset($return[$key]) ? $return[$key] : '');
|
||||
}
|
||||
|
||||
private function _bc() {
|
||||
return $this->_extensions('basicConstraints');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse our Sign Certifcate to extract information
|
||||
* @param $key Return just that index
|
||||
*/
|
||||
private function _details($key=NULL) {
|
||||
if (! $this->cert)
|
||||
return array();
|
||||
|
||||
if (! $this->_details)
|
||||
$this->_details = openssl_x509_parse($this->cert);
|
||||
|
||||
return is_null($key) ? $this->_details : (isset($this->_details[$key]) ? $this->_details[$key] : array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse our Sign Certifcate Extensions to extract information
|
||||
* @param $key Return just that index
|
||||
*/
|
||||
private function _extensions($key=NULL) {
|
||||
$return = $this->_details('extensions');
|
||||
|
||||
return is_null($key) ? $return : (isset($return[$key]) ? $return[$key] : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a DN array as a string
|
||||
*/
|
||||
private function _dn(array $array) {
|
||||
$return = '';
|
||||
$i = 0;
|
||||
|
||||
foreach ($array as $k=>$v) {
|
||||
if ($i++)
|
||||
$return .= ',';
|
||||
|
||||
$return .= sprintf('%s=%s',$k,$v);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function get_aki_dirname() {
|
||||
return $this->_aki('dirname');
|
||||
}
|
||||
|
||||
public function get_aki_keyid() {
|
||||
return $this->_aki('keyid');
|
||||
}
|
||||
|
||||
public function get_aki_serial() {
|
||||
return $this->_aki('serial');
|
||||
}
|
||||
|
||||
public function get_algorithm() {
|
||||
$e = '';
|
||||
openssl_x509_export(openssl_x509_read($this->cert),$e,FALSE);
|
||||
|
||||
// @todo There must be a nice way to get this?
|
||||
if (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match))
|
||||
return $match[1];
|
||||
else
|
||||
return _('Unknown');
|
||||
return (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match)) ? $match[1] : _('Unknown');
|
||||
}
|
||||
|
||||
public static function aki($cert,$key=NULL) {
|
||||
$k = array();
|
||||
foreach (explode("\n",preg_replace("/\n$/",'',static::extensions($cert,'authorityKeyIdentifier'))) as $x) {
|
||||
list($a,$b) = explode(":",$x,2);
|
||||
$k[strtolower($a)] = $b;
|
||||
}
|
||||
public function get_ca_path_len() {
|
||||
$m = array();
|
||||
$x = preg_match('/.*pathlen:\s*([0-9]+).*$/',$this->_bc(),$m);
|
||||
|
||||
return is_null($key) ? $k : $k[$key];
|
||||
return isset($m[1]) ? (int)$m[1] : 0;
|
||||
}
|
||||
|
||||
public static function aki_keyid($key) {
|
||||
return static::aki($key,'keyid');
|
||||
public function get_dn() {
|
||||
return $this->_dn($this->_details('subject'));
|
||||
}
|
||||
|
||||
public static function aki_dirname($key) {
|
||||
return static::aki($key,'dirname');
|
||||
public function get_hash() {
|
||||
return $this->_details('hash');
|
||||
}
|
||||
|
||||
public static function aki_serial($key) {
|
||||
return static::aki($key,'serial');
|
||||
public function get_isCA() {
|
||||
return preg_match('/CA:TRUE/',$this->_bc());
|
||||
}
|
||||
|
||||
public static function dn($cert) {
|
||||
if (! $cert)
|
||||
return '';
|
||||
|
||||
$s = '';
|
||||
|
||||
$c = 0;
|
||||
foreach (static::details($cert,'subject') as $k=>$v) {
|
||||
if ($c++)
|
||||
$s .= ',';
|
||||
|
||||
$s .= sprintf('%s=%s',$k,$v);
|
||||
}
|
||||
|
||||
return $s;
|
||||
public function get_isCert() {
|
||||
return is_array($this->_details());
|
||||
}
|
||||
|
||||
public static function dnissuer($cert) {
|
||||
if (! $cert)
|
||||
return '';
|
||||
|
||||
$s = '';
|
||||
|
||||
$c = 0;
|
||||
foreach (static::details($cert,'issuer') as $k=>$v) {
|
||||
if ($c++)
|
||||
$s .= ',';
|
||||
|
||||
$s .= sprintf('%s=%s',$k,$v);
|
||||
}
|
||||
|
||||
return $s;
|
||||
public function get_isRoot() {
|
||||
return $this->get_aki_keyid() == $this->get_ski();
|
||||
}
|
||||
|
||||
public static function issuer($cert) {
|
||||
$k = static::details($cert,'issuer');
|
||||
return $k['CN'];
|
||||
public function get_issuer() {
|
||||
$k = $this->_details('issuer');
|
||||
|
||||
return isset($k['CN']) ? $k['CN'] : '';
|
||||
}
|
||||
|
||||
public static function from($cert,$format=FALSE) {
|
||||
$k = static::details($cert,'validFrom_time_t');
|
||||
public function get_issuerdn() {
|
||||
return $this->_dn($this->_details('issuer'));
|
||||
}
|
||||
|
||||
public function get_serial() {
|
||||
return $this->_dec_to_hex($this->_details('serialNumber'));
|
||||
}
|
||||
|
||||
public function get_subject() {
|
||||
$k = $this->_details('subject');
|
||||
|
||||
return isset($k['CN']) ? $k['CN'] : '';
|
||||
}
|
||||
|
||||
public function get_ski() {
|
||||
return $this->_extensions('subjectKeyIdentifier');
|
||||
}
|
||||
|
||||
public function get_valid_to($format=FALSE) {
|
||||
$k = $this->_details('validTo_time_t');
|
||||
|
||||
return $format ? Config::date($k) : $k;
|
||||
}
|
||||
|
||||
public static function expire($key,$format=FALSE) {
|
||||
$k = static::details($key,'validTo_time_t');
|
||||
public function get_valid_from($format=FALSE) {
|
||||
$k = $this->_details('validFrom_time_t');
|
||||
|
||||
return $format ? Config::date($k) : $k;
|
||||
}
|
||||
|
||||
public static function extensions($cert,$key=NULL) {
|
||||
$k = static::details($cert,'extensions');
|
||||
return is_null($key) ? $k : $k[$key];
|
||||
public function get_version() {
|
||||
return $this->_details('version');
|
||||
}
|
||||
|
||||
public static function hash($key) {
|
||||
return static::details($key,'hash');
|
||||
public static function xdn($cert) {
|
||||
return static::instance($cert)->get_dn();
|
||||
}
|
||||
|
||||
public static function serial($key) {
|
||||
return static::dec_to_hex(static::details($key,'serialNumber'));
|
||||
public static function xexpire($cert,$format=FALSE) {
|
||||
return static::instance($cert)->get_expire($format);
|
||||
}
|
||||
|
||||
public static function subject($key) {
|
||||
$k = static::details($key,'subject');
|
||||
return $k['CN'];
|
||||
}
|
||||
|
||||
public static function ski($key) {
|
||||
return static::extensions($key,'subjectKeyIdentifier');
|
||||
}
|
||||
|
||||
public static function version($key) {
|
||||
return static::details($key,'version');
|
||||
public static function subject($cert) {
|
||||
return static::instance($cert)->get_subject();
|
||||
}
|
||||
|
||||
public static function csrsubject($csr) {
|
||||
@@ -137,25 +219,5 @@ class SSL {
|
||||
|
||||
return $c['CN'];
|
||||
}
|
||||
|
||||
private 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))));
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
Reference in New Issue
Block a user