mirror of
https://github.com/danog/phpseclib.git
synced 2024-11-27 04:46:26 +01:00
- make it so an array returned by loadX509() can be reloaded by loadX509()
- validateDate() didn't work - add postalCode and streetAddress as supported DN attributes - add getDN() - split setKey() out into setPrivateKey() and setPublicKey() - add sign(), setStartDate(), setEndDate(), setSerialNumber(), removeExtension(), getExtension() and getExtensions() git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@209 21d32557-59b3-4da0-833f-c5933fad653e
This commit is contained in:
parent
09f4bef2f1
commit
3f9aa1ad6a
@ -117,12 +117,20 @@ class File_X509 {
|
||||
var $dn = array('rdnSequence' => array());
|
||||
|
||||
/**
|
||||
* Public or private key
|
||||
* Public key
|
||||
*
|
||||
* @var String
|
||||
* @access private
|
||||
*/
|
||||
var $key;
|
||||
var $publicKey;
|
||||
|
||||
/**
|
||||
* Private key
|
||||
*
|
||||
* @var String
|
||||
* @access private
|
||||
*/
|
||||
var $privateKey;
|
||||
|
||||
/**
|
||||
* Object identifiers for X.509 certificates
|
||||
@ -149,6 +157,41 @@ class File_X509 {
|
||||
*/
|
||||
var $currentCert;
|
||||
|
||||
/**
|
||||
* The signature subject
|
||||
*
|
||||
* There's no guarantee File_X509 is going to reencode an X.509 cert in the same way it was originally
|
||||
* encoded so we take save the portion of the original cert that the signature would have made for.
|
||||
*
|
||||
* @var String
|
||||
* @access private
|
||||
*/
|
||||
var $signatureSubject;
|
||||
|
||||
/**
|
||||
* Certificate Start Date
|
||||
*
|
||||
* @var String
|
||||
* @access private
|
||||
*/
|
||||
var $startDate;
|
||||
|
||||
/**
|
||||
* Certificate End Date
|
||||
*
|
||||
* @var String
|
||||
* @access private
|
||||
*/
|
||||
var $endDate;
|
||||
|
||||
/**
|
||||
* Serial Number
|
||||
*
|
||||
* @var String
|
||||
* @access private
|
||||
*/
|
||||
var $serialNumber;
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
*
|
||||
@ -984,6 +1027,8 @@ class File_X509 {
|
||||
'0.9.2342.19200300.100.1.25' => 'id-domainComponent',
|
||||
'1.2.840.113549.1.9' => 'pkcs-9',
|
||||
'1.2.840.113549.1.9.1' => 'id-emailAddress',
|
||||
'2.5.4.17' => 'id-at-postalCode',
|
||||
'2.5.4.9' => 'id-at-streetAddress',
|
||||
'2.5.29' => 'id-ce',
|
||||
'2.5.29.35' => 'id-ce-authorityKeyIdentifier',
|
||||
'2.5.29.14' => 'id-ce-subjectKeyIdentifier',
|
||||
@ -1112,12 +1157,20 @@ class File_X509 {
|
||||
/**
|
||||
* Load X.509 certificate
|
||||
*
|
||||
* Returns an associative array describing the X.509 cert or a false if the cert failed to load
|
||||
*
|
||||
* @param String $cert
|
||||
* @access public
|
||||
* @return Array
|
||||
* @return Mixed
|
||||
*/
|
||||
function loadX509($cert)
|
||||
{
|
||||
if (is_array($cert) && isset($cert['tbsCertificate'])) {
|
||||
$this->currentCert = $cert;
|
||||
unset($this->signatureSubject);
|
||||
return false;
|
||||
}
|
||||
|
||||
$asn1 = new File_ASN1();
|
||||
|
||||
/*
|
||||
@ -1198,6 +1251,10 @@ class File_X509 {
|
||||
*/
|
||||
function saveX509($cert)
|
||||
{
|
||||
if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm']) {
|
||||
case 'rsaEncryption':
|
||||
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] =
|
||||
@ -1389,8 +1446,8 @@ class File_X509 {
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case time() < @strtotime($this->currentCert['tbsCertificate']['notBefore']):
|
||||
case time() > @strtotime($this->currentCert['tbsCertificate']['notAfter']):
|
||||
case time() < @strtotime($this->currentCert['tbsCertificate']['validity']['notBefore']):
|
||||
case time() > @strtotime($this->currentCert['tbsCertificate']['validity']['notAfter']):
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1409,7 +1466,7 @@ class File_X509 {
|
||||
*/
|
||||
function validateSignature($options = 0)
|
||||
{
|
||||
if (!is_array($this->currentCert)) {
|
||||
if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1552,7 +1609,7 @@ class File_X509 {
|
||||
case 'id-at-organizationname':
|
||||
case 'organizationname':
|
||||
case 'o':
|
||||
$type = 'id-at-organizationname';
|
||||
$type = 'id-at-organizationName';
|
||||
break;
|
||||
case 'id-at-dnqualifier':
|
||||
case 'dnqualifier':
|
||||
@ -1585,6 +1642,14 @@ class File_X509 {
|
||||
case 'serialnumber':
|
||||
$type = 'id-at-serialNumber';
|
||||
break;
|
||||
case 'id-at-postalcode':
|
||||
case 'postalcode':
|
||||
$type = 'id-at-postalCode';
|
||||
break;
|
||||
case 'id-at-streetaddress':
|
||||
case 'streetaddress':
|
||||
$type = 'id-at-streetAddress';
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -1608,8 +1673,13 @@ class File_X509 {
|
||||
*/
|
||||
function setDN($dn)
|
||||
{
|
||||
// handles stuff generated by openssl_x509_parse()
|
||||
if (is_array($dn)) {
|
||||
if (isset($dn['rdnSequence'])) {
|
||||
$this->dn = $dn;
|
||||
return true;
|
||||
}
|
||||
|
||||
// handles stuff generated by openssl_x509_parse()
|
||||
foreach ($dn as $type => $value) {
|
||||
if (!$this->setDNProp($type, $value)) {
|
||||
return false;
|
||||
@ -1619,9 +1689,9 @@ class File_X509 {
|
||||
}
|
||||
|
||||
// handles everything else
|
||||
$results = preg_split('#((?:^|, )(?:C=|O=|OU=|CN=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$results = preg_split('#((?:^|, |/)(?:C=|O=|OU=|CN=|L=|ST=|postalCode=|streetAddress=|emailAddress=|serialNumber=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
for ($i = 1; $i < count($results); $i+=2) {
|
||||
$type = trim($results[$i], ', =');
|
||||
$type = trim($results[$i], ', =/');
|
||||
$value = $results[$i + 1];
|
||||
if (!$this->setDNProp($type, $value)) {
|
||||
return false;
|
||||
@ -1632,7 +1702,88 @@ class File_X509 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set public or private key
|
||||
* Get the Distinguished Name for a certificates subject
|
||||
*
|
||||
* @param Boolean $string optional
|
||||
* @access public
|
||||
* @return Boolean
|
||||
*/
|
||||
function getDN($string = false)
|
||||
{
|
||||
if (!$string) {
|
||||
return $this->currentCert['tbsCertificate']['subject'];
|
||||
}
|
||||
|
||||
$start = true;
|
||||
foreach ($this->currentCert['tbsCertificate']['subject']['rdnSequence'] as $field) {
|
||||
$type = $field[0]['type'];
|
||||
$value = $field[0]['value'];
|
||||
|
||||
$delim = ', ';
|
||||
switch ($type) {
|
||||
case 'id-at-countryName':
|
||||
$desc = 'C=';
|
||||
break;
|
||||
case 'id-at-stateOrProvinceName':
|
||||
$desc = 'ST=';
|
||||
break;
|
||||
case 'id-at-organizationName':
|
||||
$desc = 'O=';
|
||||
break;
|
||||
case 'id-at-dnQualifier':
|
||||
$desc = 'OU=';
|
||||
break;
|
||||
case 'id-at-commonName':
|
||||
$desc = 'CN=';
|
||||
break;
|
||||
case 'id-at-localityName':
|
||||
$desc = 'L=';
|
||||
break;
|
||||
default:
|
||||
$delim = '/';
|
||||
$desc = preg_replace('#.+-([^-]+)$#', '$1', $type) . '=';
|
||||
}
|
||||
|
||||
if (!$start) {
|
||||
$output.= $delim;
|
||||
}
|
||||
$output.= $desc . $value;
|
||||
$start = false;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set public key
|
||||
*
|
||||
* Key needs to be a Crypt_RSA object
|
||||
*
|
||||
* @param Object $key
|
||||
* @access public
|
||||
* @return Boolean
|
||||
*/
|
||||
function setPublicKey($key)
|
||||
{
|
||||
$this->publicKey = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set private key
|
||||
*
|
||||
* Key needs to be a Crypt_RSA object
|
||||
*
|
||||
* @param Object $key
|
||||
* @access public
|
||||
* @return Boolean
|
||||
*/
|
||||
function setPrivateKey($key)
|
||||
{
|
||||
$this->privateKey = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public key
|
||||
*
|
||||
* Keys need to be Crypt_RSA objects
|
||||
*
|
||||
@ -1640,9 +1791,9 @@ class File_X509 {
|
||||
* @access public
|
||||
* @return Boolean
|
||||
*/
|
||||
function setKey($key)
|
||||
function getPublicKey($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->publicKey = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1685,14 +1836,224 @@ class File_X509 {
|
||||
if (!class_exists('Crypt_RSA')) {
|
||||
require_once('Crypt/RSA.php');
|
||||
}
|
||||
$this->key = new Crypt_RSA();
|
||||
$this->key->loadKey($key);
|
||||
$this->publicKey = new Crypt_RSA();
|
||||
$this->publicKey->loadKey($key);
|
||||
default:
|
||||
$this->key = NULL;
|
||||
$this->publicKey = NULL;
|
||||
}
|
||||
|
||||
$this->currentCert = $csr;
|
||||
|
||||
return $csr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign an X.509 certificate
|
||||
*
|
||||
* $issuer's private key needs to be loaded.
|
||||
* $subject can be either an existing X.509 cert (if you want to resign it),
|
||||
* a CSR or something with the DN and public key explicitly set.
|
||||
*
|
||||
* @param Crypt_X509 $issuer
|
||||
* @param Crypt_X509 $subject
|
||||
* @param String $signatureAlgorithm optional
|
||||
* @access public
|
||||
* @return Mixed
|
||||
*/
|
||||
function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
|
||||
{
|
||||
if (!is_object($issuer->privateKey) || !is_array($issuer->dn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentCert = $this->currentCert;
|
||||
$signatureSubject = $this->signatureSubject;
|
||||
|
||||
if (is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
|
||||
$this->currentCert = $subject->currentCert;
|
||||
if (!empty($this->startDate)) {
|
||||
$this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime'] = $this->startDate;
|
||||
unset($this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime']);
|
||||
}
|
||||
if (!empty($this->endDate)) {
|
||||
$this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime'] = $this->endDate;
|
||||
unset($this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime']);
|
||||
}
|
||||
if (!empty($this->dn)) {
|
||||
$this->currentCert['tbsCertificate']['subject'] = $this->dn;
|
||||
}
|
||||
$this->removeExtension('id-ce-authorityKeyIdentifier');
|
||||
|
||||
} else {
|
||||
$startDate = empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T');
|
||||
$endDate = empty($this->endDate) ? $this->endDate : @date('M j H:i:s Y T', strtotime('+1 year'));
|
||||
$serialNumber = empty($this->serialNumber) ? $this->serialNumber : "\0";
|
||||
|
||||
$this->currentCert = array(
|
||||
'tbsCertificate' =>
|
||||
array(
|
||||
'version' => 'v3',
|
||||
'serialNumber' => $this->serialNumber, // $this->setserialNumber()
|
||||
'signature' => $signatureAlgorithm,
|
||||
'issuer' => false, // this is going to be overwritten later
|
||||
'validity' => array(
|
||||
'notBefore' => array('utcTime' => $this->startDate), // $this->setStartDate()
|
||||
'notAfter' => array('utcTime' => $this->endDate) // $this->setEndDate()
|
||||
),
|
||||
'subject' => $subject->dn,
|
||||
'subjectPublicKeyInfo' => $subject->publicKey->getPublicKey()
|
||||
),
|
||||
'signatureAlgorithm' => $signatureAlgorithm,
|
||||
'signature' => false // this is going to be overwritten later
|
||||
);
|
||||
}
|
||||
|
||||
$this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;
|
||||
|
||||
$this->loadX509($this->saveX509($this->currentCert));
|
||||
|
||||
$result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
|
||||
|
||||
$this->currentCert = $currentCert;
|
||||
$this->signatureSubject = $signatureSubject;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* X.509 certificate signing helper function.
|
||||
*
|
||||
* @param Object $key
|
||||
* @param Crypt_X509 $subject
|
||||
* @param String $signatureAlgorithm
|
||||
* @access public
|
||||
* @return Mixed
|
||||
*/
|
||||
function _sign($key, $signatureAlgorithm)
|
||||
{
|
||||
switch (strtolower(get_class($key))) {
|
||||
case 'crypt_rsa':
|
||||
switch ($signatureAlgorithm) {
|
||||
case 'md2WithRSAEncryption':
|
||||
case 'md5WithRSAEncryption':
|
||||
case 'sha1WithRSAEncryption':
|
||||
case 'sha224WithRSAEncryption':
|
||||
case 'sha256WithRSAEncryption':
|
||||
case 'sha384WithRSAEncryption':
|
||||
case 'sha512WithRSAEncryption':
|
||||
$key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
|
||||
$key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
||||
|
||||
$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
|
||||
|
||||
return $this->currentCert;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set certificate start date
|
||||
*
|
||||
* @param String $date
|
||||
* @access public
|
||||
*/
|
||||
function setStartDate($date)
|
||||
{
|
||||
$this->startDate = @date('M j H:i:s Y T', @strtotime($date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set certificate end date
|
||||
*
|
||||
* @param String $date
|
||||
* @access public
|
||||
*/
|
||||
function setEndDate($date)
|
||||
{
|
||||
$this->endDate = @date('M j H:i:s Y T', @strtotime($date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Serial Number
|
||||
*
|
||||
* @param String $serial
|
||||
* @access public
|
||||
*/
|
||||
function setSerialNumber($serial)
|
||||
{
|
||||
$this->serialNumber = $serial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an Extension
|
||||
*
|
||||
* @param String $id
|
||||
* @access public
|
||||
* @return Boolean
|
||||
*/
|
||||
function removeExtension($id)
|
||||
{
|
||||
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
$extensions = &$this->currentCert['tbsCertificate']['extensions'];
|
||||
foreach ($extensions as $key => $value) {
|
||||
if ($value['extnId'] == $id) {
|
||||
unset($extensions[$key]);
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
|
||||
$extensions = array_values($extensions);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Extension
|
||||
*
|
||||
* Returns the extension if it exists and false if not
|
||||
*
|
||||
* @param String $id
|
||||
* @access public
|
||||
* @return Mixed
|
||||
*/
|
||||
function getExtension($id)
|
||||
{
|
||||
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->currentCert['tbsCertificate']['extensions'] as $key => $value) {
|
||||
if ($value['extnId'] == $id) {
|
||||
return $value['extnValue'];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all extensions in use
|
||||
*
|
||||
* @access public
|
||||
* @return Array
|
||||
*/
|
||||
function getExtensions()
|
||||
{
|
||||
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$extensions = array();
|
||||
foreach ($this->currentCert['tbsCertificate']['extensions'] as $extension) {
|
||||
$extensions[] = $extension['extnId'];
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user