/** * Save Certificate Revocation List. * * @param array $crl * @param int $format optional * @access public * @return string */ function saveCRL($crl, $format = self::FORMAT_PEM) { if (!is_array($crl) || !isset($crl['tbsCertList'])) { return false; } $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['tbsCertList']['issuer']['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $filters['tbsCertList']['signature']['parameters'] = array('type' => ASN1::TYPE_UTF8_STRING); $filters['signatureAlgorithm']['parameters'] = array('type' => ASN1::TYPE_UTF8_STRING); if (empty($crl['tbsCertList']['signature']['parameters'])) { $filters['tbsCertList']['signature']['parameters'] = array('type' => ASN1::TYPE_NULL); } if (empty($crl['signatureAlgorithm']['parameters'])) { $filters['signatureAlgorithm']['parameters'] = array('type' => ASN1::TYPE_NULL); } $asn1->loadFilters($filters); $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); if (is_array($rclist)) { foreach ($rclist as $i => $extension) { $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1); } } $crl = $asn1->encodeDER($crl, $this->CertificateList); switch ($format) { case self::FORMAT_DER: return $crl; // case self::FORMAT_PEM: default: return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; } }
/** * 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], '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-----"; }
/** * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) * * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. * This means that under rare conditions you can have a perfectly valid v1.5 signature * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends * that if you're going to validate these types of signatures you "should indicate * whether the underlying BER encoding is a DER encoding and hence whether the signature * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of * RSA::PADDING_PKCS1... that means BER encoding was used. * * @access private * @param string $m * @param string $s * @return bool */ function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { return false; } // RSA verification $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { return false; } $em = $this->_i2osp($m2, $this->k); if ($em === false) { return false; } if ($this->_string_shift($em, 2) != "") { return false; } $em = ltrim($em, "ÿ"); if ($this->_string_shift($em) != "") { return false; } $asn1 = new ASN1(); $decoded = $asn1->decodeBER($em); if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { return false; } $AlgorithmIdentifier = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'parameters' => array('type' => ASN1::TYPE_ANY, 'optional' => true))); $DigestInfo = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('digestAlgorithm' => $AlgorithmIdentifier, 'digest' => array('type' => ASN1::TYPE_OCTET_STRING))); $oids = array('1.2.840.113549.2.2' => 'md2', '1.2.840.113549.2.4' => 'md4', '1.2.840.113549.2.5' => 'md5', '1.3.14.3.2.26' => 'sha1', '2.16.840.1.101.3.4.2.1' => 'sha256', '2.16.840.1.101.3.4.2.2' => 'sha384', '2.16.840.1.101.3.4.2.3' => 'sha512'); $asn1->loadOIDs($oids); $decoded = $asn1->asn1map($decoded[0], $DigestInfo); if (!isset($decoded) || $decoded === false) { return false; } if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { return false; } $hash = new Hash($decoded['digestAlgorithm']['algorithm']); $em = $hash->hash($m); $em2 = Base64::decode($decoded['digest']); return $this->_equals($em, $em2); }