- make Crypt_RSA use openssl for key generation (if openssl is available) and make it so File_X509 can create CSRs

This commit is contained in:
terrafrost 2012-07-01 12:07:42 -05:00
parent f0e1b2deec
commit 1417463eba
3 changed files with 137 additions and 24 deletions

View File

@ -439,9 +439,9 @@ class Crypt_RSA {
{
if ( !defined('CRYPT_RSA_MODE') ) {
switch (true) {
//case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
// define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
// break;
case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
break;
default:
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
}
@ -477,9 +477,28 @@ class Crypt_RSA {
*/
function createKey($bits = 1024, $timeout = false, $partial = array())
{
if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) {
$rsa = openssl_pkey_new(array('private_key_bits' => $bits));
openssl_pkey_export($rsa, $privatekey);
if (!defined('CRYPT_RSA_EXPONENT')) {
// http://en.wikipedia.org/wiki/65537_%28number%29
define('CRYPT_RSA_EXPONENT', '65537');
}
// per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
// than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
// to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
// CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
// CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
// generation when there's a chance neither gmp nor OpenSSL are installed)
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
}
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
$rsa = openssl_pkey_new(array(
'private_key_bits' => $bits,
'config' => dirname(__FILE__) . '/../openssl.cnf'
));
openssl_pkey_export($rsa, $privatekey, NULL, array('config' => dirname(__FILE__) . '/../openssl.cnf'));
$publickey = openssl_pkey_get_details($rsa);
$publickey = $publickey['key'];
@ -488,6 +507,9 @@ class Crypt_RSA {
$publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
}
// clear the buffer of error strings stemming from a minimalistic openssl.cnf
while (openssl_error_string() !== false);
return array(
'privatekey' => $privatekey,
'publickey' => $publickey,
@ -497,22 +519,12 @@ class Crypt_RSA {
static $e;
if (!isset($e)) {
if (!defined('CRYPT_RSA_EXPONENT')) {
// http://en.wikipedia.org/wiki/65537_%28number%29
define('CRYPT_RSA_EXPONENT', '65537');
}
// per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
// than 256 bits.
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
}
$e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
}
extract($this->_generateMinMax($bits));
$absoluteMin = $min;
$temp = $bits >> 1;
$temp = $bits >> 1; // divide by two to see how many bits P and Q would be
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
$temp = CRYPT_RSA_SMALLEST_PRIME;

View File

@ -1269,7 +1269,7 @@ class File_X509 {
/**
* Save X.509 certificate
*
* @param optional Array $cert
* @param Array $cert
* @access public
* @return String
*/
@ -2059,6 +2059,7 @@ class File_X509 {
$orig = $csr = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $csr) ? base64_decode($csr) : false;
if ($csr === false) {
$this->currentCert = false;
return false;
}
@ -2066,6 +2067,7 @@ class File_X509 {
$decoded = $asn1->decodeBER($csr);
$csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
if (!isset($csr) || $csr === false) {
$this->currentCert = false;
return false;
}
@ -2095,6 +2097,40 @@ class File_X509 {
return $csr;
}
/**
* Save CSR request
*
* @param Array $csr
* @access public
* @return String
*/
function saveCSR($csr)
{
if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
return false;
}
switch ($csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']) {
case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
}
$asn1 = new File_ASN1();
$asn1->loadOIDs($this->oids);
$filters = array();
$filters['certificationRequestInfo']['subject']['rdnSequence']['value'] =
array('type' => FILE_ASN1_TYPE_UTF8_STRING);
$asn1->loadFilters($filters);
$csr = $asn1->encodeDER($csr, $this->CertificationRequest);
return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr)) . '-----END CERTIFICATE REQUEST-----';
}
/**
* Sign an X.509 certificate
*
@ -2102,8 +2138,8 @@ class File_X509 {
* $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 File_X509 $issuer
* @param File_X509 $subject
* @param String $signatureAlgorithm optional
* @access public
* @return Mixed
@ -2122,10 +2158,10 @@ class File_X509 {
$signatureSubject = $this->signatureSubject;
if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert;
$this->currentCert['tbsCertificate']['signature']['algorithm'] =
$this->currentCert['signatureAlgorithm']['algorithm'] =
$signatureAlgorithm;
$this->currentCert = $subject->currentCert;
if (!empty($this->startDate)) {
$this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate;
unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']);
@ -2258,11 +2294,70 @@ class File_X509 {
return $result;
}
/**
* Sign a CSR
*
* @access public
* @return Mixed
*/
function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
{
if (!is_object($this->privateKey) || empty($this->dn)) {
return false;
}
$origPublicKey = $this->publicKey;
$class = get_class($this->privateKey);
$this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey();
if (!($publicKey = $this->_formatSubjectPublicKey())) {
return false;
}
$this->publicKey = $origPublicKey;
$currentCert = $this->currentCert;
$signatureSubject = $this->signatureSubject;
if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
$this->currentCert['signatureAlgorithm']['algorithm'] =
$signatureAlgorithm;
if (!empty($this->dn)) {
$this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
}
$this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
} else {
$this->currentCert = array(
'certificationRequestInfo' =>
array(
'version' => 'v1',
'subject' => $this->dn,
'subjectPKInfo' => $publicKey
),
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
'signature' => false // this is going to be overwritten later
);
}
// resync $this->signatureSubject
// save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
$certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
$this->loadCSR($this->saveCSR($this->currentCert));
$result = $this->_sign($this->privateKey, $signatureAlgorithm);
$result['certificationRequestInfo'] = $certificationRequestInfo;
$this->currentCert = $currentCert;
$this->signatureSubject = $signatureSubject;
return $result;
}
/**
* X.509 certificate signing helper function.
*
* @param Object $key
* @param Crypt_X509 $subject
* @param File_X509 $subject
* @param String $signatureAlgorithm
* @access public
* @return Mixed

6
phpseclib/openssl.cnf Normal file
View File

@ -0,0 +1,6 @@
# minimalist openssl.cnf file for use with phpseclib
HOME = .
RANDFILE = $ENV::HOME/.rnd
[ v3_ca ]