RSA: refactor PKCS1/PKCS8 to facilitate re-use

This commit is contained in:
terrafrost 2016-10-19 07:45:42 -05:00
parent 9d5aa56c2f
commit 863ff6789b
22 changed files with 2030 additions and 958 deletions

View File

@ -1,12 +1,8 @@
language: php
php:
- 5.5.9
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm
env:
global:

View File

@ -53,7 +53,7 @@
"require": {
"paragonie/constant_time_encoding": "^1|^2",
"paragonie/random_compat": "^1.4|^2.0",
"php": ">=5.3.3"
"php": ">=7.0"
},
"require-dev": {
"phing/phing": "~2.7",

View File

@ -0,0 +1,80 @@
<?php
/**
* PKCS Formatted Key Handler
*
* PHP version 5
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
/**
* PKCS1 Formatted Key Handler
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
abstract class PKCS
{
/**
* Auto-detect the format
*/
const MODE_ANY = 0;
/**
* Require base64-encoded PEM's be supplied
*/
const MODE_PEM = 1;
/**
* Require raw DER's be supplied
*/
const MODE_DER = 2;
/**#@-*/
/**
* Is the key a base-64 encoded PEM, DER or should it be auto-detected?
*
* @access private
* @param int
*/
static $format = self::MODE_ANY;
/**
* Require base64-encoded PEM's be supplied
*
* @access public
*/
static function requirePEM()
{
self::$format = self::MODE_PEM;
}
/**
* Require raw DER's be supplied
*
* @access public
*/
static function requireDER()
{
self::$format = self::MODE_DER;
}
/**
* Accept any format and auto detect the format
*
* This is the default setting
*
* @access public
*/
static function requireAny()
{
self::$format = self::MODE_ANY;
}
}

View File

@ -0,0 +1,224 @@
<?php
/**
* PKCS1 Formatted Key Handler
*
* PHP version 5
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\Common\BlockCipher;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Base;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\TripleDES;
use phpseclib\File\ASN1;
/**
* PKCS1 Formatted Key Handler
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
abstract class PKCS1 extends PKCS
{
/**
* Default encryption algorithm
*
* @var string
* @access private
*/
static $defaultEncryptionAlgorithm = 'AES-128-CBC';
/**
* Sets the default encryption algorithm
*
* @access public
* @param string $algo
*/
static function setEncryptionAlgorithm($algo)
{
self::$defaultEncryptionAlgorithm = $algo;
}
/**
* Returns the mode constant corresponding to the mode string
*
* @access public
* @param string $mode
* @return int
* @throws \UnexpectedValueException if the block cipher mode is unsupported
*/
static function getEncryptionMode($mode)
{
switch ($mode) {
case 'CBC':
return BlockCipher::MODE_CBC;
case 'ECB':
return BlockCipher::MODE_ECB;
case 'CFB':
return BlockCipher::MODE_CFB;
case 'OFB':
return BlockCipher::MODE_OFB;
case 'CTR':
return BlockCipher::MODE_CTR;
}
throw new \UnexpectedValueException('Unsupported block cipher mode of operation');
}
/**
* Returns a cipher object corresponding to a string
*
* @access public
* @param string $algo
* @return string
* @throws \UnexpectedValueException if the encryption algorithm is unsupported
*/
static function getEncryptionObject($algo)
{
$modes = '(CBC|ECB|CFB|OFB|CTR)';
switch (true) {
case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
$cipher = new AES(self::getEncryptionMode($matches[2]));
$cipher->setKeyLength($matches[1]);
return $cipher;
case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
return new TripleDES(self::getEncryptionMode($matches[1]));
case preg_match("#^DES-$modes$#", $algo, $matches):
return new DES(self::getEncryptionMode($matches[1]));
default:
throw new \UnexpectedValueException('Unsupported encryption algorithmn');
}
}
/**
* Generate a symmetric key for PKCS#1 keys
*
* @access public
* @param string $password
* @param string $iv
* @param int $length
* @return string
*/
static function generateSymmetricKey($password, $iv, $length)
{
$symkey = '';
$iv = substr($iv, 0, 8);
while (strlen($symkey) < $length) {
$symkey.= md5($symkey . $password . $iv, true);
}
return substr($symkey, 0, $length);
}
/**
* Break a public or private key down into its constituent components
*
* @access public
* @param string $key
* @param string $password optional
* @return array
*/
static function load($key, $password)
{
if (!is_string($key)) {
return false;
}
/* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
"outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
implementation are part of the standard, as well.
* OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
$iv = Hex::decode(trim($matches[2]));
// remove the Proc-Type / DEK-Info sections as they're no longer needed
$key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
$ciphertext = ASN1::extractBER($key);
if ($ciphertext === false) {
$ciphertext = $key;
}
$crypto = self::getEncryptionObject($matches[1]);
$crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
$crypto->setIV($iv);
$key = $crypto->decrypt($ciphertext);
} else {
if (self::$format != self::MODE_DER) {
$decoded = ASN1::extractBER($key);
if ($decoded !== false) {
$key = $decoded;
} elseif (self::$format == self::MODE_PEM) {
return false;
}
}
}
return $key;
}
/**
* Wrap a private key appropriately
*
* @access public
* @param string $key
* @param string $type
* @param string $password
* @return string
*/
static function wrapPrivateKey($key, $type, $password)
{
if (empty($password) || !is_string($password)) {
return "-----BEGIN $type PRIVATE KEY-----\r\n" .
chunk_split(Base64::encode($key), 64) .
"-----END $type PRIVATE KEY-----";
}
$cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm);
$iv = Random::string($cipher->getBlockLength() >> 3);
$cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
$cipher->setIV($iv);
$iv = strtoupper(Hex::encode($iv));
return "-----BEGIN $type PRIVATE KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n" .
"DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" .
"\r\n" .
chunk_split(Base64::encode($cipher->encrypt($key)), 64) .
"-----END $type PRIVATE KEY-----";
}
/**
* Wrap a public key appropriately
*
* @access public
* @param string $key
* @param string $type
* @return string
*/
static function wrapPublicKey($key, $type)
{
return "-----BEGIN $type PUBLIC KEY-----\r\n" .
chunk_split(Base64::encode($key), 64) .
"-----END $type PUBLIC KEY-----";
}
}

View File

@ -0,0 +1,713 @@
<?php
/**
* PKCS#8 Formatted Key Handler
*
* PHP version 5
*
* Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----BEGIN PUBLIC KEY-----
*
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
* is specific to private keys it's basically creating a DER-encoded wrapper
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
*
* @category Crypt
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\Common;
use ParagonIE\ConstantTime\Base64;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\RC2;
use phpseclib\Crypt\RC4;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\TripleDES;
use phpseclib\Crypt\Common\BlockCipher;
use phpseclib\Crypt\Random;
use phpseclib\Math\BigInteger;
use phpseclib\File\ASN1;
use phpseclib\Exception\UnsupportedAlgorithmException;
// version is the syntax version number, for compatibility with
// future revisions of this document. It shall be 0 for this version
// of the document.
define(__NAMESPACE__ . '\Version', [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['v1']
]);
// we can replace this later once the X509 definitions are rewritten
define(__NAMESPACE__ . '\AlgorithmIdentifier', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'algorithm' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
'parameters' => [
'type' => ASN1::TYPE_ANY,
'optional' => true
]
]
]);
define(__NAMESPACE__ . '\PrivateKey', ['type' => ASN1::TYPE_OCTET_STRING]);
// we can replace this later once the X509 definitions are rewritten
define(__NAMESPACE__ . '\AttributeType', ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]);
// we can replace this later once the X509 definitions are rewritten
define(__NAMESPACE__ . '\AttributeValue', ['type' => ASN1::TYPE_ANY]);
// we can replace this later once the X509 definitions are rewritten
define(__NAMESPACE__ . '\Attribute', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'type' => AttributeType,
'value'=> [
'type' => ASN1::TYPE_SET,
'min' => 1,
'max' => -1,
'children' => AttributeValue
]
]
]);
define(__NAMESPACE__ . '\Attributes', [
'type' => ASN1::TYPE_SET,
'children' => Attribute
]);
define(__NAMESPACE__ . '\PrivateKeyInfo', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => Version,
'privateKeyAlgorithm'=> AlgorithmIdentifier,
'privateKey' => PrivateKey,
'attributes' => [
'constant' => 0,
'optional' => true,
'implicit' => true
] + Attributes
]
]);
define(__NAMESPACE__ . '\EncryptedData', ['type' => ASN1::TYPE_OCTET_STRING]);
define(__NAMESPACE__ . '\EncryptedPrivateKeyInfo', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'encryptionAlgorithm' => AlgorithmIdentifier,
'encryptedData' => EncryptedData
]
]);
// this format is not formally defined anywhere but is none-the-less the form you
// get when you do "openssl rsa -in private.pem -outform PEM -pubout"
define(__NAMESPACE__ . '\PublicKeyInfo', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'publicKeyAlgorithm'=> AlgorithmIdentifier,
'publicKey' => ['type' => ASN1::TYPE_BIT_STRING]
]
]);
// from https://tools.ietf.org/html/rfc2898#appendix-A.3
define(__NAMESPACE__ . '\PBEParameter', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'salt' => ['type' => ASN1::TYPE_OCTET_STRING],
'iterationCount' => ['type' => ASN1::TYPE_INTEGER]
]
]);
define(__NAMESPACE__ . '\PBES2params', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'keyDerivationFunc'=> AlgorithmIdentifier,
'encryptionScheme' => AlgorithmIdentifier
]
]);
define(__NAMESPACE__ . '\PBMAC1params', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'keyDerivationFunc'=> AlgorithmIdentifier,
'messageAuthScheme'=> AlgorithmIdentifier
]
]);
define(__NAMESPACE__ . '\RC2CBCParameter', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'rc2ParametersVersion'=> [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
],
'iv'=> ['type' => ASN1::TYPE_OCTET_STRING]
]
]);
define(__NAMESPACE__ . '\PBKDF2params', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
// technically, this is a CHOICE in RFC2898 but the other "choice" is, currently, more of a placeholder
// in the RFC
'salt'=> ['type' => ASN1::TYPE_OCTET_STRING],
'iterationCount'=> ['type' => ASN1::TYPE_INTEGER],
'keyLength' => [
'type' => ASN1::TYPE_INTEGER,
'optional' => true
],
'prf' => AlgorithmIdentifier + ['optional' => true]
]
]);
// from https://tools.ietf.org/html/rfc2898
define(__NAMESPACE__ . '\oids', [
// PBES1 encryption schemes
'1.2.840.113549.1.5.1' => 'pbeWithMD2AndDES-CBC',
'1.2.840.113549.1.5.4' => 'pbeWithMD2AndRC2-CBC',
'1.2.840.113549.1.5.3' => 'pbeWithMD5AndDES-CBC',
'1.2.840.113549.1.5.6' => 'pbeWithMD5AndRC2-CBC',
'1.2.840.113549.1.5.10'=> 'pbeWithSHA1AndDES-CBC',
'1.2.840.113549.1.5.11'=> 'pbeWithSHA1AndRC2-CBC',
// from PKCS#12:
// https://tools.ietf.org/html/rfc7292
'1.2.840.113549.1.12.1.1' => 'pbeWithSHAAnd128BitRC4',
'1.2.840.113549.1.12.1.2' => 'pbeWithSHAAnd40BitRC4',
'1.2.840.113549.1.12.1.3' => 'pbeWithSHAAnd3-KeyTripleDES-CBC',
'1.2.840.113549.1.12.1.4' => 'pbeWithSHAAnd2-KeyTripleDES-CBC',
'1.2.840.113549.1.12.1.5' => 'pbeWithSHAAnd128BitRC2-CBC',
'1.2.840.113549.1.12.1.6' => 'pbeWithSHAAnd40BitRC2-CBC',
'1.2.840.113549.1.5.12' => 'id-PBKDF2',
'1.2.840.113549.1.5.13' => 'id-PBES2',
'1.2.840.113549.1.5.14' => 'id-PBMAC1',
// from PKCS#5 v2.1:
// http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf
'1.2.840.113549.2.7' => 'id-hmacWithSHA1',
'1.2.840.113549.2.8' => 'id-hmacWithSHA224',
'1.2.840.113549.2.9' => 'id-hmacWithSHA256',
'1.2.840.113549.2.10'=> 'id-hmacWithSHA384',
'1.2.840.113549.2.11'=> 'id-hmacWithSHA512',
'1.2.840.113549.2.12'=> 'id-hmacWithSHA512-224',
'1.2.840.113549.2.13'=> 'id-hmacWithSHA512-256',
'1.3.14.3.2.7' => 'desCBC',
'1.2.840.113549.3.7' => 'des-EDE3-CBC',
'1.2.840.113549.3.2' => 'rc2CBC',
'1.2.840.113549.3.9' => 'rc5-CBC-PAD',
'2.16.840.1.101.3.4.1.2' => 'aes128-CBC-PAD',
'2.16.840.1.101.3.4.1.22'=> 'aes192-CBC-PAD',
'2.16.840.1.101.3.4.1.42'=> 'aes256-CBC-PAD'
]);
/**
* PKCS#8 Formatted Key Handler
*
* @package Common
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PKCS8 extends PKCS
{
/**
* Default encryption algorithm
*
* @var string
* @access private
*/
static $defaultEncryptionAlgorithm = 'id-PBES2';
/**
* Default encryption scheme
*
* Only used when defaultEncryptionAlgorithm is id-PBES2
*
* @var string
* @access private
*/
static $defaultEncryptionScheme = 'aes128-CBC-PAD';
/**
* Default PRF
*
* Only used when defaultEncryptionAlgorithm is id-PBES2
*
* @var string
* @access private
*/
static $defaultPRF = 'id-hmacWithSHA256';
/**
* Default Iteration Count
*
* @var int
* @access private
*/
static $defaultIterationCount = 2048;
/**
* Sets the default encryption algorithm
*
* @access public
* @param string $algo
*/
static function setEncryptionAlgorithm($algo)
{
self::$defaultEncryptionAlgorithm = $algo;
}
/**
* Sets the default encryption algorithm for PBES2
*
* @access public
* @param string $algo
*/
static function setEncryptionScheme($algo)
{
self::$defaultEncryptionScheme = $algo;
}
/**
* Sets the iteration count
*
* @access public
* @param int $count
*/
static function setIterationCount($count)
{
self::$defaultIterationCount = $count;
}
/**
* Sets the PRF for PBES2
*
* @access public
* @param string $algo
*/
static function setPRF($algo)
{
self::$defaultPRF = $algo;
}
/**
* Returns a SymmetricKey object based on a PBES1 $algo
*
* @access public
* @param string $algo
*/
static function getPBES1EncryptionObject($algo)
{
$algo = preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo, $matches) ?
$matches[1] :
substr($algo, 13); // strlen('pbeWithSHAAnd') == 13
switch ($algo) {
case 'DES':
$cipher = new DES(BlockCipher::MODE_CBC);
break;
case 'RC2':
$cipher = new RC2(BlockCipher::MODE_CBC);
break;
case '3-KeyTripleDES':
$cipher = new TripleDES(BlockCipher::MODE_CBC);
break;
case '2-KeyTripleDES':
$cipher = new TripleDES(BlockCipher::MODE_CBC);
$cipher->setKeyLength(128);
break;
case '128BitRC2':
$cipher = new RC2(BlockCipher::MODE_CBC);
$cipher->setKeyLength(128);
break;
case '40BitRC2':
$cipher = new RC2(BlockCipher::MODE_CBC);
$cipher->setKeyLength(40);
break;
case '128BitRC4':
$cipher = new RC4();
$cipher->setKeyLength(128);
break;
case '40BitRC4':
$cipher = new RC4();
$cipher->setKeyLength(40);
break;
default:
throw new UnsupportedAlgorithmException("$algo is not a supported algorithm");
}
return $cipher;
}
/**
* Returns a hash based on a PBES1 $algo
*
* @access public
* @param string $algo
*/
static function getPBES1Hash($algo)
{
if (preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#', $algo, $matches)) {
return $matches[1] == 'SHA' ? 'sha1' : $matches[1];
}
return 'sha1';
}
/**
* Returns a KDF baesd on a PBES1 $algo
*
* @access public
* @param string $algo
*/
static function getPBES1KDF($algo)
{
switch ($algo) {
case 'pbeWithMD2AndDES-CBC':
case 'pbeWithMD2AndRC2-CBC':
case 'pbeWithMD5AndDES-CBC':
case 'pbeWithMD5AndRC2-CBC':
case 'pbeWithSHA1AndDES-CBC':
case 'pbeWithSHA1AndRC2-CBC':
return 'pbkdf1';
}
return 'pkcs12';
}
/**
* Returns a SymmetricKey object baesd on a PBES2 $algo
*
* @access public
* @param string $algo
*/
static function getPBES2EncryptionObject($algo)
{
switch ($algo) {
case 'desCBC':
$cipher = new TripleDES(BlockCipher::MODE_CBC);
break;
case 'des-EDE3-CBC':
$cipher = new TripleDES(BlockCipher::MODE_CBC);
break;
case 'rc2CBC':
$cipher = new RC2(BlockCipher::MODE_CBC);
// in theory this can be changed
$cipher->setKeyLength(128);
break;
case 'rc5-CBC-PAD':
throw new UnsupportedAlgorithmException('rc5-CBC-PAD is not supported for PBES2 PKCS#8 keys');
case 'aes128-CBC-PAD':
case 'aes192-CBC-PAD':
case 'aes256-CBC-PAD':
$cipher = new AES(BlockCipher::MODE_CBC);
$cipher->setKeyLength(substr($algo, 3, 3));
break;
default:
throw new UnsupportedAlgorithmException("$algo is not supported");
}
return $cipher;
}
/**
* Break a public or private key down into its constituent components
*
* @access public
* @param string $key
* @param string $password optional
* @return array
*/
static function load($key, $password = '')
{
if (!is_string($key)) {
return false;
}
if (self::$format != self::MODE_DER) {
$decoded = ASN1::extractBER($key);
if ($decoded !== false) {
$key = $decoded;
} elseif (self::$format == self::MODE_PEM) {
return false;
}
}
$asn1 = new ASN1();
$decoded = $asn1->decodeBER($key);
if (empty($decoded)) {
return false;
}
$meta = [];
$asn1->loadOIDs(oids);
$decrypted = $asn1->asn1map($decoded[0], EncryptedPrivateKeyInfo);
if (strlen($password) && is_array($decrypted)) {
$algorithm = $decrypted['encryptionAlgorithm']['algorithm'];
switch ($algorithm) {
// PBES1
case 'pbeWithMD2AndDES-CBC':
case 'pbeWithMD2AndRC2-CBC':
case 'pbeWithMD5AndDES-CBC':
case 'pbeWithMD5AndRC2-CBC':
case 'pbeWithSHA1AndDES-CBC':
case 'pbeWithSHA1AndRC2-CBC':
case 'pbeWithSHAAnd3-KeyTripleDES-CBC':
case 'pbeWithSHAAnd2-KeyTripleDES-CBC':
case 'pbeWithSHAAnd128BitRC2-CBC':
case 'pbeWithSHAAnd40BitRC2-CBC':
case 'pbeWithSHAAnd128BitRC4':
case 'pbeWithSHAAnd40BitRC4':
$cipher = self::getPBES1EncryptionObject($algorithm);
$hash = self::getPBES1Hash($algorithm);
$kdf = self::getPBES1KDF($algorithm);
$meta['meta']['algorithm'] = $algorithm;
$temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
extract($asn1->asn1map($temp[0], PBEParameter));
$iterationCount = (int) $iterationCount->toString();
$cipher->setPassword($password, $kdf, $hash, Base64::decode($salt), $iterationCount);
$key = $cipher->decrypt(Base64::decode($decrypted['encryptedData']));
$decoded = $asn1->decodeBER($key);
if (empty($decoded)) {
return false;
}
break;
case 'id-PBES2':
$meta['meta']['algorithm'] = $algorithm;
$temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
$temp = $asn1->asn1map($temp[0], PBES2params);
extract($temp);
$cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']);
$meta['meta']['cipher'] = $encryptionScheme['algorithm'];
$temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
$temp = $asn1->asn1map($temp[0], PBES2params);
extract($temp);
if (!$cipher instanceof RC2) {
$cipher->setIV(Base64::decode($encryptionScheme['parameters']['octetString']));
} else {
$temp = $asn1->decodeBER($encryptionScheme['parameters']);
extract($asn1->asn1map($temp[0], RC2CBCParameter));
$effectiveKeyLength = (int) $rc2ParametersVersion->toString();
switch ($effectiveKeyLength) {
case 160:
$effectiveKeyLength = 40;
break;
case 120:
$effectiveKeyLength = 64;
break;
case 58:
$effectiveKeyLength = 128;
break;
//default: // should be >= 256
}
$cipher->setIV(Base64::decode($iv));
$cipher->setKeyLength($effectiveKeyLength);
}
$meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm'];
switch ($keyDerivationFunc['algorithm']) {
case 'id-PBKDF2':
$temp = $asn1->decodeBER($keyDerivationFunc['parameters']);
$prf = ['algorithm' => 'id-hmacWithSHA1'];
$params = $asn1->asn1map($temp[0], PBKDF2params);
extract($params);
$meta['meta']['prf'] = $prf['algorithm'];
$hash = str_replace('-', '/', substr($prf['algorithm'], 11));
$params = [
$password,
'pbkdf2',
$hash,
Base64::decode($salt),
(int) $iterationCount->toString()
];
if (isset($keyLength)) {
$params[] = (int) $keyLength->toString();
}
call_user_func_array([$cipher, 'setPassword'], $params);
$key = $cipher->decrypt(Base64::decode($decrypted['encryptedData']));
$decoded = $asn1->decodeBER($key);
if (empty($decoded)) {
return false;
}
break;
default:
throw new UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys');
}
break;
case 'id-PBMAC1':
//$temp = $asn1->decodeBER($decrypted['encryptionAlgorithm']['parameters']);
//$value = $asn1->asn1map($temp[0], PBMAC1params);
// since i can't find any implementation that does PBMAC1 it is unsupported
throw new UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.');
// at this point we'll assume that the key conforms to PublicKeyInfo
}
}
$private = $asn1->asn1map($decoded[0], PrivateKeyInfo);
if (is_array($private)) {
return $private + $meta;
}
// EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference
// is that the former has an octet string and the later has a bit string. the first byte of a bit
// string represents the number of bits in the last byte that are to be ignored but, currently,
// bit strings wanting a non-zero amount of bits trimmed are not supported
$public = $asn1->asn1map($decoded[0], PublicKeyInfo);
if (is_array($public)) {
$public['publicKey'] = base64_decode($public['publicKey']);
if ($public['publicKey'][0] != "\0") {
return false;
}
$public['publicKey'] = base64_encode(substr($public['publicKey'], 1));
return $public;
}
return false;
}
/**
* Wrap a private key appropriately
*
* @access public
* @param string $algorithm
* @param string $key
* @param string $attr
* @param string $password
* @return string
*/
static function wrapPrivateKey($key, $algorithm, $attr, $password)
{
$asn1 = new ASN1();
$asn1->loadOIDs(oids);
$key = [
'version' => 'v1',
'privateKeyAlgorithm' => ['algorithm' => $algorithm], // parameters are not currently supported
'privateKey' => Base64::encode($key)
];
if (!empty($attr)) {
$key['attributes'] = $attr;
}
$key = $asn1->encodeDER($key, PrivateKeyInfo);
if (!empty($password) && is_string($password)) {
$salt = Random::string(8);
$iterationCount = self::$defaultIterationCount;
if (self::$defaultEncryptionAlgorithm == 'id-PBES2') {
$crypto = self::getPBES2EncryptionObject(self::$defaultEncryptionScheme);
$hash = str_replace('-', '/', substr(self::$defaultPRF, 11));
$kdf = 'pbkdf2';
$iv = Random::string($crypto->getBlockLength() >> 3);
$PBKDF2params = [
'salt' => Base64::encode($salt),
'iterationCount' => $iterationCount,
'prf' => ['algorithm' => self::$defaultPRF, 'parameters' => null]
];
$PBKDF2params = $asn1->encodeDER($PBKDF2params, PBKDF2params);
if (!$crypto instanceof RC2) {
$params = ['octetString' => Base64::encode($iv)];
} else {
$params = [
'rc2ParametersVersion' => 58,
'iv' => Base64::encode($iv)
];
$params = $asn1->encodeDER($params, RC2CBCParameter);
$params = new ASN1\Element($params);
}
$params = [
'keyDerivationFunc' => [
'algorithm' => 'id-PBKDF2',
'parameters' => new ASN1\Element($PBKDF2params)
],
'encryptionScheme' => [
'algorithm' => self::$defaultEncryptionScheme,
'parameters' => $params
]
];
$params = $asn1->encodeDER($params, PBES2params);
$crypto->setIV($iv);
} else {
$crypto = self::getPBES1EncryptionObject(self::$defaultEncryptionAlgorithm);
$hash = self::getPBES1Hash(self::$defaultEncryptionAlgorithm);
$kdf = self::getPBES1KDF(self::$defaultEncryptionAlgorithm);
$params = [
'salt' => Base64::encode($salt),
'iterationCount' => $iterationCount
];
$params = $asn1->encodeDER($params, PBEParameter);
}
$crypto->setPassword($password, $kdf, $hash, $salt, $iterationCount);
$key = $crypto->encrypt($key);
$key = [
'encryptionAlgorithm' => [
'algorithm' => self::$defaultEncryptionAlgorithm,
'parameters' => new ASN1\Element($params)
],
'encryptedData' => Base64::encode($key)
];
$key = $asn1->encodeDER($key, EncryptedPrivateKeyInfo);
return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
chunk_split(Base64::encode($key), 64) .
"-----END ENCRYPTED PRIVATE KEY-----";
}
return "-----BEGIN PRIVATE KEY-----\r\n" .
chunk_split(Base64::encode($key), 64) .
"-----END PRIVATE KEY-----";
}
/**
* Wrap a public key appropriately
*
* @access public
* @param string $key
* @return string
*/
static function wrapPublicKey($key, $algorithm)
{
$asn1 = new ASN1();
$key = [
'publicKeyAlgorithm' => [
'algorithm' => $algorithm,
'parameters' => null // parameters are not currently supported
],
'publicKey' => Base64::encode("\0" . $key)
];
$key = $asn1->encodeDER($key, PublicKeyInfo);
return "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(Base64::encode($key), 64) .
"-----END PUBLIC KEY-----";
}
}

View File

@ -38,6 +38,7 @@ namespace phpseclib\Crypt\Common;
use phpseclib\Crypt\Hash;
use phpseclib\Common\Functions\Strings;
use phpseclib\Math\BigInteger;
/**
* Base Class for all \phpseclib\Crypt\* cipher classes
@ -512,7 +513,7 @@ abstract class SymmetricKey
throw new \InvalidArgumentException('This mode does not require an IV.');
}
if ($this->mode == self::MODE_STREAM && $this->usesIV()) {
if (!$this->usesIV()) {
throw new \InvalidArgumentException('This algorithm does not use an IV.');
}
@ -622,12 +623,17 @@ abstract class SymmetricKey
{
$key = '';
$method = strtolower($method);
switch ($method) {
default: // 'pbkdf2' or 'pbkdf1'
case 'pkcs12': // from https://tools.ietf.org/html/rfc7292#appendix-B.2
case 'pbkdf1':
case 'pbkdf2':
$func_args = func_get_args();
// Hash function
$hash = isset($func_args[2]) ? $func_args[2] : 'sha1';
$hash = isset($func_args[2]) ? strtolower($func_args[2]) : 'sha1';
$hashObj = new Hash();
$hashObj->setHash($hash);
// WPA and WPA2 use the SSID as the salt
$salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt;
@ -645,10 +651,63 @@ abstract class SymmetricKey
}
switch (true) {
case $method == 'pkcs12':
/*
In this specification, however, all passwords are created from
BMPStrings with a NULL terminator. This means that each character in
the original BMPString is encoded in 2 bytes in big-endian format
(most-significant byte first). There are no Unicode byte order
marks. The 2 bytes produced from the last character in the BMPString
are followed by 2 additional bytes with the value 0x00.
-- https://tools.ietf.org/html/rfc7292#appendix-B.1
*/
$password = "\0". chunk_split($password, 1, "\0") . "\0";
/*
This standard specifies 3 different values for the ID byte mentioned
above:
1. If ID=1, then the pseudorandom bits being produced are to be used
as key material for performing encryption or decryption.
2. If ID=2, then the pseudorandom bits being produced are to be used
as an IV (Initial Value) for encryption or decryption.
3. If ID=3, then the pseudorandom bits being produced are to be used
as an integrity key for MACing.
*/
// Construct a string, D (the "diversifier"), by concatenating v/8
// copies of ID.
$blockLength = $hashObj->getBlockLengthInBytes();
$d1 = str_repeat(chr(1), $blockLength);
$d2 = str_repeat(chr(2), $blockLength);
$s = '';
if (strlen($salt)) {
while (strlen($s) < $blockLength) {
$s.= $salt;
}
}
$s = substr($s, 0, $blockLength);
$p = '';
if (strlen($password)) {
while (strlen($p) < $blockLength) {
$p.= $password;
}
}
$p = substr($p, 0, $blockLength);
$i = $s . $p;
$this->setKey(self::_pkcs12helper($dkLen, $hashObj, $i, $d1, $count));
if ($this->usesIV()) {
$this->setIV(self::_pkcs12helper($this->block_size, $hashObj, $i, $d2, $count));
}
return true;
case $method == 'pbkdf1':
$hashObj = new Hash();
$hashObj->setHash($hash);
if ($dkLen > $hashObj->getLength()) {
if ($dkLen > $hashObj->getLengthInBytes()) {
throw new \LengthException('Derived key length cannot be longer than the hash length');
}
$t = $password . $salt;
@ -658,21 +717,20 @@ abstract class SymmetricKey
$key = substr($t, 0, $dkLen);
$this->setKey(substr($key, 0, $dkLen >> 1));
$this->setIV(substr($key, $dkLen >> 1));
if ($this->usesIV()) {
$this->setIV(substr($key, $dkLen >> 1));
}
return true;
// Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable
case !function_exists('hash_pbkdf2'):
case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
$hashObj->setKey($password);
while (strlen($key) < $dkLen) {
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
$f = $u = $hmac->hash($salt . pack('N', $i++));
$f = $u = $hashObj->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u);
$u = $hashObj->hash($u);
$f^= $u;
}
$key.= $f;
@ -682,6 +740,9 @@ abstract class SymmetricKey
default:
$key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true);
}
break;
default:
throw new \InvalidArgumentException($method . ' is not a supported password hashing method');
}
$this->setKey($key);
@ -689,6 +750,60 @@ abstract class SymmetricKey
return true;
}
/**
* PKCS#12 KDF Helper Function
*
* As discussed here:
*
* {@link https://tools.ietf.org/html/rfc7292#appendix-B}
*
* @see self::setPassword()
* @access private
* @param int $n
* @param \phpseclib\Crypt\Hash $hashObj
* @param string $i
* @param string $d
* @param int $count
* @return string $a
*/
static function _pkcs12helper($n, $hashObj, $i, $d, $count)
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
$blockLength = $hashObj->getBlockLength() >> 3;
$c = ceil($n / $hashObj->getLengthInBytes());
$a = '';
for ($j = 1; $j <= $c; $j++) {
$ai = $d . $i;
for ($k = 0; $k < $count; $k++) {
$ai = $hashObj->hash($ai);
}
$b = '';
while (strlen($b) < $blockLength) {
$b.= $ai;
}
$b = substr($b, 0, $blockLength);
$b = new BigInteger($b, 256);
$newi = '';
for ($k = 0; $k < strlen($i); $k+= $blockLength) {
$temp = substr($i, $k, $blockLength);
$temp = new BigInteger($temp, 256);
$temp->setPrecision($blockLength << 3);
$temp = $temp->add($b);
$temp = $temp->add($one);
$newi.= $temp->toBytes(false);
}
$i = $newi;
$a.= $ai;
}
return substr($a, 0, $n);
}
/**
* Encrypts a message.
*

View File

@ -203,6 +203,23 @@ class Hash
);
}
switch ($hash) {
case 'md2-96':
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->blockSize = 512;
break;
default:
$this->blockSize = 1024;
}
if ($hash == 'sha512/224' || $hash == 'sha512/256') {
// from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24
$this->initial = $hash == 'sha512/256' ?
@ -264,16 +281,49 @@ class Hash
}
/**
* Returns the hash length (in bytes)
* Returns the hash length (in bits)
*
* @access public
* @return int
*/
function getLength()
{
return $this->length << 3;
}
/**
* Returns the hash length (in bytes)
*
* @access public
* @return int
*/
function getLengthInBytes()
{
return $this->length;
}
/**
* Returns the block length (in bits)
*
* @access public
* @return int
*/
function getBlockLength()
{
return $this->blockSize;
}
/**
* Returns the block length (in bytes)
*
* @access public
* @return int
*/
function getBlockLengthInBytes()
{
return $this->blockSize >> 3;
}
/**
* Pure-PHP implementation of SHA512
*

View File

@ -319,6 +319,7 @@ class RC2 extends BlockCipher
}
$this->default_key_length = $this->current_key_length = $length;
$this->explicit_key_length = $length >> 3;
}
/**
@ -340,9 +341,6 @@ class RC2 extends BlockCipher
* has more then 128 bytes in it, and set $key to a single null byte if
* it is empty.
*
* If the key is not explicitly set, it'll be assumed to be a single
* null byte.
*
* @see \phpseclib\Crypt\Common\SymmetricKey::setKey()
* @access public
* @param string $key
@ -362,8 +360,10 @@ class RC2 extends BlockCipher
}
$this->current_key_length = $t1;
// Key byte count should be 1..128.
$key = strlen($key) ? substr($key, 0, 128) : "\x00";
if (strlen($key) < 1 || strlen($key) > 128) {
throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes between 8 and 1024 bits, inclusive, are supported');
}
$t = strlen($key);
// The mcrypt RC2 implementation only supports effective key length
@ -392,7 +392,10 @@ class RC2 extends BlockCipher
$l[0] = $this->invpitable[$l[0]];
array_unshift($l, 'C*');
parent::setKey(call_user_func_array('pack', $l));
$this->key = call_user_func_array('pack', $l);
$this->key_length = strlen($this->key);
$this->changed = true;
$this->_setEngine();
}
/**

View File

@ -109,7 +109,7 @@ class RC4 extends StreamCipher
* @var string
* @access private
*/
var $key = "\0";
var $key;
/**
* The Key Stream for decryption and encryption

View File

@ -118,32 +118,6 @@ class RSA
const PADDING_RELAXED_PKCS1 = 5;
/**#@-*/
/**#@+
* @access private
* @see self::createKey()
*/
/**
* ASN1 Integer
*/
const ASN1_INTEGER = 2;
/**
* ASN1 Bit String
*/
const ASN1_BITSTRING = 3;
/**
* ASN1 Octet String
*/
const ASN1_OCTETSTRING = 4;
/**
* ASN1 Object Identifier
*/
const ASN1_OBJECT = 6;
/**
* ASN1 Sequence (with the constucted bit set)
*/
const ASN1_SEQUENCE = 48;
/**#@-*/
/**#@+
* @access private
* @see self::__construct()
@ -151,13 +125,13 @@ class RSA
/**
* To use the pure-PHP implementation
*/
const MODE_INTERNAL = 1;
const ENGINE_INTERNAL = 1;
/**
* To use the OpenSSL library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
const MODE_OPENSSL = 2;
const ENGINE_OPENSSL = 2;
/**#@-*/
/**
@ -182,7 +156,7 @@ class RSA
* @var string
* @access private
*/
var $privateKeyFormat = 'PKCS1';
var $privateKeyFormat = 'PKCS8';
/**
* Public Key Format
@ -341,6 +315,39 @@ class RSA
*/
static $origFileFormats = false;
/**
* Public exponent
*
* @var int
* @link http://en.wikipedia.org/wiki/65537_%28number%29
* @access private
*/
static $defaultExponent = 65537;
/**
* Smallest Prime
*
* 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 smallestPrime
* to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if
* engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then 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)
*
* @var int
* @access private
*/
static $smallestPrime = 4096;
/**
* Engine
*
* This is only used for key generation. Valid values are RSA::ENGINE_INTERNAL and RSA::ENGINE_OPENSSL
*
* @var int
* @access private
*/
static $engine = NULL;
/**
* Initialize static variables
@ -384,10 +391,49 @@ class RSA
self::_initialize_static_variables();
$this->hash = new Hash('sha256');
$this->hLen = $this->hash->getLength();
$this->hLen = $this->hash->getLengthInBytes();
$this->hashName = 'sha256';
$this->mgfHash = new Hash('sha256');
$this->mgfHLen = $this->mgfHash->getLength();
$this->mgfHLen = $this->mgfHash->getLengthInBytes();
}
/**
* Sets the public exponent
*
* This will be 65537 unless changed.
*
* @access public
* @param int $val
*/
static function setExponent($val)
{
self::$defaultExponent = $val;
}
/**
* Sets the smallest prime number in bits
*
* This will be 4096 unless changed.
*
* @access public
* @param int $val
*/
static function setSmallestPrime($val)
{
self::$smallestPrime = $val;
}
/**
* Sets the engine
*
* Only used in the constructor. Valid values are RSA::ENGINE_OPENSSL and RSA::ENGINE_INTERNAL
*
* @access public
* @param int $val
*/
static function setEngine($val)
{
self::$engine = $val;
}
/**
@ -404,42 +450,29 @@ class RSA
* @param int $timeout
* @param array $p
*/
static function createKey($bits = 2048, $timeout = false, $partial = array())
static function createKey($bits = 2048)
{
self::_initialize_static_variables();
if (!defined('CRYPT_RSA_MODE')) {
if (!isset(self::$engine)) {
switch (true) {
// Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
// Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
// can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
self::$engine = self::ENGINE_INTERNAL;
break;
case extension_loaded('openssl') && file_exists(self::$configFile):
define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
self::$engine = self::ENGINE_OPENSSL;
break;
default:
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
self::$engine = self::ENGINE_INTERNAL;
}
}
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 self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::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 == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
if (self::$engine == self::ENGINE_OPENSSL && $bits >= 384 && self::$defaultExponent == 65537) {
$config = array();
if (isset(self::$configFile)) {
$config['config'] = self::$configFile;
@ -466,75 +499,34 @@ class RSA
static $e;
if (!isset($e)) {
$e = new BigInteger(CRYPT_RSA_EXPONENT);
$e = new BigInteger(self::$defaultExponent);
}
extract(self::_generateMinMax($bits));
$absoluteMin = $min;
$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;
$regSize = $bits >> 1; // divide by two to see how many bits P and Q would be
if ($regSize > self::$smallestPrime) {
$num_primes = floor($bits / self::$smallestPrime);
$regSize = self::$smallestPrime;
} else {
$num_primes = 2;
}
$regSize = $temp;
$finalSize = $temp + $bits % $temp;
$n = clone self::$one;
if (!empty($partial)) {
extract(unserialize($partial));
} else {
$exponents = $coefficients = $primes = array();
$lcm = array(
'top' => clone self::$one,
'bottom' => false
);
}
$start = time();
$i0 = count($primes) + 1;
$exponents = $coefficients = $primes = array();
$lcm = array(
'top' => clone self::$one,
'bottom' => false
);
do {
for ($i = $i0; $i <= $num_primes; $i++) {
if ($timeout !== false) {
$timeout-= time() - $start;
$start = time();
if ($timeout <= 0) {
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
))
);
}
}
$size = $i == $num_primes ? $finalSize : $regSize;
$primes[$i] = BigInteger::randomPrime($size, $timeout);
if ($primes[$i] === false) { // if we've reached the timeout
if (count($primes) > 1) {
$partialkey = '';
} else {
array_pop($primes);
$partialkey = serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
));
}
return array(
'privatekey' => false,
'publickey' => false,
'partialkey' => $partialkey
);
for ($i = 1; $i <= $num_primes; $i++) {
if ($i != $num_primes) {
$primes[$i] = BigInteger::randomPrime($regSize);
} else {
extract(BigInteger::minMaxBits($bits));
list($min) = $min->divide($n);
$min = $min->add(self::$one);
list($max) = $max->divide($n);
$primes[$i] = BigInteger::randomRangePrime($min, $max);
}
// the first coefficient is calculated differently from the rest
@ -551,8 +543,6 @@ class RSA
// see http://en.wikipedia.org/wiki/Euler%27s_totient_function
$lcm['top'] = $lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
$exponents[$i] = $e->modInverse($temp);
}
list($temp) = $lcm['top']->divide($lcm['bottom']);
@ -560,9 +550,14 @@ class RSA
$i0 = 1;
} while (!$gcd->equals(self::$one));
$coefficients[2] = $primes[2]->modInverse($primes[1]);
$d = $e->modInverse($temp);
$coefficients[2] = $primes[2]->modInverse($primes[1]);
foreach ($primes as $i => $prime) {
$temp = $prime->subtract(self::$one);
$exponents[$i] = $e->modInverse($temp);
}
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
// RSAPrivateKey ::= SEQUENCE {
@ -594,8 +589,7 @@ class RSA
return array(
'privatekey' => $privatekey,
'publickey' => $publickey,
'partialkey' => false
'publickey' => $publickey
);
}
@ -705,11 +699,7 @@ class RSA
$format = strtolower($type);
if (isset(self::$fileFormats[$format])) {
$format = self::$fileFormats[$format];
try {
$components = $format::load($key, $this->password);
} catch (\Exception $e) {
$components = false;
}
$components = $format::load($key, $this->password);
}
}
@ -721,7 +711,7 @@ class RSA
$this->format = $format;
$this->modulus = $components['modulus'];
$this->k = strlen($this->modulus->toBytes());
$this->k = $this->modulus->getLengthInBytes();
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
if (isset($components['primes'])) {
$this->primes = $components['primes'];
@ -788,6 +778,34 @@ class RSA
}
return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password);
/*
$key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password);
if ($key !== false || count($this->primes) == 2) {
return $key;
}
$nSize = $this->getSize() >> 1;
$primes = [1 => clone self::$one, clone self::$one];
$i = 1;
foreach ($this->primes as $prime) {
$primes[$i] = $primes[$i]->multiply($prime);
if ($primes[$i]->getLength() >= $nSize) {
$i++;
}
}
$exponents = [];
$coefficients = [2 => $primes[2]->modInverse($primes[1])];
foreach ($primes as $i => $prime) {
$temp = $prime->subtract(self::$one);
$exponents[$i] = $this->modulus->modInverse($temp);
}
return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password);
*/
}
/**
@ -798,9 +816,9 @@ class RSA
* @access public
* @return int
*/
function getSize()
function getLength()
{
return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
return !isset($this->modulus) ? 0 : $this->modulus->getLength();
}
/**
@ -1038,12 +1056,16 @@ class RSA
*/
function __toString()
{
$key = $this->getPrivateKey($this->privateKeyFormat);
if (is_string($key)) {
return $key;
try {
$key = $this->getPrivateKey($this->privateKeyFormat);
if (is_string($key)) {
return $key;
}
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
return is_string($key) ? $key : '';
} catch (\Exception $e) {
return '';
}
$key = $this->_getPrivatePublicKey($this->publicKeyFormat);
return is_string($key) ? $key : '';
}
/**
@ -1112,7 +1134,7 @@ class RSA
$this->hash = new Hash('sha256');
$this->hashName = 'sha256';
}
$this->hLen = $this->hash->getLength();
$this->hLen = $this->hash->getLengthInBytes();
}
/**
@ -1142,7 +1164,7 @@ class RSA
default:
$this->mgfHash = new Hash('sha256');
}
$this->mgfHLen = $this->mgfHash->getLength();
$this->mgfHLen = $this->mgfHash->getLengthInBytes();
}
/**
@ -1316,7 +1338,7 @@ class RSA
* @param string $y
* @return bool
*/
function _equals($x, $y)
static function _equals($x, $y)
{
if (strlen($x) != strlen($y)) {
return false;
@ -1528,7 +1550,7 @@ class RSA
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
if ($lHash != $lHash2) {
if (!self::_equals($lHash, $lHash2)) {
return false;
}
$m = ltrim($m, chr(0));
@ -1746,7 +1768,7 @@ class RSA
$salt = substr($db, $temp + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return $this->_equals($h, $h2);
return self::_equals($h, $h2);
}
/**
@ -1940,7 +1962,7 @@ class RSA
}
// Compare
return $this->_equals($em, $em2);
return self::_equals($em, $em2);
}
/**
@ -2044,7 +2066,7 @@ class RSA
$em = $hash->hash($m);
$em2 = Base64::decode($decoded['digest']);
return $this->_equals($em, $em2);
return self::_equals($em, $em2);
}
/**

View File

@ -1,433 +0,0 @@
<?php
/**
* PKCS Formatted RSA Key Handler
*
* PHP version 5
*
* @category Crypt
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Crypt\RSA;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\Common\BlockCipher;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Base;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\TripleDES;
use phpseclib\Math\BigInteger;
use phpseclib\Common\Functions\Strings;
use phpseclib\Common\Functions\ASN1;
/**
* PKCS Formatted RSA Key Handler
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
abstract class PKCS
{
/**#@+
* @access private
* @see \phpseclib\Crypt\RSA::createKey()
*/
/**
* ASN1 Integer
*/
const ASN1_INTEGER = 2;
/**
* ASN1 Bit String
*/
const ASN1_BITSTRING = 3;
/**
* ASN1 Octet String
*/
const ASN1_OCTETSTRING = 4;
/**
* ASN1 Object Identifier
*/
const ASN1_OBJECT = 6;
/**
* ASN1 Sequence (with the constucted bit set)
*/
const ASN1_SEQUENCE = 48;
/**#@-*/
/**#@+
* @access private
*/
/**
* Auto-detect the format
*/
const MODE_ANY = 0;
/**
* Require base64-encoded PEM's be supplied
*/
const MODE_PEM = 1;
/**
* Require raw DER's be supplied
*/
const MODE_DER = 2;
/**#@-*/
/**
* Is the key a base-64 encoded PEM, DER or should it be auto-detected?
*
* @access private
* @param int
*/
static $format = self::MODE_ANY;
/**
* Returns the mode constant corresponding to the mode string
*
* @access public
* @param string $mode
* @return int
* @throws \UnexpectedValueException if the block cipher mode is unsupported
*/
static function getEncryptionMode($mode)
{
switch ($mode) {
case 'CBC':
return BlockCipher::MODE_CBC;
case 'ECB':
return BlockCipher::MODE_ECB;
case 'CFB':
return BlockCipher::MODE_CFB;
case 'OFB':
return BlockCipher::MODE_OFB;
case 'CTR':
return BlockCipher::MODE_CTR;
}
throw new \UnexpectedValueException('Unsupported block cipher mode of operation');
}
/**
* Returns a cipher object corresponding to a string
*
* @access public
* @param string $algo
* @return string
* @throws \UnexpectedValueException if the encryption algorithm is unsupported
*/
static function getEncryptionObject($algo)
{
$modes = '(CBC|ECB|CFB|OFB|CTR)';
switch (true) {
case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
$cipher = new AES(self::getEncryptionMode($matches[2]));
$cipher->setKeyLength($matches[1]);
return $cipher;
case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
return new TripleDES(self::getEncryptionMode($matches[1]));
case preg_match("#^DES-$modes$#", $algo, $matches):
return new DES(self::getEncryptionMode($matches[1]));
default:
throw new \UnexpectedValueException('Unsupported encryption algorithmn');
}
}
/**
* Generate a symmetric key for PKCS#1 keys
*
* @access public
* @param string $password
* @param string $iv
* @param int $length
* @return string
*/
static function generateSymmetricKey($password, $iv, $length)
{
$symkey = '';
$iv = substr($iv, 0, 8);
while (strlen($symkey) < $length) {
$symkey.= md5($symkey . $password . $iv, true);
}
return substr($symkey, 0, $length);
}
/**
* Break a public or private key down into its constituent components
*
* @access public
* @param string $key
* @param string $password optional
* @return array
*/
static function load($key, $password = '')
{
if (!is_string($key)) {
return false;
}
$components = array('isPublicKey' => strpos($key, 'PUBLIC') !== false);
/* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
"outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
implementation are part of the standard, as well.
* OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
$iv = Hex::decode(trim($matches[2]));
// remove the Proc-Type / DEK-Info sections as they're no longer needed
$key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
$ciphertext = self::_extractBER($key);
if ($ciphertext === false) {
$ciphertext = $key;
}
$crypto = self::getEncryptionObject($matches[1]);
$crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
$crypto->setIV($iv);
$key = $crypto->decrypt($ciphertext);
if ($key === false) {
return false;
}
} else {
if (self::$format != self::MODE_DER) {
$decoded = self::_extractBER($key);
if ($decoded !== false) {
$key = $decoded;
} elseif (self::$format == self::MODE_PEM) {
return false;
}
}
}
if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
if (ASN1::decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord(Strings::shift($key));
/* intended for keys for which OpenSSL's asn1parse returns the following:
0:d=0 hl=4 l= 631 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=2 l= 13 cons: SEQUENCE
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
20:d=2 hl=2 l= 0 prim: NULL
22:d=1 hl=4 l= 609 prim: OCTET STRING
ie. PKCS8 keys */
if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
Strings::shift($key, 3);
$tag = self::ASN1_SEQUENCE;
}
if ($tag == self::ASN1_SEQUENCE) {
$temp = Strings::shift($key, ASN1::decodeLength($key));
if (ord(Strings::shift($temp)) != self::ASN1_OBJECT) {
return false;
}
$length = ASN1::decodeLength($temp);
switch (Strings::shift($temp, $length)) {
case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
break;
case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
/*
PBEParameter ::= SEQUENCE {
salt OCTET STRING (SIZE(8)),
iterationCount INTEGER }
*/
if (ord(Strings::shift($temp)) != self::ASN1_SEQUENCE) {
return false;
}
if (ASN1::decodeLength($temp) != strlen($temp)) {
return false;
}
Strings::shift($temp); // assume it's an octet string
$salt = Strings::shift($temp, ASN1::decodeLength($temp));
if (ord(Strings::shift($temp)) != self::ASN1_INTEGER) {
return false;
}
ASN1::decodeLength($temp);
list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
Strings::shift($key); // assume it's an octet string
$length = ASN1::decodeLength($key);
if (strlen($key) != $length) {
return false;
}
$crypto = new DES(DES::MODE_CBC);
$crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount);
$key = $crypto->decrypt($key);
if ($key === false) {
return false;
}
return self::load($key);
default:
return false;
}
/* intended for keys for which OpenSSL's asn1parse returns the following:
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING */
$tag = ord(Strings::shift($key)); // skip over the BIT STRING / OCTET STRING tag
ASN1::decodeLength($key); // skip over the BIT STRING / OCTET STRING length
// "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
// unused bits in the final subsequent octet. The number shall be in the range zero to seven."
// -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
if ($tag == self::ASN1_BITSTRING) {
Strings::shift($key);
}
if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
if (ASN1::decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord(Strings::shift($key));
}
if ($tag != self::ASN1_INTEGER) {
return false;
}
$length = ASN1::decodeLength($key);
$temp = Strings::shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) {
$components['modulus'] = new BigInteger($temp, 256);
Strings::shift($key); // skip over self::ASN1_INTEGER
$length = ASN1::decodeLength($key);
$components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(Strings::shift($key, $length), 256);
return $components;
}
if (ord(Strings::shift($key)) != self::ASN1_INTEGER) {
return false;
}
$length = ASN1::decodeLength($key);
$components['modulus'] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['publicExponent'] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['privateExponent'] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['primes'] = array(1 => new BigInteger(Strings::shift($key, $length), 256));
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['primes'][] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['exponents'] = array(1 => new BigInteger(Strings::shift($key, $length), 256));
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['exponents'][] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['coefficients'] = array(2 => new BigInteger(Strings::shift($key, $length), 256));
if (!empty($key)) {
if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
ASN1::decodeLength($key);
while (!empty($key)) {
if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
return false;
}
ASN1::decodeLength($key);
$key = substr($key, 1);
$length = ASN1::decodeLength($key);
$components['primes'][] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['exponents'][] = new BigInteger(Strings::shift($key, $length), 256);
Strings::shift($key);
$length = ASN1::decodeLength($key);
$components['coefficients'][] = new BigInteger(Strings::shift($key, $length), 256);
}
}
return $components;
}
/**
* Require base64-encoded PEM's be supplied
*
* @see self::load()
* @access public
*/
static function requirePEM()
{
self::$format = self::MODE_PEM;
}
/**
* Require raw DER's be supplied
*
* @see self::load()
* @access public
*/
static function requireDER()
{
self::$format = self::MODE_DER;
}
/**
* Accept any format and auto detect the format
*
* This is the default setting
*
* @see self::load()
* @access public
*/
static function requireAny()
{
self::$format = self::MODE_ANY;
}
/**
* Extract raw BER from Base64 encoding
*
* @access private
* @param string $str
* @return string
*/
static function _extractBER($str)
{
/* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
* above and beyond the ceritificate.
* ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
*
* Bag Attributes
* localKeyID: 01 00 00 00
* subject=/O=organization/OU=org unit/CN=common name
* issuer=/O=organization/CN=common name
*/
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
// remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false;
return $temp != false ? $temp : $str;
}
}

View File

@ -24,14 +24,55 @@
namespace phpseclib\Crypt\RSA;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\Random;
use phpseclib\Crypt\TripleDES;
use phpseclib\Math\BigInteger;
use phpseclib\Common\Functions\ASN1;
use phpseclib\Crypt\Common\PKCS1 as Progenitor;
use phpseclib\File\ASN1;
// version must be multi if otherPrimeInfos present
define(__NAMESPACE__ . '\Version', [
'type' => ASN1::TYPE_INTEGER,
'mapping' => ['two-prime', 'multi']
]);
define(__NAMESPACE__ . '\OtherPrimeInfo', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'prime' => ['type' => ASN1::TYPE_INTEGER], // ri
'exponent' => ['type' => ASN1::TYPE_INTEGER], // di
'coefficient' => ['type' => ASN1::TYPE_INTEGER] // ti
]
]);
define(__NAMESPACE__ . '\OtherPrimeInfos', [
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => OtherPrimeInfo
]);
define(__NAMESPACE__ . '\RSAPrivateKey', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'version' => Version,
'modulus' => ['type' => ASN1::TYPE_INTEGER], // n
'publicExponent' => ['type' => ASN1::TYPE_INTEGER], // e
'privateExponent' => ['type' => ASN1::TYPE_INTEGER], // d
'prime1' => ['type' => ASN1::TYPE_INTEGER], // p
'prime2' => ['type' => ASN1::TYPE_INTEGER], // q
'exponent1' => ['type' => ASN1::TYPE_INTEGER], // d mod (p-1)
'exponent2' => ['type' => ASN1::TYPE_INTEGER], // d mod (q-1)
'coefficient' => ['type' => ASN1::TYPE_INTEGER], // (inverse of q) mod p
'otherPrimeInfos' => OtherPrimeInfos + ['optional' => true]
]
]);
define(__NAMESPACE__ . '\RSAPublicKey', [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'modulus' => ['type' => ASN1::TYPE_INTEGER],
'publicExponent' => ['type' => ASN1::TYPE_INTEGER]
]
]);
/**
* PKCS#1 Formatted RSA Key Handler
@ -40,25 +81,58 @@ use phpseclib\Common\Functions\ASN1;
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PKCS1 extends PKCS
class PKCS1 extends Progenitor
{
/**
* Default encryption algorithm
*
* @var string
* @access private
*/
static $defaultEncryptionAlgorithm = 'DES-EDE3-CBC';
/**
* Sets the default encryption algorithm
* Break a public or private key down into its constituent components
*
* @access public
* @param string $algo
* @param string $key
* @param string $password optional
* @return array
*/
static function setEncryptionAlgorithm($algo)
static function load($key, $password = '')
{
self::$defaultEncryptionAlgorithm = $algo;
if (!is_string($key)) {
return false;
}
$components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
$key = parent::load($key, $password);
if ($key === false) {
return false;
}
$asn1 = new ASN1();
$decoded = $asn1->decodeBER($key);
if (empty($decoded)) {
return false;
}
$key = $asn1->asn1map($decoded[0], RSAPrivateKey);
if (is_array($key)) {
$components+= [
'modulus' => $key['modulus'],
'publicExponent' => $key['publicExponent'],
'privateExponent' => $key['privateExponent'],
'primes' => [1 => $key['prime1'], $key['prime2']],
'exponents' => [1 => $key['exponent1'], $key['exponent2']],
'coefficients' => [2 => $key['coefficient']]
];
if ($key['version'] == 'multi') {
foreach ($key['otherPrimeInfos'] as $primeInfo) {
$components['primes'][] = $primeInfo['prime'];
$components['exponents'][] = $primeInfo['exponent'];
$components['coefficients'][] = $primeInfo['coefficient'];
}
}
return $components;
}
$key = $asn1->asn1map($decoded[0], RSAPublicKey);
return is_array($key) ? $components + $key : false;
}
/**
@ -77,64 +151,29 @@ class PKCS1 extends PKCS
static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
{
$num_primes = count($primes);
$raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
'modulus' => $n->toBytes(true),
'publicExponent' => $e->toBytes(true),
'privateExponent' => $d->toBytes(true),
'prime1' => $primes[1]->toBytes(true),
'prime2' => $primes[2]->toBytes(true),
'exponent1' => $exponents[1]->toBytes(true),
'exponent2' => $exponents[2]->toBytes(true),
'coefficient' => $coefficients[2]->toBytes(true)
);
$components = array();
foreach ($raw as $name => $value) {
$components[$name] = pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($value)), $value);
$key = [
'version' => $num_primes == 2 ? 'two-prime' : 'multi',
'modulus' => $n,
'publicExponent' => $e,
'privateExponent' => $d,
'prime1' => $primes[1],
'prime2' => $primes[2],
'exponent1' => $exponents[1],
'exponent2' => $exponents[2],
'coefficient' => $coefficients[2]
];
for ($i = 3; $i <= $num_primes; $i++) {
$key['otherPrimeInfos'][] = [
'prime' => $primes[$i],
'exponent' => $exponents[$i],
'coefficient' => $coefficients[$i]
];
}
$RSAPrivateKey = implode('', $components);
$asn1 = new ASN1();
$key = $asn1->encodeDER($key, RSAPrivateKey);
if ($num_primes > 2) {
$OtherPrimeInfos = '';
for ($i = 3; $i <= $num_primes; $i++) {
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
//
// OtherPrimeInfo ::= SEQUENCE {
// prime INTEGER, -- ri
// exponent INTEGER, -- di
// coefficient INTEGER -- ti
// }
$OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
$OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
}
$RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
}
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
if (!empty($password) || is_string($password)) {
$cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm);
$iv = Random::string($cipher->getBlockLength() >> 3);
$cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
$cipher->setIV($iv);
$iv = strtoupper(Hex::encode($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n" .
"DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" .
"\r\n" .
chunk_split(Base64::encode($cipher->encrypt($RSAPrivateKey)), 64) .
'-----END RSA PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
chunk_split(Base64::encode($RSAPrivateKey), 64) .
'-----END RSA PRIVATE KEY-----';
}
return $RSAPrivateKey;
return self::wrapPrivateKey($key, 'RSA', $password);
}
/**
@ -147,31 +186,14 @@ class PKCS1 extends PKCS
*/
static function savePublicKey(BigInteger $n, BigInteger $e)
{
$modulus = $n->toBytes(true);
$publicExponent = $e->toBytes(true);
$key = [
'modulus' => $n,
'publicExponent' => $e
];
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
$components = array(
'modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus),
'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent)
);
$asn1 = new ASN1();
$key = $asn1->encodeDER($key, RSAPublicKey);
$RSAPublicKey = pack(
'Ca*a*a*',
self::ASN1_SEQUENCE,
ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
$RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
chunk_split(Base64::encode($RSAPublicKey), 64) .
'-----END RSA PUBLIC KEY-----';
return $RSAPublicKey;
return self::wrapPublicKey($key, 'RSA');
}
}

View File

@ -27,21 +27,51 @@
namespace phpseclib\Crypt\RSA;
use ParagonIE\ConstantTime\Base64;
use phpseclib\Crypt\DES;
use phpseclib\Crypt\Random;
use phpseclib\Math\BigInteger;
use phpseclib\Common\Functions\ASN1;
use phpseclib\Crypt\Common\PKCS8 as Progenitor;
use phpseclib\File\ASN1;
/**
* PKCS#8 Formatted RSA Key Handler
* PKCS#1 Formatted RSA Key Handler
*
* @package RSA
* @author Jim Wigginton <terrafrost@php.net>
* @access public
*/
class PKCS8 extends PKCS
class PKCS8 extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @access public
* @param string $key
* @param string $password optional
* @return array
*/
static function load($key, $password = '')
{
$components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
$key = parent::load($key, $password);
if ($key === false) {
return false;
}
$type = isset($key['privateKey']) ? 'private' : 'public';
if ($key[$type . 'KeyAlgorithm']['algorithm'] != '1.2.840.113549.1.1.1') {
return false;
}
$result = $components + PKCS1::load($key[$type . 'Key']);
if (isset($key['meta'])) {
$result['meta'] = $key['meta'];
}
return $result;
}
/**
* Convert a private key to the appropriate format.
*
@ -57,108 +87,9 @@ class PKCS8 extends PKCS
*/
static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
{
$num_primes = count($primes);
$raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
'modulus' => $n->toBytes(true),
'publicExponent' => $e->toBytes(true),
'privateExponent' => $d->toBytes(true),
'prime1' => $primes[1]->toBytes(true),
'prime2' => $primes[2]->toBytes(true),
'exponent1' => $exponents[1]->toBytes(true),
'exponent2' => $exponents[2]->toBytes(true),
'coefficient' => $coefficients[2]->toBytes(true)
);
$components = array();
foreach ($raw as $name => $value) {
$components[$name] = pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($value)), $value);
}
$RSAPrivateKey = implode('', $components);
if ($num_primes > 2) {
$OtherPrimeInfos = '';
for ($i = 3; $i <= $num_primes; $i++) {
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
//
// OtherPrimeInfo ::= SEQUENCE {
// prime INTEGER, -- ri
// exponent INTEGER, -- di
// coefficient INTEGER -- ti
// }
$OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
$OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
}
$RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
}
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
$rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_INTEGER,
"\01\00",
$rsaOID,
4,
ASN1::encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
if (!empty($password) || is_string($password)) {
$salt = Random::string(8);
$iterationCount = 2048;
$crypto = new DES(DES::MODE_CBC);
$crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount);
$RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
$parameters = pack(
'Ca*a*Ca*N',
self::ASN1_OCTETSTRING,
ASN1::encodeLength(strlen($salt)),
$salt,
self::ASN1_INTEGER,
ASN1::encodeLength(4),
$iterationCount
);
$pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
$encryptionAlgorithm = pack(
'Ca*a*Ca*a*',
self::ASN1_OBJECT,
ASN1::encodeLength(strlen($pbeWithMD5AndDES_CBC)),
$pbeWithMD5AndDES_CBC,
self::ASN1_SEQUENCE,
ASN1::encodeLength(strlen($parameters)),
$parameters
);
$RSAPrivateKey = pack(
'Ca*a*Ca*a*',
self::ASN1_SEQUENCE,
ASN1::encodeLength(strlen($encryptionAlgorithm)),
$encryptionAlgorithm,
self::ASN1_OCTETSTRING,
ASN1::encodeLength(strlen($RSAPrivateKey)),
$RSAPrivateKey
);
$RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
$RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
chunk_split(Base64::encode($RSAPrivateKey), 64) .
'-----END ENCRYPTED PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
chunk_split(Base64::encode($RSAPrivateKey), 64) .
'-----END PRIVATE KEY-----';
}
return $RSAPrivateKey;
$key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
$key = ASN1::extractBER($key);
return self::wrapPrivateKey($key, '1.2.840.113549.1.1.1', [], $password);
}
/**
@ -171,43 +102,8 @@ class PKCS8 extends PKCS
*/
static function savePublicKey(BigInteger $n, BigInteger $e)
{
$modulus = $n->toBytes(true);
$publicExponent = $e->toBytes(true);
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
$components = array(
'modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus),
'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent)
);
$RSAPublicKey = pack(
'Ca*a*a*',
self::ASN1_SEQUENCE,
ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
$rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) . ASN1::encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
$RSAPublicKey = pack(
'Ca*a*',
self::ASN1_SEQUENCE,
ASN1::encodeLength(strlen($rsaOID . $RSAPublicKey)),
$rsaOID . $RSAPublicKey
);
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(Base64::encode($RSAPublicKey), 64) .
'-----END PUBLIC KEY-----';
return $RSAPublicKey;
$key = PKCS1::savePublicKey($n, $e);
$key = ASN1::extractBER($key);
return self::wrapPublicKey($key, '1.2.840.113549.1.1.1');
}
}

View File

@ -138,9 +138,6 @@ class PuTTY
$crypto->setIV(str_repeat("\0", $crypto->getBlockLength() >> 3));
$crypto->disablePadding();
$private = $crypto->decrypt($private);
if ($private === false) {
return false;
}
}
extract(unpack('Nlength', Strings::shift($private, 4)));
@ -289,7 +286,7 @@ class PuTTY
$n
);
$key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" .
'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\"'), self::$comment) . "\"\r\n";
'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\"'), self::$comment) . "\"\r\n" .
chunk_split(Base64::encode($key), 64) .
'---- END SSH2 PUBLIC KEY ----';

View File

@ -184,6 +184,7 @@ class Random
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
$result.= $r;
}
return substr($result, 0, $length);
}

View File

@ -1286,4 +1286,31 @@ class ASN1
}
return $out;
}
/**
* Extract raw BER from Base64 encoding
*
* @access private
* @param string $str
* @return string
*/
static function extractBER($str)
{
/* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
* above and beyond the ceritificate.
* ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
*
* Bag Attributes
* localKeyID: 01 00 00 00
* subject=/O=organization/OU=org unit/CN=common name
* issuer=/O=organization/CN=common name
*/
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
// remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false;
return $temp != false ? $temp : $str;
}
}

View File

@ -1462,7 +1462,7 @@ class X509
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcert = $this->_extractBER($cert);
$newcert = ASN1::extractBER($cert);
if ($mode == self::FORMAT_PEM && $cert == $newcert) {
return false;
}
@ -2974,7 +2974,7 @@ class X509
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcsr = $this->_extractBER($csr);
$newcsr = ASN1::extractBER($csr);
if ($mode == self::FORMAT_PEM && $csr == $newcsr) {
return false;
}
@ -3216,7 +3216,7 @@ class X509
$asn1 = new ASN1();
if ($mode != self::FORMAT_DER) {
$newcrl = $this->_extractBER($crl);
$newcrl = ASN1::extractBER($crl);
if ($mode == self::FORMAT_PEM && $crl == $newcrl) {
return false;
}
@ -3847,7 +3847,7 @@ class X509
if (strtolower($date) == 'lifetime') {
$temp = '99991231235959Z';
$asn1 = new ASN1();
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . Functions::encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
$this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
@ -4465,7 +4465,7 @@ class X509
}
// If in PEM format, convert to binary.
$key = $this->_extractBER($key);
$key = ASN1::extractBER($key);
// Now we have the key string: compute its sha-1 sum.
$hash = new Hash('sha1');
@ -4770,33 +4770,6 @@ class X509
return false;
}
/**
* Extract raw BER from Base64 encoding
*
* @access private
* @param string $str
* @return string
*/
function _extractBER($str)
{
/* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
* above and beyond the ceritificate.
* ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
*
* Bag Attributes
* localKeyID: 01 00 00 00
* subject=/O=organization/OU=org unit/CN=common name
* issuer=/O=organization/CN=common name
*/
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
// remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false;
return $temp != false ? $temp : $str;
}
/**
* Returns the OID corresponding to a name
*

View File

@ -53,6 +53,7 @@ namespace phpseclib\Math;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\Random;
use phpseclib\Common\Functions\ASN1;
/**
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
@ -1602,26 +1603,26 @@ class BigInteger
);
$components = array(
'modulus' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
'publicExponent' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
'modulus' => pack('Ca*a*', 2, ASN1::encodeLength(strlen($components['modulus'])), $components['modulus']),
'publicExponent' => pack('Ca*a*', 2, ASN1::encodeLength(strlen($components['publicExponent'])), $components['publicExponent'])
);
$RSAPublicKey = pack(
'Ca*a*a*',
48,
self::_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
$rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) . self::_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
$RSAPublicKey = chr(3) . ASN1::encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
$encapsulated = pack(
'Ca*a*',
48,
self::_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)),
ASN1::encodeLength(strlen($rsaOID . $RSAPublicKey)),
$rsaOID . $RSAPublicKey
);
@ -3009,10 +3010,35 @@ class BigInteger
return $this->bitwise_leftRotate(-$shift);
}
/**
* Returns the smallest and largest n-bit number
*
* @param int $bits
* @return \phpseclib\Math\BigInteger
* @access public
*/
static function minMaxBits($bits)
{
$bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}
return array(
'min' => new static($min, 256),
'max' => new static($max, 256)
);
}
/**
* Generates a random number of a certain size
*
* Byte length is equal to $size. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
* Bit length is equal to $size.
*
* @param int $size
* @return \phpseclib\Math\BigInteger
@ -3020,23 +3046,8 @@ class BigInteger
*/
static function random($size)
{
if (class_exists('\phpseclib\Crypt\Random')) {
$random = Random::string($size);
} else {
$random = '';
if ($size & 1) {
$random.= chr(mt_rand(0, 255));
}
$blocks = $size >> 1;
for ($i = 0; $i < $blocks; ++$i) {
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
$random.= pack('n', mt_rand(0, 0xFFFF));
}
}
return new static($random, 256);
extract(self::minMaxBits($size));
return self::randomRange($min, $max);
}
/**
@ -3058,7 +3069,7 @@ class BigInteger
$compare = $max->compare($min);
if (!$compare) {
return $this->_normalize($min);
return $min;
} elseif ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
@ -3072,6 +3083,7 @@ class BigInteger
}
$max = $max->subtract($min->subtract($one));
$size = strlen(ltrim($max->toBytes(), chr(0)));
/*
@ -3090,7 +3102,7 @@ class BigInteger
http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
*/
$random_max = new static(chr(1) . str_repeat("\0", $size), 256);
$random = static::random($size);
$random = new static(Random::string($size), 256);
list($max_multiple) = $random_max->divide($max);
$max_multiple = $max_multiple->multiply($max);
@ -3099,7 +3111,7 @@ class BigInteger
$random = $random->subtract($max_multiple);
$random_max = $random_max->subtract($max_multiple);
$random = $random->bitwise_leftShift(8);
$random = $random->add(self::random(1));
$random = $random->add(new static(Random::string(1), 256));
$random_max = $random_max->bitwise_leftShift(8);
list($max_multiple) = $random_max->divide($max);
$max_multiple = $max_multiple->multiply($max);
@ -3112,46 +3124,30 @@ class BigInteger
/**
* Generates a random prime number of a certain size
*
* Byte length is equal to $size
* Bit length is equal to $size
*
* @param int $size
* @param int $timeout
* @return \phpseclib\Math\BigInteger
* @access public
*/
static function randomPrime($size, $timeout = false)
static function randomPrime($size)
{
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}
return self::randomRangePrime(
new Math_BigInteger($min, 256),
new Math_BigInteger($max, 256),
$timeout
);
extract(self::minMaxBits($size));
return self::randomRangePrime($min, $max);
}
/**
* Generate a random prime number between a range
*
* If there's not a prime within the given range, false will be returned.
* If more than $timeout seconds have elapsed, give up and return false.
*
* @param \phpseclib\Math\BigInteger $min
* @param \phpseclib\Math\BigInteger $max
* @param int $timeout
* @return Math_BigInteger|false
* @access public
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
*/
static function randomRangePrime(BigInteger $min, BigInteger $max, $timeout = false)
static function randomRangePrime(BigInteger $min, BigInteger $max)
{
$compare = $max->compare($min);
@ -3170,9 +3166,7 @@ class BigInteger
$two = new static(2);
}
$start = time();
$x = self::random($min, $max);
$x = self::randomRange($min, $max);
// gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) {
@ -3187,7 +3181,7 @@ class BigInteger
$x = $x->subtract($one);
}
return self::randomPrime($min, $x);
return self::randomRangePrime($min, $x);
}
if ($x->equals($two)) {
@ -3207,10 +3201,6 @@ class BigInteger
$initial_x = clone $x;
while (true) {
if ($timeout !== false && time() - $start > $timeout) {
return false;
}
if ($x->isPrime()) {
return $x;
}
@ -3391,7 +3381,7 @@ class BigInteger
}
for ($i = 0; $i < $t; ++$i) {
$a = self::random($two, $n_2);
$a = self::randomRange($two, $n_2);
$y = $a->modPow($r, $n);
if (!$y->equals($one) && !$y->equals($n_1)) {
@ -3661,26 +3651,6 @@ class BigInteger
return $temp['int'];
}
/**
* DER-encode an integer
*
* The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL
*
* @see self::modPow()
* @access private
* @param int $length
* @return string
*/
static function _encodeASN1Length($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* Single digit division
*
@ -3723,7 +3693,6 @@ class BigInteger
* @param \phpseclib\Math\BigInteger $n
* @access public
* @return \phpseclib\Math\BigInteger
*
* @internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.
*/
function root($n = null)
@ -3876,4 +3845,26 @@ class BigInteger
}
return $max;
}
/**
* Return the size of a BigInteger in bits
*
* @access public
* @return int
*/
function getLength()
{
return strlen($this->toBits());
}
/**
* Return the size of a BigInteger in bytes
*
* @access public
* @return int
*/
function getLengthInBytes()
{
return strlen($this->toBytes());
}
}

View File

@ -1511,7 +1511,7 @@ class SSH2
-- http://tools.ietf.org/html/rfc4419#section-6.2 */
$one = new BigInteger(1);
$keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
$keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));
$max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
$max = $max->subtract($one);
@ -2928,9 +2928,6 @@ class SSH2
if ($this->decrypt !== false) {
$raw = $this->decrypt->decrypt($raw);
}
if ($raw === false) {
throw new \RuntimeException('Unable to decrypt content');
}
extract(unpack('Npacket_length/Cpadding_length', Strings::shift($raw, 5)));

View File

@ -375,7 +375,7 @@ class Unit_Crypt_HashTest extends PhpseclibTestCase
public function testGetLengthKnown($algorithm, $length)
{
$hash = new Hash($algorithm);
$this->assertSame($hash->getLength(), $length);
$this->assertSame($hash->getLengthInBytes(), $length);
}
public function lengths()

View File

@ -1,4 +1,5 @@
<?php
/**
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
@ -6,6 +7,7 @@
*/
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA\PKCS1;
class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
{
@ -16,6 +18,8 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey);
$this->assertNotEmpty("$privatekey");
$this->assertNotEmpty("$publickey");
$this->assertSame($privatekey->getLength(), 768);
$this->assertSame($publickey->getLength(), 768);
return array($publickey, $privatekey);
}
@ -31,4 +35,31 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
$plaintext = $privatekey->decrypt($ciphertext);
$this->assertSame($plaintext, 'zzz');
}
public function testMultiPrime()
{
RSA::setEngine(RSA::ENGINE_INTERNAL);
RSA::setSmallestPrime(256);
extract(RSA::createKey(1024));
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey);
$privatekey->setPrivateKeyFormat('PKCS1');
$this->assertNotEmpty("$privatekey");
$this->assertNotEmpty("$publickey");
$this->assertSame($privatekey->getLength(), 1024);
$this->assertSame($publickey->getLength(), 1024);
$r = PKCS1::load("$privatekey");
$this->assertCount(4, $r['primes']);
// the last prime number could be slightly over. eg. 99 * 99 == 9801 but 10 * 10 = 100. the more numbers you're
// multiplying the less certain you are to have each of them multiply to an n-bit number
foreach (array_slice($r['primes'], 0, 3) as $i => $prime) {
$this->assertSame($prime->getLength(), 256);
}
$rsa = new RSA();
$rsa->load($privatekey->getPrivateKey());
$signature = $rsa->sign('zzz');
$rsa->load($rsa->getPublicKey());
$this->assertTrue($rsa->verify('zzz', $signature));
}
}

View File

@ -7,6 +7,7 @@
use phpseclib\Crypt\RSA;
use phpseclib\Crypt\RSA\PKCS1;
use phpseclib\Crypt\RSA\PKCS8;
use phpseclib\Crypt\RSA\PuTTY;
use phpseclib\Math\BigInteger;
@ -376,6 +377,8 @@ Private-MAC: 03e2cb74e1d67652fbad063d2ed0478f31bdf256
$key = preg_replace('#(?<!\r)\n#', "\r\n", $key);
$this->assertTrue($rsa->load($key));
$rsa->setPrivateKeyFormat('PKCS1');
PKCS1::setEncryptionAlgorithm('AES-256-CBC');
$rsa->setPassword('demo');
@ -554,4 +557,368 @@ AAIBAAIBAAIBAAIBAA==
$rsa->sign('zzzz', RSA::PADDING_PKCS1);
}
public function pkcs8tester($key, $pass)
{
$rsa = new RSA();
$rsa->setPassword($pass);
$rsa->load($key);
$r = PKCS8::load($key, $pass);
PKCS8::setEncryptionAlgorithm($r['meta']['algorithm']);
if (isset($r['meta']['cipher'])) {
PKCS8::setEncryptionScheme($r['meta']['cipher']);
}
if (isset($r['meta']['prf'])) {
PKCS8::setPRF($r['meta']['prf']);
}
$newkey = "$rsa";
$r2 = PKCS8::load($newkey, $pass);
$this->assertSame($r['meta']['algorithm'], $r2['meta']['algorithm']);
if (isset($r['meta']['cipher']) || isset($r2['meta']['cipher'])) {
$this->assertSame($r['meta']['cipher'], $r2['meta']['cipher']);
}
if (isset($r['meta']['prf']) || isset($r2['meta']['prf'])) {
$this->assertSame($r['meta']['prf'], $r2['meta']['prf']);
}
$rsa2 = new RSA();
$rsa2->setPassword($pass);
$rsa2->load($newkey);
// comparing $key to $newkey won't work since phpseclib randomly generates IV's and salt's
// so we'll strip the encryption
$rsa->setPassword();
$rsa2->setPassword();
$this->assertSame("$rsa", "$rsa2");
}
public function testPKCS8AES256CBC()
{
// openssl pkcs8 -in private.pem -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256 -out enckey.pem
// EncryptionAlgorithm: id-PBES2
// EncryptionScheme: aes256-CBC-PAD
// PRF: id-hmacWithSHA256 (default)
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIIU53ox17kUkCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBATi8ZER9juR41S2c35WXTQBIIE
0K98r5dQq/OxbwA2CH0ENs9Jw2qjvW0uGkH8DdO8XvCJohMrIU8FABxw/50Af5Ew
Nq4FJIYz90LjZzlI7kf97TDMZKw2K3AleymwmfMcKer5pZ6jqdxLGXFztdj3Fm/S
P+NcVjEZFSEH1MNDEPhiPSIUAf1yQcLwKAzHH0JTnZBOFSBbGxTZLYfvD2angVNL
xTivLYJGdr1cUrAuZQcM3JGQEvCA5qAC7oRhdVgGyJrl8xXY3mVlaXMsW8A+Q7xj
NyH7lJUFEF3YPMbpWr8zblCQYgGByM++yOfYQXno50AgWdYjPO88pPzKcCe4x6WV
qKlvqTYZqb1HgZurTd3BS/e6GWRgnRt8W87nuNcyJasud92Z0FhSGwIirlE89gUW
EinbY8m6+sL9VZZ5+t66TROtpj1Ohj8t3W+01oLDCtdSTGwLuq9XUsEyuYZSqUN9
0F43U8pOykNbChi1S8vfFdwf7U1R+hgoF0MRNDwh3hRfSS0zPUnCGb6hDZrOZB9C
e3xbfXiujVlfhRc7r2qbZHAwqNLcccC98oLfbEIUdBXn6M7GfFIwiuNiS48rehp0
dA9+CiWJBq+7b/lRdcgQJxjwUpxtMXr/812Bky4dDoMDs32cmMghH2sgUvht0imy
ZhA3IvSCAV1wVoQLqUuPXLMskcKsNCTbL9AYEpJm612dm43btXec2vtjCc4ajpCg
wICLE2V1jwzWw0girrT/IMt8QUd3fkJZkEAbmFHwuZptFnreRCidZjfQqYhWfyqJ
nGW+cc7G1bGwxt32fC5eu23hBTJERmRlvkhC+v2WKhYXcKyOKQn5/I4eaEZauDn3
wmg3f4h/PPuQgqv/vspOai9a5HhPRNyeIjXsk3hxHepEgV+kVSU50BpchSSzBuhK
71F3nOMTyJ/XXxaZrLLtpo3CcXmI0/JuNG7pjDS++Vx/BQFs8xxDfxRs0Um7RlT1
piGZGDn9zHNpbspHkAeoQmlplbmjtCClojhfBj4HbXTtlYmDgwKHul4YIni6kgCr
G+WduGXLeyxmH976vvJasD4wyttL2CZTHLR7Elp+yl0xjXMlj/iP4WYozJAmGifq
xjLWMsZ0gaBtAoOFrvcgOueE7+E+NdbIHzU4u5FTbz0DLCvrsZeKwpOPEsMw0LVG
T6rNsBzMY3XyBtV1FdXwmuOcWha62Ezr/RRrfvRPRImy/xVVKOrOQ/KbyELkjroh
UAEPs7s+89Ovc7P30IfS0Xzlhz2aSRflZarOIqu1JtjTYZ0XWLTWoQT2fjZdnMDV
qFrbTPdXezqTAAzk3rnkkghgamTVQ7Y8D+BIGHIc4+oVT2jxzSjBQC7szmudanGQ
hfGLyO+vwLg4r1lanzSULtqfwTZMarjYGxLqpQp8cIjJfzvLI3psRDFyuWCdIbEs
y3VKgoNsa+PmyimGSa7x2cw6ayTx9wlOhPzaBwqMhHxr4qJwS2ohDONeRfnPr34+
oVD7mnCBLB14qiZcpQv+qPGvd/Q/tA5SBNbZhPuWtjqvy7/K+1FQX6xvx1kl7p9W
l0Q99rwqECl8y+CiKEXdItkCTA/vgxblSt465Mbdic7cbcP6wAMSGmpryrmZomm/
mKVKf5kPx2aR2W2KAcgw3TJIu1QX7N+l3kFrf9Owtz1a
-----END ENCRYPTED PRIVATE KEY-----';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS8RC2CBC()
{
// openssl pkcs8 -in private.pem -topk8 -v2 rc2 -out enckey.pem
// EncryptionAlgorithm: id-PBES2
// EncryptionScheme: rc2CBC
// PRF: id-hmacWithSHA1
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFFjBIBgkqhkiG9w0BBQ0wOzAeBgkqhkiG9w0BBQwwEQQI/V5Qw9+hKt4CAggA
AgEQMBkGCCqGSIb3DQMCMA0CAToECPxrtS4U+IIBBIIEyKQyYpJ8tfWXVvitxaPq
+gtrVVWd/ukjwZ+jQY3g/ZjZNWQPq5XbuoP5F3u5g4V+RoXzAIkdwyiveEv+XssV
DJVHfNiL6VcdxhFJ1rmt2uq9vFW/x9UHDqAWsnytn46NFRWUqgKzkYWMDqU71IC/
wyq7UzjTtqdLzaCxkTWYst1o+Iu7VXapFVcscPYyGshLVyZ0x/etc/09LOC4bIpk
3Qzf+f+adrNxW0mbD3SyDfVadvS8mApsd7bJR320iEKd4CmW0sNAzKkm2ya2aUIi
Hrk3DEgr4rPmpn3BVfZ6pg+yRu+MOxBhl+8yfA+E8kXfe/F7BiMkJQcJTOfLRLfH
TXipyb4f8oa+gmwwWK0jfCuxoxiOTA1CBCjZoTvdSuFYVTdblysQO3BivvSQgbmD
oHntb7HEoZ6yB49u/LrrowUQNH+XihBcototyLCmC5K+x8N5cZsp+yaLJekDHlQs
ATVMeKCbPjYaS4g48lDyC1VbtNtJc/zN5gOUB0PM80iB02iZegYyeW5+WWzY+Lgu
lpWLH7PdpqL5KtoH6SJKD6Szl8dKJLYzpHI2esckpp9YsDtX2z/VkUFKTd0PeeNh
WefX0q8A47NBeBLFEZqmzPrL6IyaPnnPCUsvqk6MEA1DgsmY3DFd8nEYhzJIAwoy
Rw1mCqwL0uukQPqFGByU9YRHyhJd5aAPyF1xSLfUQUJb9xn+wyN57xoamFePPWMi
UXdESZWX+rjA0ChfEtL9AzXcfO9PBS1p/2JkVxUt/UPfI9SgQn92kLo0LRi/iRLk
H4zjnkaDy65ZY15bzyK+EvJ+VZ+P24QI7X12f1m+rkssMekHWHf5/SitUpW26ZFe
M6vXyz3RlXxow+0WcsPob19n/vbgeJQPTfMY0zPS0iCRIggC/liWMEOzP/R1jCYi
q8TEaUi1Ztx3Gp4Y8Vcf33a/YsxKoUsQlFFtyE6KE3ZEI03E6cMiX21nWULKrk9l
+8Tq4T1a8I4goVa+e4CYBYwMAY9fdfUJ/p1EnrG6Ynj06a2Zx0IK/dF5w0b/5TeL
PMyafb4FHkpkyYYFlktQdKIqGjjtmKUr56/7vumVHUyItf5nSuM8lLps8to2MLkE
MAolD+X4FIGs/1Z5NlUb5AlNVNRY1c7tf+YSXI21PlkBpaRSAvN9/2fmGnxWSvAa
BEGR0JA4zMPrCSpxrBQpOrZPh/cD9YXNu+N9P4dtf57smCviKTJg8eMl9NYu4vtc
FygdqPKuhJM2WI5Qdrqjbf4NQ1mngSxXNKrcNmC/m60JPNKHC7dM2ynbN8QyZqEE
EzSdL0Z3YQGnuwr+4zKHHsNnO4nRJfUowWks4Gvi2HIyy3DBVqqyEPxDDGEpcqs+
8GNKTGBg1PCVg+I9Xjxio4tBuwLDo6Y8Ef8SphN/0DC9svaQRfEOY3/9WB+fDnrq
SUSNZNWetkCd247WHwl+JvJDXCuzGJ2+JG5DXuEdCq2EhEVNUWPuotXTPvI+0wsP
Kq23uvzS53ZArQnxlqgwyXQ06jzc+J4AiNtl3uIw8D6LrRyaDsOsKQCEh7qjkqTc
khzefbnNRDL5PIJnTfM7vSQ4nUzdAxs/7YzX6GMx1DaCtBANbUVUoIE+3oKdqpGV
9AmO2phYWCBefw==
-----END ENCRYPTED PRIVATE KEY-----';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS83DES()
{
// openssl pkcs8 -in private.pem -topk8 -v1 PBE-SHA1-3DES -out enckey.pem
// EncryptionAlgorithm: pbeWithSHAAnd3-KeyTripleDES-CBC
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6jAcBgoqhkiG9w0BDAEDMA4ECHwurC0qxNK7AgIIAASCBMjRJefQ1oLo/pml
Zw1qTE2NMgSNdP6z3vEap0qMMs/EXO37GDuHGla/yvvBIZbBXPVoQwd7K9QfU1NP
JCBtNBTD9Pl7a4fJIlrf2dN5SCP8lu+nFa5ZyCiBvUtxfdROnhXfkhs0kqOowLaq
1mw+Di9FPSA3ZkdJTPpAyMNPMlYpQII2ex+j6NmRB8t7O121o6ynbmDj0Rh87dv9
VtRhO9sonTy160Mv2HPMLMliXMwFUvpEH9XNE+K6V6DnoMc7I9jmEnqLAzmjsKv/
AOwuX8t8cPboeGOu++/0h3879+OVcnkXGMW0aAT+3sX4oMgEVREHDwkn2IGsrIuV
SerUKg8WSoyhRNb9j7uJAlvU6bCrivcOujjNasdWKG47ojeiySFUkKu9JBohQ+vI
mrlkqZv+FMwEKgApPhCKbQYqLuKl/kp6lNBXmhcusuxsGCnaw7/Wa+Y6p+Gz6UL5
caFpDm6FX+Snvi5/6sUpMKL9LPAAZZVRpKj9JWcidEoXa5rINIMtKyVpl+GEQmac
9lCdFq+5zO2r94af9AKRUIqTquyCkcy2s2mzNq2IIv2atibnb2HQex0/EhLFxMC/
UZbl61YaSBxrH52LY4SNOUy+ppCsP4z0ojTdci9Yc3BnMqzSPD3FMQPmGpWWRGOq
Jdj2/B76Q7rYZjIdrh6UbSROrgTNgmbeASxfDSHHmmcZyIUtMzBC45bmz3ra/FO0
eb+6srXOmdIG368/xRdo9o1R/cNw9BHgGu5R/Bx+AxhK8DuhL7rMYLVn3Ukl2qrt
0koCtDbPxq2YgF8VYXz4WNRCmLuUroty99WVOM7BvM3XyfSP5iLynoRr8B7Rju5K
t5o/OJUrNqSNjtzYd3PZEXqi7ShWkYp5BACzBfSxkGfL6bBMfoM8Yd3dBLrplRu8
WpVbJSA3DbJwAyGhKP3dQmmhBH9nJEppSK0iQPCruAyyZJT6kEmPhcNuYq8CyWe4
+l9Hs5qIHMrkEq1BcLYiQFBLVLXR6eHf3J8fAMmz8I74TWNM2u3FZUcDoIwqHmHG
zDwYZ9h1tkijvyvbH5RkMWRb7WAB2b9Q9ZsPR0naQbqHmO73Uypu/pfugx+cmOPr
AiYOQHnwSCDcaTHJnO33L7KhgA+RfIRoigJXgeYzlWmjW/U6SRK8RTvda4lxPsOo
/bXTZOoUA8qTDKT02n20h0Ab6kLDApSigGQlYI8Jhfre/PGFWfrLxPpy4ED53sPg
xY1d6tIa18yQCAznC3k6Q9OK02bGaFTcGnTPg88PBgyUFuqljKGrG9rpm4uTPzwa
1vzKv05oYjK9xyzy6LkIPYHyp6R2tedVO34pa52LNCO3/DaLnwiDfMRW5SFu0J0E
P7/viLPTwwR6zdAAlt8hsT6apBYlLzVqGRy9nbN60ZS3Q8lIe6xR47koWAJvnHuS
uBx9xP3JWcs+Cnis85wODY1qxXa0Yx58kUVmoyyiBOWHcwsi82YtgwAAjhR8K1wy
gRJR72XIKmdZ/RINp7f2dN7xswy8iU2m3vBxgc1AH2/8knlGLebNS7/RJwW1KXsb
pp/6vHRPSla5cxzsF9NmYHmSAUpk1t+Mo+YjjoT63bVC4xNzkXdft4l4QyUQQXU2
aENeUJKn2r8X3Tpz92U=
-----END ENCRYPTED PRIVATE KEY-----
';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS82DES()
{
// openssl pkcs8 -in private.pem -topk8 -v1 PBE-SHA1-2DES -out enckey.pem
// EncryptionAlgorithm: pbeWithSHAAnd2-KeyTripleDES-CBC
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6jAcBgoqhkiG9w0BDAEEMA4ECGT21lf3Tl4dAgIIAASCBMgJmnyaQktoSk8u
JyUAaB4ZgGWW22BX1xRA7en0sNj4PhBxj1DEXGKNUBx3k6u6Cd7JUupxGfeag5aw
fi0bWNgEw7YSITmZKaKO5Ee4shQrEDucaH6KEGYV6YspNys5dD817hmd4Yc31q2e
Ig5k3rIpP72Yy9Si0FXmKDE8/GmCYckdIQVUCGZD9nLugvqEC0adludfMAwzCHUY
68jeyKiPJGTFSmE0wPD5EaWknn3U0eRcmKwZPtFpWEAEv5GTm1h6Y5q+P2X2Qsia
Neoa4jjnSEww85zbno/k0KdoRIuSEM8qOHdNuU6XNTCGKxEkgBLkY+vjRjOCi+K4
fzJSAPxaYATGX+W8EWegz3yhwiFujjDPkO9nfeoyks/saFbP9uT8aesZUn4/rIw0
KyW7lYW0TUyBxfIXg1DEsKcmSrrb0WrFLN/MnjO8Y1bAY63KgKgpFZk+7H/7eCmD
2Egj6o2LXTEPkxwYyeR41k64LM5RFF5qs4wS0Gfo1oTc6lSbuZNFHSgsXkb+CXFL
JZ3CuaYFY5Ldfm+1HsrZ9s3GmNAnog6WABXIcz9aULUyJfLr+oZaQR7TC5KpM5Xy
dyztlsN43D9UZKdz93zW2V3LxbzbOWTrcd9dB1GwrPvWIy/0/dqFOvpcr9k/4S1T
AJ6pja4x19EQLj2DUvO7JQEy2Rlam+SI/ARQTc0W0dJ8x7FboHZDxUQCRDih5Qw3
s/xoGflLUYYtAR5hfgjbWuvG3Told4IYlBn2vvVu7UxXQekUOaZLePqucAP3sTDC
pK7JK+OT223FNU5NieGS4hh+jxZQnLuuyxWQaTCJM9isYPqJYsWT9X+c44ixOgLJ
unYtg+8Lck3On6wiDUPWTLCGJvjb53NhPSovTjNBW2Q7YszXXjeO5svwwxtKHabx
vCDsG0zdNdwIgupqynbtcuUhsmIsJKBu5c+9i8P21rNF0DZjOkv8mThRA6YQLce8
mLTcnpLsvCGNehVEStD6pr+CtGsQEEtH3bPc2ZBrpxtz1EHmrI7H3kX1gjbD7bsT
XWzaxsId+8pmqnAcMRzU3mRv4Fe+387X2irG4OxR/6cFMk3+yfpKJLSsNh6HAVRX
xzYwVz2WP7RM0KuLh7auAcI1mHk+0xAvDi7s3ggy3SzLzQq9p+EEFVGVSYuVFLbi
TtlY6HQ3b1Z6KgntoPj79YuOmri6/8w7nBkKt09faYLUf9wZWHLL9/LjZqoJxPfl
lX5Ss4+MDV1aG9aJoTT53d8Gn8ApWK+XFToFg2InYZzZqBnKP8DHPG8D2Gh/MZlA
Yt4hPDNLf733zm1zJTWo0TF6+4AwZp7XUKTg+pM1CZJDOTbJlEA+cXY3BxOq10bl
JPJmV/JFINqSeBLN8V2Ong8Q0Dt4uabSmlOUz19SXpimBrO8ztxaqigMFIKMbLXX
uIVAoxG7KLPuv44yK3Fjsg6OtwZnrWqea02b1qwFFrIKoqmQ8FNFBMYqcHU2IkSq
gJqSylfqcre5Y1DOSlcjGa7aP4C8AyB5qOG7LZ/CLAePKqgAHtMd/K40Zgku36Ir
9OAcUXy/H5PFJIleEyjvLLBE0VWs0TVBXi/FiIqwvAYNOFqXl/gtRcZ0kVex/QeN
rcONqwmUGJOjrfhUyJA=
-----END ENCRYPTED PRIVATE KEY-----
';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS8RC2()
{
// openssl pkcs8 -in private.pem -topk8 -v1 PBE-SHA1-RC2-128 -out enckey.pem
// EncryptionAlgorithm: pbeWithSHAAnd128BitRC2-CBC
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6jAcBgoqhkiG9w0BDAEFMA4ECAdC2l5rzAQeAgIIAASCBMjuEQDNkvSX6ylp
WsgQZUSvPdNpdlG084oLmhTV0z4pZeLB0YCyKCM7GMVQ0tsprRW0ky86ulbY3W5Q
86WNHXYtVIFXEmIjN1syRG5Pq3RZ4Ba6wf36Gc/1713p6GjcPxLZ+JOw1xBEm1rh
1nI9b43PzbKmczs+6IvRO5b9MjKNkBeNzH9kh4b3zsEW/IFgYaz8zip/zRu4hCSW
ORhnRYFvbI22E84g4/SB1WS34nR/flyZBXT4P87s7bwXEOsXAGnEeVF38znV3awD
V8rry2e1drRmlhfNhvDroQrkv7O9X2ee91I9gahPKpXtAlGXBgRb8qjVHeI8Ea3K
Ty2zcWnjdE7/jt6pO07+B+FlHNdKySlFdKTHEmJ1x+O5Ui4JaGjI2UML0yGHoYFU
wGH/1DYJ4+R9d97BYJ/yp9+JAQAjpG7UUt3jFgNx0CAbP7d438l06z2EE87EqIEa
3Y0ZG1Q5PWE60hPJsvUELdgzcUiKkVCxhOPhwmbSlQpEYXZRBWv0RAJzey6yPMQ+
L/TkMDpgTNUk9x+n3ehnRuA/tlthxXN/ViDthPO7ovSVCsKsUq6lXotO+3hHLGs0
u8ZyVKHNEqGso7PfAFsjcJq2C46mQNME4HPOWm5J+TFf/vvwqdYKCiF3arV0hUtW
x9lyPR2PvZM1ik9jXi6lc5hPegcwmx9/j5yc4/3rQNiwe4dtUpL5JLKAxecKBLgr
atyVnAs69JYaaUT9aKRDzYzCRjo0jIQ9/lgJl2DqVRF9aYnknrVWRIjyKbfKjw7N
w0yfXlVRw46YuJ72PSHpr7HcfzL1EWzmKcAEPDH/UCpIaoeNTwxeEQSltOM0D0Su
ZzyAQP8sWSXSwdtD5YD7iiUjDN4UMDuIwAEMLN9231/RjvKuLAys0oNRQfHkkiCo
9rt/VUP8be98UTyKu7Bx7JUEW6VVnYM+Y274MLQk6TcjZyXgbKwhHFJyAjcBAFQp
5kkYES0kk+57HeBImcB0a5qBor/uAnlsCV690roUlBtkVhBOkTjVi7w/uZsSjIWr
MBndNHTFqqnkbm8xOOoSH6vS32c1KE8FrGpmkPGc6wziX9Ja/MkuLDXrBIlnP4Yj
aCf0sVMSR1/LoHIGGaGXmTzs0VTR8Z5EyW5uvvCy6dWCWnTKEWmTTS+zqW1RYVBZ
n/P2ovj2Kl4rhQuSpfOE9xBFWsgPAD6T2FJzfvu0/D3Sw5pI/RT4NnQ77oJSs+jN
lV33FeceRoJqjcP6YMAiRX4RmkTeD5Hgy0YRLrfQ4PKwAQG82uIj3yqXWveexwb0
Cpm5XxzgCMWGBRvIvM+yByf0SP7fIWYHbzsEWJkN5btF3tMc6i12q7AJ0/UFMQLt
KNiMg+dLWP18cySU8OysXqPq1JKHDU1NMg2Xf9o2c35eOktLfcO9axS4oAAz4bGN
hTB8rk+MWnHfSlWvMPMzlJyndXv/WxfojujLogDTOHd4/q4KyoOwJY3H44eeW79u
sS7pDbjKMl8A15BLMLx01DaOYk7EiHFnGIpY2V2+2Xm7vQu9+8fHSf8whuCRVmBY
Drhy2HTF1veKrQ6IrIyQicmzTtW6moSnNg69SpuzKTegYyCRsSHDIL3WxMoopVwr
Pu3ed6UvXDfotj3v8rE=
-----END ENCRYPTED PRIVATE KEY-----';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS8RC240()
{
// openssl pkcs8 -in private.pem -topk8 -v1 PBE-SHA1-RC2-40 -out enckey.pem
// EncryptionAlgorithm: pbeWithSHAAnd40BitRC2-CBC
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6jAcBgoqhkiG9w0BDAEGMA4ECHzZ2FqUJyiCAgIIAASCBMiKVNJiU/1UCaoq
V6VSX33gL8CjQqhzDEUlXhHoSYx7IWAJIx7C1DDgeLDfJ//cCMlBPbIOr6knohFQ
KegbsulIYm2DXUQvEoivh02F4An2RLkP0JSMl5CmYTbiu/nJic2jdin+vaKWhRA8
Kznk5wEuhz4t6Oo4Hp3k3+0sd6YqLbmdcFCiYSXE52WN271VvUXqwz3TosUgOUVH
YNzlER1xzNBFgcUyrfiyA+t5NaSDfpYncD64zXFz2KgkjcpfPBBMRnQ2I4mperbe
3uUt8ZFCVeRiROWtx9WQgCVDDWztlrYzfEo/lFtNKjdseiYDj1/1gG6S2x5/S5dP
LzEndNYiVEB2q9XrocrdKVumr7EqNe7C/AMNxuyfAoU2QV5SRyDDSRa1muYwHBa7
x2XIThMS2tQLdN9bjJGcT653DKoq0Qwf1uMAOdHuqBLNXOpZw8PG8d3xmVHCyB4u
rr5E48L2DmD0TwY3YBjfb3KCw/r/CvT1/cWkCpO90aNmSS4JMuOBiFlKaERMvXcw
Ffo0ErZWwlgDN40hQO7xySxI3Paz6/QbxXunVnFQkTclkcQG63K6nWO9fMtgxRaZ
ZGsv/jdWUZ6fBvOvW09zLvF2ZXKhTbfwU47C+2TefvENVz4rTAJcGgtF1wiFv1Lt
0XT2FeJ6/jVGhk6745cHgOhsxqamOTuQ/y948ViMmR037INHouazD8dWHkjOY45d
hy0DjRGIiig94/r+b7YZn81QUkk0HddyH4zi18f5Lx+ExiLDKaLqLv57PQ3ZCeBy
v6Hoq/5tpZWCdXkLIAHx/a7ltiJQlyRUr8QcKPcGfr/qvIcYsUocHZ3iwkhxCnZ9
77E2f/Owf8VaS3x4g5V6RYNlkhuqVixLq/3QyphykcqDK2g4PnWfq8prGY97jHXN
+LZdwwV8LJkkoxCG1aehPlvtpGYGS75aeU1iFbqfke+gF2JG4LJZQsl1dAoL8R+X
ZdILuN+182CpwwptK4QmCvSWXk/ZJtYK9e6OtE1keLM4oQW122fxwyVkEnnAR2/f
Qc2U3WLk/UZSuhcHExxreWP4kiN8cYgSpw+k2uk7Xuw2kelu6hv6UzB4EtL8Xy4O
kK7y6EjCkRJVqQoZ3vVny5408edc7Kh6bz0P5G4LIaCpvrcCYOJv2f57/lr8dMM6
XcoZ0YIXATdUzKzhXKNFiib7SzBwFRRD+jMwzLnNeUhss8IUVl5LYwD6sOWbhdep
LtUO2eXa74i6pa7sz1PLLWDZQ64f9fVPX7EEBy2LBVP3iLqLd2x/OHw+s7pTPT0G
EgRpW0+IYBZGQjGN9s1VXXyhTm3RL03KeYls7aHmmcVqDqvozarqplN/kAhSGPmD
N99FlSrIfihCEZlXO4iP2LRkJaFy11mU0ZvIMkDJ51fNO8PWypca4Rgi4azMCaiR
HW/dtSBH2N8St0G80c2gUKXRmmJWFqebUIk4000VrpDcZlg0INC2unY0NwRFDe2a
55/NR7TO9GdhWYDWsZedRatUFul5DWznncIfrXAD3T32gUR3I9zc62OFVsH2Dve3
oMZUabvnj3g1sQqiRgJyIe5aVZljgXMh0cdWjcUi34vOVBU8yOU921/jSinJWyH7
GxyNlS3BiKQ22CLvbjc=
-----END ENCRYPTED PRIVATE KEY-----';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS8RC4()
{
// openssl pkcs8 -in private.pem -topk8 -v1 PBE-SHA1-RC4-128 -out enckey.pem
// EncryptionAlgorithm: pbeWithSHAAnd128BitRC4
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE5DAcBgoqhkiG9w0BDAEBMA4ECKQOMmQxsqe0AgIIAASCBMJ86iav9sz/nsgC
KrPC1m7fPDuJF1BCPd6Yu+b+D4++4htITNBujK+Ur0xgQnfs7+Et8/cz521KR1zp
kalr56rqvPu9X45l86f/PYv6D+3660jxAadk1bZ9Y7nXzjXsFlZljEi/oLYSAKwh
rn88ZNBM7hwoEtEZJXOK7yZlcpfLuNyRJhfxRp893yeG3SHDN+SAKzqbjrGtPnJY
2X0Y/KidhYAYLi3onhxkq6I9aEI54oBZUiKLHhRD5/ASx8EeSPK2Ydj20PfDIRIk
t75Tlqn5eLC124xdO0rm/vrczIrzo+JaqLq8dO0T7PGrR7hv9OyFwM6ssfzl2MyT
Si4Yv3gBk6dUQ4lySj6XfscjEPwnUSjO3SMwAV0uBoBxDyeKg+58sT0e4Ow7k+6U
SFoqa2m+gBAjXzL8SaGfvjfy0ViBtgLycGrK80dp7k0L5pJAZou7WCPWlP+5+kIl
IprSGD1luOm1olQBSaQO+GkhQlMg4jK7cMKM2bRWyT2ibq8KZujlhWlcqXbbaqSh
nJdadTfAsaCf3/hK1fiFwwSyFbiPjIE1H+WS+JMcg826S7FzoZ3BzcEVbiHRsBXy
PS95ZM3v/HWOejEO44NEqnrwjyqBlSJXOK2WLOUWWlf6t8pdQEjA1xUfJARXqv16
rQEXq2ZTGBOGeorwKLeUNgMQS7SfVP56Mmi23A3QQk7JNPXfkcrQscHu3mzesYKA
+ckJwDsyjnTwYWFXfDxfReKVA17YSV160oKCPhO7jIeiHO8azw7RKaaIaueKe5QU
boKWPAKeEfsDrSxtEYxy6hQ/45LYB+gUlAauaFlT4d0qMWQzt05Zs4ugUtx8SuI3
hWB80fi8XEJajti/3JIg0+cDEmv9XtYQXpaFKR/gTHl1ReSscY/rNyiUc7t1dBsn
kAwMhk/7p/0MZdEpG0e3qQ9Fs2pELlShzORobM0HWd41d2BkW54W/TJ9ERJhMU9Q
NJ5KZDukkCdTIgvnPKJ/50byYVGtt8VBXDzMQsm5ex9yDkEmBLtc14z7UaGd7FCK
xmbcVfkf+h5GPuJqXiZv9RsOfV0eVXlNx7jQ8Pq3FM5EiL8Wtj1XB3+cpFkPREoC
lA9enCZNdjXPB4SSz4kF+UwWdaNS77SGXDq4NQRT/cu4mce+1VPjepEc1WzLw5m+
aaHtJJCdLhaUJmYfaPGz4kg2CSdFCDjzDLOQCOwGtqAY6667ZOFb2VCukQR2aSfK
XJF4Br7UsKhtlZvRDZGLSdxS/6IPe3KgzInP+27kLpv5UcolD3GfuS9WZhfa7tlB
37n5nyGJCgVHufWRrYdKPI32Dn4R312/k+6X9zR1G4FlYzbuA+g2Q5CX8n5e/9jm
1WjCv8ppB3p3BjIv8iEAyfPShwe4uk2ohry+nY/pq7qYl3E7Y6vS1MOmRJT5jvBA
oCWQITjRu/d0xocYp6agkMEBgkyiqzLEW8PV2bziRZVGsYvC/4ky1MVERFO9jiSk
3L6Xllp1yB+Mw9y12bUhDAZNAqpkNtL0CJbLKh6fQ7x6l4d0t/QqpuKXPvF5l0wr
+Fb131STrh7fkiTT1glrra1UhJzz/KVOR+TG32GOSI0hOTqu4/gDQ/vUV0gh0SJw
OvndKFWbSnE=
-----END ENCRYPTED PRIVATE KEY-----';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
public function testPKCS8RC440()
{
// openssl pkcs8 -in private.pem -topk8 -v1 PBE-SHA1-RC4-40 -out enckey.pem
// EncryptionAlgorithm: pbeWithSHAAnd40BitRC4
$key = '-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE5DAcBgoqhkiG9w0BDAECMA4ECNrUHnZnezluAgIIAASCBMKm0lEML1hIdzcu
pglVZSwG8JyigX3xgHwnN4oAz6HYTNeatkV3xqGP23DP84e9En7mQTRxuQ9Tk8sN
NoEK2VsjYmrJtsbyTH21M0vPlAvFv/sIgxVYcF7jv58jHhLNnibPtDeMXZDjd4uq
vdppAGRxIr5z8XZ8ruAlsAB/xzcz1bIK8CwWH9NBOWLDe195/OYoUrgf9h+U2s8e
1rbq+U/woJZHD7+3RuPmtWTtrneY/NVTiU03OUUleys88eCSqZrQzR0faNxdVzTg
mhSrtkqwho9xxUFY6KqLOTF4BQufD+ZSNt9LN3EaFdvGcI1QWWDH11ne98oz3GUa
GCGhaADTOAAOdvXyv+6YRfj1VvtisUeiGjttFmaUHGOmHCCYoyVkbhsRq4QnbbCv
641ogRbuISBHwr+mzqjwTZXC5Laxsrn5EnCZ309vohq7l+g3M1Y9nzR8hOsiqTFu
7PPj1jYM8znYkVx/me+xnpB/d3Ot86K6NszbTaWk9cHr4qfkF+pu2kUYQ/26CUBE
y7DxYmhXnOBRGUTvQebrMoSK8hOaw0uWEQtYp3gLOS1hituL965m2qRbP/ysDP85
DAorOSbKDEMHYy7UP3xh743FErEOoY83GtugnJgjrTlJ/5NyS1KFr5QUsQD/N/Zw
bIkjdFT5mjWVaotHzpNc1IigpAPbNpe4J1E1E2nB8YE5ckSEVseUJ+ypgWSvJxmU
G68YvidODClnBekff8sRDCNN4dekQgnNEMbAWgHRWtMERvXS/9xfJZiqiq+7WvIE
Xvu1Qq5zG3+mESNX9AVLngv5btD2m6QFEqOLG9JKQWp61J3c2lG/kdtWBjsXiWoi
zvkA4u9ZxUzX3s3T2aHozg9O4+0ti947l7wSIxbxLYA0d1M7cQoeKAuRnpwzfCZ9
gpQ9VG9acDhU9LCxcZBHfuKROeI7D7wL//MJp/ue26uhOZY7Z0gbFIfjeSPW+HD0
fRGA849/1aKIsRarKg2YleqsXO04E3J/lpTt1gjy3aGE25Arq6qo+4DRsUIIWeS+
QwzdDeqy1zs8BIPxa51U/jvbqxCvqXsMw4la0txkSwymMvc6U+QpJgm2KqSDCs8W
+QYIz4SYlADLgl+MVDGd9IB/PN8AIZ0Lr7QqKKBIrfyegO/gjCkHCdNIh1Q/Bzbf
rq8AYwbxHnp2Jn2MAzw9s13ncENpZqCDHkhmd89hJc1B4f8rv5KhDsIVb85XQJek
pdpqugcYjxohSBEa9yzp0JDRa97Btir7D4+9HG2NUullFgXvbqKvlKPj+ORUDxJd
DMGC2Uov1koiVBvvahtmr8eTBNdA48cA7l/c5t8UsGbjrwpqLZLTJ1FHjnVKybuu
soPwPAxr3WBE4Ien7WqPj+GTeLWMb9//kpi5grguv3Db6rdH2Y4PT9Fi4UBxd+6N
LqB1rPkt4AQtQwda1ccixYXIFfWSJ6+XEyp6/wsW05DZAiu3R4o/T9Z59KPGlbf0
aaEAW+FZ9jYa6sDBlMwCN2TEmnBFkytJYe8+B5UxkEAIn3g/Vr9R4t4YDCSE2ugs
q6YJC1bQ8jHojcWTs47zcefCXhOkKOg3oxzYIQe9Ikdmf70JxIo+bS92O2vrkV0p
OFLPBrLe4Hw=
-----END ENCRYPTED PRIVATE KEY-----';
$pass = 'asdf';
$this->pkcs8tester($key, $pass);
}
}