Вход Регистрация
Файл: oc-includes/phpseclib/File/X509.php
Строк: 4113
<?php

/**
 * Pure-PHP X.509 Parser
 *
 * PHP versions 4 and 5
 *
 * Encode and decode X.509 certificates.
 *
 * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
 * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
 *
 * Note that loading an X.509 certificate and resaving it may invalidate the signature.  The reason being that the signature is based on a
 * portion of the certificate that contains optional parameters with default values.  ie. if the parameter isn't there the default value is
 * used.  Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can
 * be encoded.  It can be encoded explicitly or left out all together.  This would effect the signature value and thus may invalidate the
 * the certificate all together unless the certificate is re-signed.
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  File
 * @package   File_X509
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include File_ASN1
 */
if (!class_exists('File_ASN1')) {
    include_once 
'ASN1.php';
}

/**
 * Flag to only accept signatures signed by certificate authorities
 *
 * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs
 *
 * @access public
 */
define('FILE_X509_VALIDATE_SIGNATURE_BY_CA'1);

/**#@+
 * @access public
 * @see File_X509::getDN()
 */
/**
 * Return internal array representation
 */
define('FILE_X509_DN_ARRAY'0);
/**
 * Return string
 */
define('FILE_X509_DN_STRING'1);
/**
 * Return ASN.1 name string
 */
define('FILE_X509_DN_ASN1'2);
/**
 * Return OpenSSL compatible array
 */
define('FILE_X509_DN_OPENSSL'3);
/**
 * Return canonical ASN.1 RDNs string
 */
define('FILE_X509_DN_CANON'4);
/**
 * Return name hash for file indexing
 */
define('FILE_X509_DN_HASH'5);
/**#@-*/

/**#@+
 * @access public
 * @see File_X509::saveX509()
 * @see File_X509::saveCSR()
 * @see File_X509::saveCRL()
 */
/**
 * Save as PEM
 *
 * ie. a base64-encoded PEM with a header and a footer
 */
define('FILE_X509_FORMAT_PEM'0);
/**
 * Save as DER
 */
define('FILE_X509_FORMAT_DER'1);
/**
 * Save as a SPKAC
 *
 * Only works on CSRs. Not currently supported.
 */
define('FILE_X509_FORMAT_SPKAC'2);
/**#@-*/

/**
 * Attribute value disposition.
 * If disposition is >= 0, this is the index of the target value.
 */
define('FILE_X509_ATTR_ALL', -1); // All attribute values (array).
define('FILE_X509_ATTR_APPEND', -2); // Add a value.
define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value.

/**
 * Pure-PHP X.509 Parser
 *
 * @package File_X509
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class File_X509
{
    
/**
     * ASN.1 syntax for X.509 certificates
     *
     * @var Array
     * @access private
     */
    
var $Certificate;

    
/**#@+
     * ASN.1 syntax for various extensions
     *
     * @access private
     */
    
var $DirectoryString;
    var 
$PKCS9String;
    var 
$AttributeValue;
    var 
$Extensions;
    var 
$KeyUsage;
    var 
$ExtKeyUsageSyntax;
    var 
$BasicConstraints;
    var 
$KeyIdentifier;
    var 
$CRLDistributionPoints;
    var 
$AuthorityKeyIdentifier;
    var 
$CertificatePolicies;
    var 
$AuthorityInfoAccessSyntax;
    var 
$SubjectAltName;
    var 
$PrivateKeyUsagePeriod;
    var 
$IssuerAltName;
    var 
$PolicyMappings;
    var 
$NameConstraints;

    var 
$CPSuri;
    var 
$UserNotice;

    var 
$netscape_cert_type;
    var 
$netscape_comment;
    var 
$netscape_ca_policy_url;

    var 
$Name;
    var 
$RelativeDistinguishedName;
    var 
$CRLNumber;
    var 
$CRLReason;
    var 
$IssuingDistributionPoint;
    var 
$InvalidityDate;
    var 
$CertificateIssuer;
    var 
$HoldInstructionCode;
    var 
$SignedPublicKeyAndChallenge;
    
/**#@-*/

    /**
     * ASN.1 syntax for Certificate Signing Requests (RFC2986)
     *
     * @var Array
     * @access private
     */
    
var $CertificationRequest;

    
/**
     * ASN.1 syntax for Certificate Revocation Lists (RFC5280)
     *
     * @var Array
     * @access private
     */
    
var $CertificateList;

    
/**
     * Distinguished Name
     *
     * @var Array
     * @access private
     */
    
var $dn;

    
/**
     * Public key
     *
     * @var String
     * @access private
     */
    
var $publicKey;

    
/**
     * Private key
     *
     * @var String
     * @access private
     */
    
var $privateKey;

    
/**
     * Object identifiers for X.509 certificates
     *
     * @var Array
     * @access private
     * @link http://en.wikipedia.org/wiki/Object_identifier
     */
    
var $oids;

    
/**
     * The certificate authorities
     *
     * @var Array
     * @access private
     */
    
var $CAs;

    
/**
     * The currently loaded certificate
     *
     * @var Array
     * @access private
     */
    
var $currentCert;

    
/**
     * The signature subject
     *
     * There's no guarantee File_X509 is going to reencode an X.509 cert in the same way it was originally
     * encoded so we take save the portion of the original cert that the signature would have made for.
     *
     * @var String
     * @access private
     */
    
var $signatureSubject;

    
/**
     * Certificate Start Date
     *
     * @var String
     * @access private
     */
    
var $startDate;

    
/**
     * Certificate End Date
     *
     * @var String
     * @access private
     */
    
var $endDate;

    
/**
     * Serial Number
     *
     * @var String
     * @access private
     */
    
var $serialNumber;

    
/**
     * Key Identifier
     *
     * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and
     * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
     *
     * @var String
     * @access private
     */
    
var $currentKeyIdentifier;

    
/**
     * CA Flag
     *
     * @var Boolean
     * @access private
     */
    
var $caFlag false;

    
/**
     * SPKAC Challenge
     *
     * @var String
     * @access private
     */
    
var $challenge;

    
/**
     * Default Constructor.
     *
     * @return File_X509
     * @access public
     */
    
function File_X509()
    {
        if (!
class_exists('Math_BigInteger')) {
            include_once 
'Math/BigInteger.php';
        }

        
// Explicitly Tagged Module, 1988 Syntax
        // http://tools.ietf.org/html/rfc5280#appendix-A.1

        
$this->DirectoryString = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'teletexString'   => array('type' => FILE_ASN1_TYPE_TELETEX_STRING),
                
'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
                
'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING),
                
'utf8String'      => array('type' => FILE_ASN1_TYPE_UTF8_STRING),
                
'bmpString'       => array('type' => FILE_ASN1_TYPE_BMP_STRING)
            )
        );

        
$this->PKCS9String = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'ia5String'       => array('type' => FILE_ASN1_TYPE_IA5_STRING),
                
'directoryString' => $this->DirectoryString
            
)
        );

        
$this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY);

        
$AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        
$AttributeTypeAndValue = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'type' => $AttributeType,
                
'value'=> $this->AttributeValue
            
)
        );

        
/*
        In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
        but they can be useful at times when either there is no unique attribute in the entry or you
        want to ensure that the entry's DN contains some useful identifying information.

        - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
        */
        
$this->RelativeDistinguishedName = array(
            
'type'     => FILE_ASN1_TYPE_SET,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $AttributeTypeAndValue
        
);

        
// http://tools.ietf.org/html/rfc5280#section-4.1.2.4
        
$RDNSequence = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
// RDNSequence does not define a min or a max, which means it doesn't have one
            
'min'      => 0,
            
'max'      => -1,
            
'children' => $this->RelativeDistinguishedName
        
);

        
$this->Name = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'rdnSequence' => $RDNSequence
            
)
        );

        
// http://tools.ietf.org/html/rfc5280#section-4.1.1.2
        
$AlgorithmIdentifier = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'algorithm'  => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                
'parameters' => array(
                                    
'type'     => FILE_ASN1_TYPE_ANY,
                                    
'optional' => true
                                
)
            )
        );

        
/*
           A certificate using system MUST reject the certificate if it encounters
           a critical extension it does not recognize; however, a non-critical
           extension may be ignored if it is not recognized.

           http://tools.ietf.org/html/rfc5280#section-4.2
        */
        
$Extension = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'extnId'   => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                
'critical' => array(
                                  
'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                  
'optional' => true,
                                  
'default'  => false
                              
),
                
'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING)
            )
        );

        
$this->Extensions = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
// technically, it's MAX, but we'll assume anything < 0 is MAX
            
'max'      => -1,
            
// if 'children' isn't an array then 'min' and 'max' must be defined
            
'children' => $Extension
        
);

        
$SubjectPublicKeyInfo = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'algorithm'        => $AlgorithmIdentifier,
                
'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        
$UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING);

        
$Time = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'utcTime'     => array('type' => FILE_ASN1_TYPE_UTC_TIME),
                
'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
            )
        );

        
// http://tools.ietf.org/html/rfc5280#section-4.1.2.5
        
$Validity = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'notBefore' => $Time,
                
'notAfter'  => $Time
            
)
        );

        
$CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER);

        
$Version = array(
            
'type'    => FILE_ASN1_TYPE_INTEGER,
            
'mapping' => array('v1''v2''v3')
        );

        
// assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
        
$TBSCertificate = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
// technically, default implies optional, but we'll define it as being optional, none-the-less, just to
                // reenforce that fact
                
'version'             => array(
                                             
'constant' => 0,
                                             
'optional' => true,
                                             
'explicit' => true,
                                             
'default'  => 'v1'
                                         
) + $Version,
                
'serialNumber'         => $CertificateSerialNumber,
                
'signature'            => $AlgorithmIdentifier,
                
'issuer'               => $this->Name,
                
'validity'             => $Validity,
                
'subject'              => $this->Name,
                
'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
                
// implicit means that the T in the TLV structure is to be rewritten, regardless of the type
                
'issuerUniqueID'       => array(
                                               
'constant' => 1,
                                               
'optional' => true,
                                               
'implicit' => true
                                           
) + $UniqueIdentifier,
                
'subjectUniqueID'       => array(
                                               
'constant' => 2,
                                               
'optional' => true,
                                               
'implicit' => true
                                           
) + $UniqueIdentifier,
                
// <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
                // it's not IMPLICIT, it's EXPLICIT
                
'extensions'            => array(
                                               
'constant' => 3,
                                               
'optional' => true,
                                               
'explicit' => true
                                           
) + $this->Extensions
            
)
        );

        
$this->Certificate = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                 
'tbsCertificate'     => $TBSCertificate,
                 
'signatureAlgorithm' => $AlgorithmIdentifier,
                 
'signature'          => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        
$this->KeyUsage = array(
            
'type'    => FILE_ASN1_TYPE_BIT_STRING,
            
'mapping' => array(
                
'digitalSignature',
                
'nonRepudiation',
                
'keyEncipherment',
                
'dataEncipherment',
                
'keyAgreement',
                
'keyCertSign',
                
'cRLSign',
                
'encipherOnly',
                
'decipherOnly'
            
)
        );

        
$this->BasicConstraints = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'cA'                => array(
                                                 
'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                 
'optional' => true,
                                                 
'default'  => false
                                       
),
                
'pathLenConstraint' => array(
                                                 
'type' => FILE_ASN1_TYPE_INTEGER,
                                                 
'optional' => true
                                       
)
            )
        );

        
$this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING);

        
$OrganizationalUnitNames = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => 4// ub-organizational-units
            
'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
        );

        
$PersonalName = array(
            
'type'     => FILE_ASN1_TYPE_SET,
            
'children' => array(
                
'surname'              => array(
                                           
'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           
'constant' => 0,
                                           
'optional' => true,
                                           
'implicit' => true
                                         
),
                
'given-name'           => array(
                                           
'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           
'constant' => 1,
                                           
'optional' => true,
                                           
'implicit' => true
                                         
),
                
'initials'             => array(
                                           
'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           
'constant' => 2,
                                           
'optional' => true,
                                           
'implicit' => true
                                         
),
                
'generation-qualifier' => array(
                                           
'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           
'constant' => 3,
                                           
'optional' => true,
                                           
'implicit' => true
                                         
)
            )
        );

        
$NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);

        
$OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);

        
$PrivateDomainName = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'numeric'   => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
                
'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        
$TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);

        
$NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);

        
$AdministrationDomainName = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
// if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
            // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
            
'class'    => FILE_ASN1_CLASS_APPLICATION,
            
'cast'     => 2,
            
'children' => array(
                
'numeric'   => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
                
'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        
$CountryName = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
// if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
            // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
            
'class'    => FILE_ASN1_CLASS_APPLICATION,
            
'cast'     => 1,
            
'children' => array(
                
'x121-dcc-code'        => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
                
'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        
$AnotherName = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                 
'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                 
'value'   => array(
                                  
'type' => FILE_ASN1_TYPE_ANY,
                                  
'constant' => 0,
                                  
'optional' => true,
                                  
'explicit' => true
                              
)
            )
        );

        
$ExtensionAttribute = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                 
'extension-attribute-type'  => array(
                                                    
'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                                    
'constant' => 0,
                                                    
'optional' => true,
                                                    
'implicit' => true
                                                
),
                 
'extension-attribute-value' => array(
                                                    
'type' => FILE_ASN1_TYPE_ANY,
                                                    
'constant' => 1,
                                                    
'optional' => true,
                                                    
'explicit' => true
                                                
)
            )
        );

        
$ExtensionAttributes = array(
            
'type'     => FILE_ASN1_TYPE_SET,
            
'min'      => 1,
            
'max'      => 256// ub-extension-attributes
            
'children' => $ExtensionAttribute
        
);

        
$BuiltInDomainDefinedAttribute = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                 
'type'  => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
                 
'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        
$BuiltInDomainDefinedAttributes = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => 4// ub-domain-defined-attributes
            
'children' => $BuiltInDomainDefinedAttribute
        
);

        
$BuiltInStandardAttributes =  array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'country-name'               => array('optional' => true) + $CountryName,
                
'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
                
'network-address'            => array(
                                                 
'constant' => 0,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $NetworkAddress,
                
'terminal-identifier'        => array(
                                                 
'constant' => 1,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $TerminalIdentifier,
                
'private-domain-name'        => array(
                                                 
'constant' => 2,
                                                 
'optional' => true,
                                                 
'explicit' => true
                                               
) + $PrivateDomainName,
                
'organization-name'          => array(
                                                 
'constant' => 3,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $OrganizationName,
                
'numeric-user-identifier'    => array(
                                                 
'constant' => 4,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $NumericUserIdentifier,
                
'personal-name'              => array(
                                                 
'constant' => 5,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $PersonalName,
                
'organizational-unit-names'  => array(
                                                 
'constant' => 6,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $OrganizationalUnitNames
            
)
        );

        
$ORAddress = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                 
'built-in-standard-attributes'       => $BuiltInStandardAttributes,
                 
'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
                 
'extension-attributes'               => array('optional' => true) + $ExtensionAttributes
            
)
        );

        
$EDIPartyName = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                 
'nameAssigner' => array(
                                    
'constant' => 0,
                                    
'optional' => true,
                                    
'implicit' => true
                                
) + $this->DirectoryString,
                 
// partyName is technically required but File_ASN1 doesn't currently support non-optional constants and
                 // setting it to optional gets the job done in any event.
                 
'partyName'    => array(
                                    
'constant' => 1,
                                    
'optional' => true,
                                    
'implicit' => true
                                
) + $this->DirectoryString
            
)
        );

        
$GeneralName = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'otherName'                 => array(
                                                 
'constant' => 0,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $AnotherName,
                
'rfc822Name'                => array(
                                                 
'type' => FILE_ASN1_TYPE_IA5_STRING,
                                                 
'constant' => 1,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
),
                
'dNSName'                   => array(
                                                 
'type' => FILE_ASN1_TYPE_IA5_STRING,
                                                 
'constant' => 2,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
),
                
'x400Address'               => array(
                                                 
'constant' => 3,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $ORAddress,
                
'directoryName'             => array(
                                                 
'constant' => 4,
                                                 
'optional' => true,
                                                 
'explicit' => true
                                               
) + $this->Name,
                
'ediPartyName'              => array(
                                                 
'constant' => 5,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $EDIPartyName,
                
'uniformResourceIdentifier' => array(
                                                 
'type' => FILE_ASN1_TYPE_IA5_STRING,
                                                 
'constant' => 6,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
),
                
'iPAddress'                 => array(
                                                 
'type' => FILE_ASN1_TYPE_OCTET_STRING,
                                                 
'constant' => 7,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
),
                
'registeredID'              => array(
                                                 
'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER,
                                                 
'constant' => 8,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
)
            )
        );

        
$GeneralNames = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $GeneralName
        
);

        
$this->IssuerAltName $GeneralNames;

        
$ReasonFlags = array(
            
'type'    => FILE_ASN1_TYPE_BIT_STRING,
            
'mapping' => array(
                
'unused',
                
'keyCompromise',
                
'cACompromise',
                
'affiliationChanged',
                
'superseded',
                
'cessationOfOperation',
                
'certificateHold',
                
'privilegeWithdrawn',
                
'aACompromise'
            
)
        );

        
$DistributionPointName = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'fullName'                => array(
                                                 
'constant' => 0,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                       
) + $GeneralNames,
                
'nameRelativeToCRLIssuer' => array(
                                                 
'constant' => 1,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                       
) + $this->RelativeDistinguishedName
            
)
        );

        
$DistributionPoint = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'distributionPoint' => array(
                                                 
'constant' => 0,
                                                 
'optional' => true,
                                                 
'explicit' => true
                                       
) + $DistributionPointName,
                
'reasons'           => array(
                                                 
'constant' => 1,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                       
) + $ReasonFlags,
                
'cRLIssuer'         => array(
                                                 
'constant' => 2,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                       
) + $GeneralNames
            
)
        );

        
$this->CRLDistributionPoints = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $DistributionPoint
        
);

        
$this->AuthorityKeyIdentifier = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'keyIdentifier'             => array(
                                                 
'constant' => 0,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $this->KeyIdentifier,
                
'authorityCertIssuer'       => array(
                                                 
'constant' => 1,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $GeneralNames,
                
'authorityCertSerialNumber' => array(
                                                 
'constant' => 2,
                                                 
'optional' => true,
                                                 
'implicit' => true
                                               
) + $CertificateSerialNumber
            
)
        );

        
$PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        
$PolicyQualifierInfo = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'policyQualifierId' => $PolicyQualifierId,
                
'qualifier'         => array('type' => FILE_ASN1_TYPE_ANY)
            )
        );

        
$CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        
$PolicyInformation = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'policyIdentifier' => $CertPolicyId,
                
'policyQualifiers' => array(
                                          
'type'     => FILE_ASN1_TYPE_SEQUENCE,
                                          
'min'      => 0,
                                          
'max'      => -1,
                                          
'optional' => true,
                                          
'children' => $PolicyQualifierInfo
                                      
)
            )
        );

        
$this->CertificatePolicies = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $PolicyInformation
        
);

        
$this->PolicyMappings = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => array(
                              
'type'     => FILE_ASN1_TYPE_SEQUENCE,
                              
'children' => array(
                                  
'issuerDomainPolicy' => $CertPolicyId,
                                  
'subjectDomainPolicy' => $CertPolicyId
                              
)
                       )
        );

        
$KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        
$this->ExtKeyUsageSyntax = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $KeyPurposeId
        
);

        
$AccessDescription = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'accessMethod'   => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                
'accessLocation' => $GeneralName
            
)
        );

        
$this->AuthorityInfoAccessSyntax = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $AccessDescription
        
);

        
$this->SubjectAltName $GeneralNames;

        
$this->PrivateKeyUsagePeriod = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'notBefore' => array(
                                                 
'constant' => 0,
                                                 
'optional' => true,
                                                 
'implicit' => true,
                                                 
'type' => FILE_ASN1_TYPE_GENERALIZED_TIME),
                
'notAfter'  => array(
                                                 
'constant' => 1,
                                                 
'optional' => true,
                                                 
'implicit' => true,
                                                 
'type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
            )
        );

        
$BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER);

        
$GeneralSubtree = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'base'    => $GeneralName,
                
'minimum' => array(
                                 
'constant' => 0,
                                 
'optional' => true,
                                 
'implicit' => true,
                                 
'default' => new Math_BigInteger(0)
                             ) + 
$BaseDistance,
                
'maximum' => array(
                                 
'constant' => 1,
                                 
'optional' => true,
                                 
'implicit' => true,
                             ) + 
$BaseDistance
            
)
        );

        
$GeneralSubtrees = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $GeneralSubtree
        
);

        
$this->NameConstraints = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'permittedSubtrees' => array(
                                           
'constant' => 0,
                                           
'optional' => true,
                                           
'implicit' => true
                                       
) + $GeneralSubtrees,
                
'excludedSubtrees'  => array(
                                           
'constant' => 1,
                                           
'optional' => true,
                                           
'implicit' => true
                                       
) + $GeneralSubtrees
            
)
        );

        
$this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING);

        
$DisplayText = array(
            
'type'     => FILE_ASN1_TYPE_CHOICE,
            
'children' => array(
                
'ia5String'     => array('type' => FILE_ASN1_TYPE_IA5_STRING),
                
'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING),
                
'bmpString'     => array('type' => FILE_ASN1_TYPE_BMP_STRING),
                
'utf8String'    => array('type' => FILE_ASN1_TYPE_UTF8_STRING)
            )
        );

        
$NoticeReference = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'organization'  => $DisplayText,
                
'noticeNumbers' => array(
                                       
'type'     => FILE_ASN1_TYPE_SEQUENCE,
                                       
'min'      => 1,
                                       
'max'      => 200,
                                       
'children' => array('type' => FILE_ASN1_TYPE_INTEGER)
                                   )
            )
        );

        
$this->UserNotice = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'noticeRef' => array(
                                           
'optional' => true,
                                           
'implicit' => true
                                       
) + $NoticeReference,
                
'explicitText'  => array(
                                           
'optional' => true,
                                           
'implicit' => true
                                       
) + $DisplayText
            
)
        );

        
// mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
        
$this->netscape_cert_type = array(
            
'type'    => FILE_ASN1_TYPE_BIT_STRING,
            
'mapping' => array(
                
'SSLClient',
                
'SSLServer',
                
'Email',
                
'ObjectSigning',
                
'Reserved',
                
'SSLCA',
                
'EmailCA',
                
'ObjectSigningCA'
            
)
        );

        
$this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING);
        
$this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING);

        
// attribute is used in RFC2986 but we're using the RFC5280 definition

        
$Attribute = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'type' => $AttributeType,
                
'value'=> array(
                              
'type'     => FILE_ASN1_TYPE_SET,
                              
'min'      => 1,
                              
'max'      => -1,
                              
'children' => $this->AttributeValue
                          
)
            )
        );

        
// adapted from <http://tools.ietf.org/html/rfc2986>

        
$Attributes = array(
            
'type'     => FILE_ASN1_TYPE_SET,
            
'min'      => 1,
            
'max'      => -1,
            
'children' => $Attribute
        
);

        
$CertificationRequestInfo = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'version'       => array(
                                       
'type' => FILE_ASN1_TYPE_INTEGER,
                                       
'mapping' => array('v1')
                                   ),
                
'subject'       => $this->Name,
                
'subjectPKInfo' => $SubjectPublicKeyInfo,
                
'attributes'    => array(
                                       
'constant' => 0,
                                       
'optional' => true,
                                       
'implicit' => true
                                   
) + $Attributes,
            )
        );

        
$this->CertificationRequest = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'certificationRequestInfo' => $CertificationRequestInfo,
                
'signatureAlgorithm'       => $AlgorithmIdentifier,
                
'signature'                => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        
$RevokedCertificate = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                              
'userCertificate'    => $CertificateSerialNumber,
                              
'revocationDate'     => $Time,
                              
'crlEntryExtensions' => array(
                                                          
'optional' => true
                                                      
) + $this->Extensions
                          
)
        );

        
$TBSCertList = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'version'             => array(
                                             
'optional' => true,
                                             
'default'  => 'v1'
                                         
) + $Version,
                
'signature'           => $AlgorithmIdentifier,
                
'issuer'              => $this->Name,
                
'thisUpdate'          => $Time,
                
'nextUpdate'          => array(
                                             
'optional' => true
                                         
) + $Time,
                
'revokedCertificates' => array(
                                             
'type'     => FILE_ASN1_TYPE_SEQUENCE,
                                             
'optional' => true,
                                             
'min'      => 0,
                                             
'max'      => -1,
                                             
'children' => $RevokedCertificate
                                         
),
                
'crlExtensions'       => array(
                                             
'constant' => 0,
                                             
'optional' => true,
                                             
'explicit' => true
                                         
) + $this->Extensions
            
)
        );

        
$this->CertificateList = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'tbsCertList'        => $TBSCertList,
                
'signatureAlgorithm' => $AlgorithmIdentifier,
                
'signature'          => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        
$this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER);

        
$this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED,
           
'mapping' => array(
                            
'unspecified',
                            
'keyCompromise',
                            
'cACompromise',
                            
'affiliationChanged',
                            
'superseded',
                            
'cessationOfOperation',
                            
'certificateHold',
                            
// Value 7 is not used.
                            
=> 'removeFromCRL',
                            
'privilegeWithdrawn',
                            
'aACompromise'
            
)
        );

        
$this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'distributionPoint'          => array(
                                                    
'constant' => 0,
                                                    
'optional' => true,
                                                    
'explicit' => true
                                                
) + $DistributionPointName,
                
'onlyContainsUserCerts'      => array(
                                                    
'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    
'constant' => 1,
                                                    
'optional' => true,
                                                    
'default'  => false,
                                                    
'implicit' => true
                                                
),
                
'onlyContainsCACerts'        => array(
                                                    
'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    
'constant' => 2,
                                                    
'optional' => true,
                                                    
'default'  => false,
                                                    
'implicit' => true
                                                
),
                
'onlySomeReasons'           => array(
                                                    
'constant' => 3,
                                                    
'optional' => true,
                                                    
'implicit' => true
                                                
) + $ReasonFlags,
                
'indirectCRL'               => array(
                                                    
'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    
'constant' => 4,
                                                    
'optional' => true,
                                                    
'default'  => false,
                                                    
'implicit' => true
                                                
),
                
'onlyContainsAttributeCerts' => array(
                                                    
'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    
'constant' => 5,
                                                    
'optional' => true,
                                                    
'default'  => false,
                                                    
'implicit' => true
                                                
)
                          )
        );

        
$this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME);

        
$this->CertificateIssuer $GeneralNames;

        
$this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        
$PublicKeyAndChallenge = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'spki'      => $SubjectPublicKeyInfo,
                
'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING)
            )
        );

        
$this->SignedPublicKeyAndChallenge = array(
            
'type'     => FILE_ASN1_TYPE_SEQUENCE,
            
'children' => array(
                
'publicKeyAndChallenge' => $PublicKeyAndChallenge,
                
'signatureAlgorithm'    => $AlgorithmIdentifier,
                
'signature'             => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        
// OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
        
$this->oids = array(
            
'1.3.6.1.5.5.7' => 'id-pkix',
            
'1.3.6.1.5.5.7.1' => 'id-pe',
            
'1.3.6.1.5.5.7.2' => 'id-qt',
            
'1.3.6.1.5.5.7.3' => 'id-kp',
            
'1.3.6.1.5.5.7.48' => 'id-ad',
            
'1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
            
'1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
            
'1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
            
'1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
            
'1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
            
'1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
            
'2.5.4' => 'id-at',
            
'2.5.4.41' => 'id-at-name',
            
'2.5.4.4' => 'id-at-surname',
            
'2.5.4.42' => 'id-at-givenName',
            
'2.5.4.43' => 'id-at-initials',
            
'2.5.4.44' => 'id-at-generationQualifier',
            
'2.5.4.3' => 'id-at-commonName',
            
'2.5.4.7' => 'id-at-localityName',
            
'2.5.4.8' => 'id-at-stateOrProvinceName',
            
'2.5.4.10' => 'id-at-organizationName',
            
'2.5.4.11' => 'id-at-organizationalUnitName',
            
'2.5.4.12' => 'id-at-title',
            
'2.5.4.13' => 'id-at-description',
            
'2.5.4.46' => 'id-at-dnQualifier',
            
'2.5.4.6' => 'id-at-countryName',
            
'2.5.4.5' => 'id-at-serialNumber',
            
'2.5.4.65' => 'id-at-pseudonym',
            
'2.5.4.17' => 'id-at-postalCode',
            
'2.5.4.9' => 'id-at-streetAddress',
            
'2.5.4.45' => 'id-at-uniqueIdentifier',
            
'2.5.4.72' => 'id-at-role',

            
'0.9.2342.19200300.100.1.25' => 'id-domainComponent',
            
'1.2.840.113549.1.9' => 'pkcs-9',
            
'1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress',
            
'2.5.29' => 'id-ce',
            
'2.5.29.35' => 'id-ce-authorityKeyIdentifier',
            
'2.5.29.14' => 'id-ce-subjectKeyIdentifier',
            
'2.5.29.15' => 'id-ce-keyUsage',
            
'2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
            
'2.5.29.32' => 'id-ce-certificatePolicies',
            
'2.5.29.32.0' => 'anyPolicy',

            
'2.5.29.33' => 'id-ce-policyMappings',
            
'2.5.29.17' => 'id-ce-subjectAltName',
            
'2.5.29.18' => 'id-ce-issuerAltName',
            
'2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
            
'2.5.29.19' => 'id-ce-basicConstraints',
            
'2.5.29.30' => 'id-ce-nameConstraints',
            
'2.5.29.36' => 'id-ce-policyConstraints',
            
'2.5.29.31' => 'id-ce-cRLDistributionPoints',
            
'2.5.29.37' => 'id-ce-extKeyUsage',
            
'2.5.29.37.0' => 'anyExtendedKeyUsage',
            
'1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
            
'1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
            
'1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
            
'1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
            
'1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
            
'1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
            
'2.5.29.54' => 'id-ce-inhibitAnyPolicy',
            
'2.5.29.46' => 'id-ce-freshestCRL',
            
'1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
            
'1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
            
'2.5.29.20' => 'id-ce-cRLNumber',
            
'2.5.29.28' => 'id-ce-issuingDistributionPoint',
            
'2.5.29.27' => 'id-ce-deltaCRLIndicator',
            
'2.5.29.21' => 'id-ce-cRLReasons',
            
'2.5.29.29' => 'id-ce-certificateIssuer',
            
'2.5.29.23' => 'id-ce-holdInstructionCode',
            
'1.2.840.10040.2' => 'holdInstruction',
            
'1.2.840.10040.2.1' => 'id-holdinstruction-none',
            
'1.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
            
'1.2.840.10040.2.3' => 'id-holdinstruction-reject',
            
'2.5.29.24' => 'id-ce-invalidityDate',

            
'1.2.840.113549.2.2' => 'md2',
            
'1.2.840.113549.2.5' => 'md5',
            
'1.3.14.3.2.26' => 'id-sha1',
            
'1.2.840.10040.4.1' => 'id-dsa',
            
'1.2.840.10040.4.3' => 'id-dsa-with-sha1',
            
'1.2.840.113549.1.1' => 'pkcs-1',
            
'1.2.840.113549.1.1.1' => 'rsaEncryption',
            
'1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
            
'1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
            
'1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
            
'1.2.840.10046.2.1' => 'dhpublicnumber',
            
'2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
            
'1.2.840.10045' => 'ansi-X9-62',
            
'1.2.840.10045.4' => 'id-ecSigType',
            
'1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
            
'1.2.840.10045.1' => 'id-fieldType',
            
'1.2.840.10045.1.1' => 'prime-field',
            
'1.2.840.10045.1.2' => 'characteristic-two-field',
            
'1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
            
'1.2.840.10045.1.2.3.1' => 'gnBasis',
            
'1.2.840.10045.1.2.3.2' => 'tpBasis',
            
'1.2.840.10045.1.2.3.3' => 'ppBasis',
            
'1.2.840.10045.2' => 'id-publicKeyType',
            
'1.2.840.10045.2.1' => 'id-ecPublicKey',
            
'1.2.840.10045.3' => 'ellipticCurve',
            
'1.2.840.10045.3.0' => 'c-TwoCurve',
            
'1.2.840.10045.3.0.1' => 'c2pnb163v1',
            
'1.2.840.10045.3.0.2' => 'c2pnb163v2',
            
'1.2.840.10045.3.0.3' => 'c2pnb163v3',
            
'1.2.840.10045.3.0.4' => 'c2pnb176w1',
            
'1.2.840.10045.3.0.5' => 'c2pnb191v1',
            
'1.2.840.10045.3.0.6' => 'c2pnb191v2',
            
'1.2.840.10045.3.0.7' => 'c2pnb191v3',
            
'1.2.840.10045.3.0.8' => 'c2pnb191v4',
            
'1.2.840.10045.3.0.9' => 'c2pnb191v5',
            
'1.2.840.10045.3.0.10' => 'c2pnb208w1',
            
'1.2.840.10045.3.0.11' => 'c2pnb239v1',
            
'1.2.840.10045.3.0.12' => 'c2pnb239v2',
            
'1.2.840.10045.3.0.13' => 'c2pnb239v3',
            
'1.2.840.10045.3.0.14' => 'c2pnb239v4',
            
'1.2.840.10045.3.0.15' => 'c2pnb239v5',
            
'1.2.840.10045.3.0.16' => 'c2pnb272w1',
            
'1.2.840.10045.3.0.17' => 'c2pnb304w1',
            
'1.2.840.10045.3.0.18' => 'c2pnb359v1',
            
'1.2.840.10045.3.0.19' => 'c2pnb368w1',
            
'1.2.840.10045.3.0.20' => 'c2pnb431r1',
            
'1.2.840.10045.3.1' => 'primeCurve',
            
'1.2.840.10045.3.1.1' => 'prime192v1',
            
'1.2.840.10045.3.1.2' => 'prime192v2',
            
'1.2.840.10045.3.1.3' => 'prime192v3',
            
'1.2.840.10045.3.1.4' => 'prime239v1',
            
'1.2.840.10045.3.1.5' => 'prime239v2',
            
'1.2.840.10045.3.1.6' => 'prime239v3',
            
'1.2.840.10045.3.1.7' => 'prime256v1',
            
'1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
            
'1.2.840.113549.1.1.9' => 'id-pSpecified',
            
'1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
            
'1.2.840.113549.1.1.8' => 'id-mgf1',
            
'1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
            
'1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
            
'1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
            
'1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
            
'2.16.840.1.101.3.4.2.4' => 'id-sha224',
            
'2.16.840.1.101.3.4.2.1' => 'id-sha256',
            
'2.16.840.1.101.3.4.2.2' => 'id-sha384',
            
'2.16.840.1.101.3.4.2.3' => 'id-sha512',
            
'1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
            
'1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
            
'1.2.643.2.2.20' => 'id-GostR3410-2001',
            
'1.2.643.2.2.19' => 'id-GostR3410-94',
            
// Netscape Object Identifiers from "Netscape Certificate Extensions"
            
'2.16.840.1.113730' => 'netscape',
            
'2.16.840.1.113730.1' => 'netscape-cert-extension',
            
'2.16.840.1.113730.1.1' => 'netscape-cert-type',
            
'2.16.840.1.113730.1.13' => 'netscape-comment',
            
'2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
            
// the following are X.509 extensions not supported by phpseclib
            
'1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
            
'1.2.840.113533.7.65.0' => 'entrustVersInfo',
            
'2.16.840.1.113733.1.6.9' => 'verisignPrivate',
            
// for Certificate Signing Requests
            // see http://tools.ietf.org/html/rfc2985
            
'1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName'// PKCS #9 unstructured name
            
'1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword'// Challenge password for certificate revocations
            
'1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request
        
);
    }

    
/**
     * Load X.509 certificate
     *
     * Returns an associative array describing the X.509 cert or a false if the cert failed to load
     *
     * @param String $cert
     * @access public
     * @return Mixed
     */
    
function loadX509($cert)
    {
        if (
is_array($cert) && isset($cert['tbsCertificate'])) {
            unset(
$this->currentCert);
            unset(
$this->currentKeyIdentifier);
            
$this->dn $cert['tbsCertificate']['subject'];
            if (!isset(
$this->dn)) {
                return 
false;
            }
            
$this->currentCert $cert;

            
$currentKeyIdentifier $this->getExtension('id-ce-subjectKeyIdentifier');
            
$this->currentKeyIdentifier is_string($currentKeyIdentifier) ? $currentKeyIdentifier null;

            unset(
$this->signatureSubject);

            return 
$cert;
        }

        
$asn1 = new File_ASN1();

        
$cert $this->_extractBER($cert);

        if (
$cert === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$asn1->loadOIDs($this->oids);
        
$decoded $asn1->decodeBER($cert);

        if (!empty(
$decoded)) {
            
$x509 $asn1->asn1map($decoded[0], $this->Certificate);
        }
        if (!isset(
$x509) || $x509 === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$this->signatureSubject substr($cert$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        
$this->_mapInExtensions($x509'tbsCertificate/extensions'$asn1);

        
$key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
        
$key $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);

        
$this->currentCert $x509;
        
$this->dn $x509['tbsCertificate']['subject'];

        
$currentKeyIdentifier $this->getExtension('id-ce-subjectKeyIdentifier');
        
$this->currentKeyIdentifier is_string($currentKeyIdentifier) ? $currentKeyIdentifier null;

        return 
$x509;
    }

    
/**
     * Save X.509 certificate
     *
     * @param Array $cert
     * @param Integer $format optional
     * @access public
     * @return String
     */
    
function saveX509($cert$format FILE_X509_FORMAT_PEM)
    {
        if (!
is_array($cert) || !isset($cert['tbsCertificate'])) {
            return 
false;
        }

        switch (
true) {
            
// "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()"
            
case !($algorithm $this->_subArray($cert'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
            case 
is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
                break;
            default:
                switch (
$algorithm) {
                    case 
'rsaEncryption':
                        
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
                            = 
base64_encode("" base64_decode(preg_replace('#-.+-|[rn]#'''$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
                        
/* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier."
                           -- https://tools.ietf.org/html/rfc3279#section-2.3.1

                           given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank,
                           it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever.
                         */
                        
$cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null;
                        
// https://tools.ietf.org/html/rfc3279#section-2.2.1
                        
$cert['signatureAlgorithm']['parameters'] = null;
                        
$cert['tbsCertificate']['signature']['parameters'] = null;
                }
        }

        
$asn1 = new File_ASN1();
        
$asn1->loadOIDs($this->oids);

        
$filters = array();
        
$type_utf8_string = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
        
$filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string;
        
$filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string;
        
$filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string;
        
$filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string;
        
$filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string;
        
$filters['signatureAlgorithm']['parameters'] = $type_utf8_string;
        
$filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
        
//$filters['policyQualifiers']['qualifier'] = $type_utf8_string;
        
$filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
        
$filters['directoryName']['rdnSequence']['value'] = $type_utf8_string;

        
/* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING.
           FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
           characters.
         */
        
$filters['policyQualifiers']['qualifier']
            = array(
'type' => FILE_ASN1_TYPE_IA5_STRING);

        
$asn1->loadFilters($filters);

        
$this->_mapOutExtensions($cert'tbsCertificate/extensions'$asn1);

        
$cert $asn1->encodeDER($cert$this->Certificate);

        switch (
$format) {
            case 
FILE_X509_FORMAT_DER:
                return 
$cert;
            
// case FILE_X509_FORMAT_PEM:
            
default:
                return 
"-----BEGIN CERTIFICATE-----rn" chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----';
        }
    }

    
/**
     * Map extension values from octet string to extension-specific internal
     *   format.
     *
     * @param Array ref $root
     * @param String $path
     * @param Object $asn1
     * @access private
     */
    
function _mapInExtensions(&$root$path$asn1)
    {
        
$extensions = &$this->_subArray($root$path);

        if (
is_array($extensions)) {
            for (
$i 0$i count($extensions); $i++) {
                
$id $extensions[$i]['extnId'];
                
$value = &$extensions[$i]['extnValue'];
                
$value base64_decode($value);
                
$decoded $asn1->decodeBER($value);
                
/* [extnValue] contains the DER encoding of an ASN.1 value
                   corresponding to the extension type identified by extnID */
                
$map $this->_getMapping($id);
                if (!
is_bool($map)) {
                    
$mapped $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this'_decodeIP')));
                    
$value $mapped === false $decoded[0] : $mapped;

                    if (
$id == 'id-ce-certificatePolicies') {
                        for (
$j 0$j count($value); $j++) {
                            if (!isset(
$value[$j]['policyQualifiers'])) {
                                continue;
                            }
                            for (
$k 0$k count($value[$j]['policyQualifiers']); $k++) {
                                
$subid $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
                                
$map $this->_getMapping($subid);
                                
$subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
                                if (
$map !== false) {
                                    
$decoded $asn1->decodeBER($subvalue);
                                    
$mapped $asn1->asn1map($decoded[0], $map);
                                    
$subvalue $mapped === false $decoded[0] : $mapped;
                                }
                            }
                        }
                    }
                } else {
                    
$value base64_encode($value);
                }
            }
        }
    }

    
/**
     * Map extension values from extension-specific internal format to
     *   octet string.
     *
     * @param Array ref $root
     * @param String $path
     * @param Object $asn1
     * @access private
     */
    
function _mapOutExtensions(&$root$path$asn1)
    {
        
$extensions = &$this->_subArray($root$path);

        if (
is_array($extensions)) {
            
$size count($extensions);
            for (
$i 0$i $size$i++) {
                if (
is_object($extensions[$i]) && strtolower(get_class($extensions[$i])) == 'file_asn1_element') {
                    continue;
                }

                
$id $extensions[$i]['extnId'];
                
$value = &$extensions[$i]['extnValue'];

                switch (
$id) {
                    case 
'id-ce-certificatePolicies':
                        for (
$j 0$j count($value); $j++) {
                            if (!isset(
$value[$j]['policyQualifiers'])) {
                                continue;
                            }
                            for (
$k 0$k count($value[$j]['policyQualifiers']); $k++) {
                                
$subid $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
                                
$map $this->_getMapping($subid);
                                
$subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
                                if (
$map !== false) {
                                    
// by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's
                                    // actual type is FILE_ASN1_TYPE_ANY
                                    
$subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue$map));
                                }
                            }
                        }
                        break;
                    case 
'id-ce-authorityKeyIdentifier'// use 00 as the serial number instead of an empty string
                        
if (isset($value['authorityCertSerialNumber'])) {
                            if (
$value['authorityCertSerialNumber']->toBytes() == '') {
                                
$temp chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "1";
                                
$value['authorityCertSerialNumber'] = new File_ASN1_Element($temp);
                            }
                        }
                }

                
/* [extnValue] contains the DER encoding of an ASN.1 value
                   corresponding to the extension type identified by extnID */
                
$map $this->_getMapping($id);
                if (
is_bool($map)) {
                    if (!
$map) {
                        
user_error($id ' is not a currently supported extension');
                        unset(
$extensions[$i]);
                    }
                } else {
                    
$temp $asn1->encodeDER($value$map, array('iPAddress' => array($this'_encodeIP')));
                    
$value base64_encode($temp);
                }
            }
        }
    }

    
/**
     * Map attribute values from ANY type to attribute-specific internal
     *   format.
     *
     * @param Array ref $root
     * @param String $path
     * @param Object $asn1
     * @access private
     */
    
function _mapInAttributes(&$root$path$asn1)
    {
        
$attributes = &$this->_subArray($root$path);

        if (
is_array($attributes)) {
            for (
$i 0$i count($attributes); $i++) {
                
$id $attributes[$i]['type'];
                
/* $value contains the DER encoding of an ASN.1 value
                   corresponding to the attribute type identified by type */
                
$map $this->_getMapping($id);
                if (
is_array($attributes[$i]['value'])) {
                    
$values = &$attributes[$i]['value'];
                    for (
$j 0$j count($values); $j++) {
                        
$value $asn1->encodeDER($values[$j], $this->AttributeValue);
                        
$decoded $asn1->decodeBER($value);
                        if (!
is_bool($map)) {
                            
$mapped $asn1->asn1map($decoded[0], $map);
                            if (
$mapped !== false) {
                                
$values[$j] = $mapped;
                            }
                            if (
$id == 'pkcs-9-at-extensionRequest') {
                                
$this->_mapInExtensions($values$j$asn1);
                            }
                        } elseif (
$map) {
                            
$values[$j] = base64_encode($value);
                        }
                    }
                }
            }
        }
    }

    
/**
     * Map attribute values from attribute-specific internal format to
     *   ANY type.
     *
     * @param Array ref $root
     * @param String $path
     * @param Object $asn1
     * @access private
     */
    
function _mapOutAttributes(&$root$path$asn1)
    {
        
$attributes = &$this->_subArray($root$path);

        if (
is_array($attributes)) {
            
$size count($attributes);
            for (
$i 0$i $size$i++) {
                
/* [value] contains the DER encoding of an ASN.1 value
                   corresponding to the attribute type identified by type */
                
$id $attributes[$i]['type'];
                
$map $this->_getMapping($id);
                if (
$map === false) {
                    
user_error($id ' is not a currently supported attribute'E_USER_NOTICE);
                    unset(
$attributes[$i]);
                } elseif (
is_array($attributes[$i]['value'])) {
                    
$values = &$attributes[$i]['value'];
                    for (
$j 0$j count($values); $j++) {
                        switch (
$id) {
                            case 
'pkcs-9-at-extensionRequest':
                                
$this->_mapOutExtensions($values$j$asn1);
                                break;
                        }

                        if (!
is_bool($map)) {
                            
$temp $asn1->encodeDER($values[$j], $map);
                            
$decoded $asn1->decodeBER($temp);
                            
$values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue);
                        }
                    }
                }
            }
        }
    }

    
/**
     * Associate an extension ID to an extension mapping
     *
     * @param String $extnId
     * @access private
     * @return Mixed
     */
    
function _getMapping($extnId)
    {
        if (!
is_string($extnId)) { // eg. if it's a File_ASN1_Element object
            
return true;
        }

        switch (
$extnId) {
            case 
'id-ce-keyUsage':
                return 
$this->KeyUsage;
            case 
'id-ce-basicConstraints':
                return 
$this->BasicConstraints;
            case 
'id-ce-subjectKeyIdentifier':
                return 
$this->KeyIdentifier;
            case 
'id-ce-cRLDistributionPoints':
                return 
$this->CRLDistributionPoints;
            case 
'id-ce-authorityKeyIdentifier':
                return 
$this->AuthorityKeyIdentifier;
            case 
'id-ce-certificatePolicies':
                return 
$this->CertificatePolicies;
            case 
'id-ce-extKeyUsage':
                return 
$this->ExtKeyUsageSyntax;
            case 
'id-pe-authorityInfoAccess':
                return 
$this->AuthorityInfoAccessSyntax;
            case 
'id-ce-subjectAltName':
                return 
$this->SubjectAltName;
            case 
'id-ce-privateKeyUsagePeriod':
                return 
$this->PrivateKeyUsagePeriod;
            case 
'id-ce-issuerAltName':
                return 
$this->IssuerAltName;
            case 
'id-ce-policyMappings':
                return 
$this->PolicyMappings;
            case 
'id-ce-nameConstraints':
                return 
$this->NameConstraints;

            case 
'netscape-cert-type':
                return 
$this->netscape_cert_type;
            case 
'netscape-comment':
                return 
$this->netscape_comment;
            case 
'netscape-ca-policy-url':
                return 
$this->netscape_ca_policy_url;

            
// since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
            // back around to asn1map() and we don't want it decoded again.
            //case 'id-qt-cps':
            //    return $this->CPSuri;
            
case 'id-qt-unotice':
                return 
$this->UserNotice;

            
// the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
            
case 'id-pe-logotype'// http://www.ietf.org/rfc/rfc3709.txt
            
case 'entrustVersInfo':
            
// http://support.microsoft.com/kb/287547
            
case '1.3.6.1.4.1.311.20.2'// szOID_ENROLL_CERTTYPE_EXTENSION
            
case '1.3.6.1.4.1.311.21.1'// szOID_CERTSRV_CA_VERSION
            // "SET Secure Electronic Transaction Specification"
            // http://www.maithean.com/docs/set_bk3.pdf
            
case '2.23.42.7.0'// id-set-hashedRootKey
                
return true;

            
// CSR attributes
            
case 'pkcs-9-at-unstructuredName':
                return 
$this->PKCS9String;
            case 
'pkcs-9-at-challengePassword':
                return 
$this->DirectoryString;
            case 
'pkcs-9-at-extensionRequest':
                return 
$this->Extensions;

            
// CRL extensions.
            
case 'id-ce-cRLNumber':
                return 
$this->CRLNumber;
            case 
'id-ce-deltaCRLIndicator':
                return 
$this->CRLNumber;
            case 
'id-ce-issuingDistributionPoint':
                return 
$this->IssuingDistributionPoint;
            case 
'id-ce-freshestCRL':
                return 
$this->CRLDistributionPoints;
            case 
'id-ce-cRLReasons':
                return 
$this->CRLReason;
            case 
'id-ce-invalidityDate':
                return 
$this->InvalidityDate;
            case 
'id-ce-certificateIssuer':
                return 
$this->CertificateIssuer;
            case 
'id-ce-holdInstructionCode':
                return 
$this->HoldInstructionCode;
        }

        return 
false;
    }

    
/**
     * Load an X.509 certificate as a certificate authority
     *
     * @param String $cert
     * @access public
     * @return Boolean
     */
    
function loadCA($cert)
    {
        
$olddn $this->dn;
        
$oldcert $this->currentCert;
        
$oldsigsubj $this->signatureSubject;
        
$oldkeyid $this->currentKeyIdentifier;

        
$cert $this->loadX509($cert);
        if (!
$cert) {
            
$this->dn $olddn;
            
$this->currentCert $oldcert;
            
$this->signatureSubject $oldsigsubj;
            
$this->currentKeyIdentifier $oldkeyid;

            return 
false;
        }

        
/* From RFC5280 "PKIX Certificate and CRL Profile":

           If the keyUsage extension is present, then the subject public key
           MUST NOT be used to verify signatures on certificates or CRLs unless
           the corresponding keyCertSign or cRLSign bit is set. */
        //$keyUsage = $this->getExtension('id-ce-keyUsage');
        //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
        //    return false;
        //}

        /* From RFC5280 "PKIX Certificate and CRL Profile":

           The cA boolean indicates whether the certified public key may be used
           to verify certificate signatures.  If the cA boolean is not asserted,
           then the keyCertSign bit in the key usage extension MUST NOT be
           asserted.  If the basic constraints extension is not present in a
           version 3 certificate, or the extension is present but the cA boolean
           is not asserted, then the certified public key MUST NOT be used to
           verify certificate signatures. */
        //$basicConstraints = $this->getExtension('id-ce-basicConstraints');
        //if (!$basicConstraints || !$basicConstraints['cA']) {
        //    return false;
        //}

        
$this->CAs[] = $cert;

        
$this->dn $olddn;
        
$this->currentCert $oldcert;
        
$this->signatureSubject $oldsigsubj;

        return 
true;
    }

    
/**
     * Validate an X.509 certificate against a URL
     *
     * From RFC2818 "HTTP over TLS":
     *
     * Matching is performed using the matching rules specified by
     * [RFC2459].  If more than one identity of a given type is present in
     * the certificate (e.g., more than one dNSName name, a match in any one
     * of the set is considered acceptable.) Names may contain the wildcard
     * character * which is considered to match any single domain name
     * component or component fragment. E.g., *.a.com matches foo.a.com but
     * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
     *
     * @param String $url
     * @access public
     * @return Boolean
     */
    
function validateURL($url)
    {
        if (!
is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
            return 
false;
        }

        
$components parse_url($url);
        if (!isset(
$components['host'])) {
            return 
false;
        }

        if (
$names $this->getExtension('id-ce-subjectAltName')) {
            foreach (
$names as $key => $value) {
                
$value str_replace(array('.''*'), array('.''[^.]*'), $value);
                switch (
$key) {
                    case 
'dNSName':
                        
/* From RFC2818 "HTTP over TLS":

                           If a subjectAltName extension of type dNSName is present, that MUST
                           be used as the identity. Otherwise, the (most specific) Common Name
                           field in the Subject field of the certificate MUST be used. Although
                           the use of the Common Name is existing practice, it is deprecated and
                           Certification Authorities are encouraged to use the dNSName instead. */
                        
if (preg_match('#^' $value '$#'$components['host'])) {
                            return 
true;
                        }
                        break;
                    case 
'iPAddress':
                        
/* From RFC2818 "HTTP over TLS":

                           In some cases, the URI is specified as an IP address rather than a
                           hostname. In this case, the iPAddress subjectAltName must be present
                           in the certificate and must exactly match the IP in the URI. */
                        
if (preg_match('#(?:d{1-3}.){4}#'$components['host'] . '.') && preg_match('#^' $value '$#'$components['host'])) {
                            return 
true;
                        }
                }
            }
            return 
false;
        }

        if (
$value $this->getDNProp('id-at-commonName')) {
            
$value str_replace(array('.''*'), array('.''[^.]*'), $value[0]);
            return 
preg_match('#^' $value '$#'$components['host']);
        }

        return 
false;
    }

    
/**
     * Validate a date
     *
     * If $date isn't defined it is assumed to be the current date.
     *
     * @param Integer $date optional
     * @access public
     */
    
function validateDate($date null)
    {
        if (!
is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
            return 
false;
        }

        if (!isset(
$date)) {
            
$date time();
        }

        
$notBefore $this->currentCert['tbsCertificate']['validity']['notBefore'];
        
$notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime'];

        
$notAfter $this->currentCert['tbsCertificate']['validity']['notAfter'];
        
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];

        switch (
true) {
            case 
$date < @strtotime($notBefore):
            case 
$date > @strtotime($notAfter):
                return 
false;
        }

        return 
true;
    }

    
/**
     * Validate a signature
     *
     * Works on X.509 certs, CSR's and CRL's.
     * Returns true if the signature is verified, false if it is not correct or null on error
     *
     * By default returns false for self-signed certs. Call validateSignature(false) to make this support
     * self-signed.
     *
     * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
     *
     * @param Boolean $caonly optional
     * @access public
     * @return Mixed
     */
    
function validateSignature($caonly true)
    {
        if (!
is_array($this->currentCert) || !isset($this->signatureSubject)) {
            return 
null;
        }

        
/* TODO:
           "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
            -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6

           implement pathLenConstraint in the id-ce-basicConstraints extension */

        
switch (true) {
            case isset(
$this->currentCert['tbsCertificate']):
                
// self-signed cert
                
if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) {
                    
$authorityKey $this->getExtension('id-ce-authorityKeyIdentifier');
                    
$subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier');
                    switch (
true) {
                        case !
is_array($authorityKey):
                        case 
is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                            
$signingCert $this->currentCert// working cert
                    
}
                }

                if (!empty(
$this->CAs)) {
                    for (
$i 0$i count($this->CAs); $i++) {
                        
// even if the cert is a self-signed one we still want to see if it's a CA;
                        // if not, we'll conditionally return an error
                        
$ca $this->CAs[$i];
                        if (
$this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
                            
$authorityKey $this->getExtension('id-ce-authorityKeyIdentifier');
                            
$subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier'$ca);
                            switch (
true) {
                                case !
is_array($authorityKey):
                                case 
is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                                    
$signingCert $ca// working cert
                                    
break 2;
                            }
                        }
                    }
                    if (
count($this->CAs) == $i && $caonly) {
                        return 
false;
                    }
                } elseif (!isset(
$signingCert) || $caonly) {
                    return 
false;
                }
                return 
$this->_validateSignature(
                    
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
                    
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
                    
$this->currentCert['signatureAlgorithm']['algorithm'],
                    
substr(base64_decode($this->currentCert['signature']), 1),
                    
$this->signatureSubject
                
);
            case isset(
$this->currentCert['certificationRequestInfo']):
                return 
$this->_validateSignature(
                    
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
                    
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
                    
$this->currentCert['signatureAlgorithm']['algorithm'],
                    
substr(base64_decode($this->currentCert['signature']), 1),
                    
$this->signatureSubject
                
);
            case isset(
$this->currentCert['publicKeyAndChallenge']):
                return 
$this->_validateSignature(
                    
$this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
                    
$this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
                    
$this->currentCert['signatureAlgorithm']['algorithm'],
                    
substr(base64_decode($this->currentCert['signature']), 1),
                    
$this->signatureSubject
                
);
            case isset(
$this->currentCert['tbsCertList']):
                if (!empty(
$this->CAs)) {
                    for (
$i 0$i count($this->CAs); $i++) {
                        
$ca $this->CAs[$i];
                        if (
$this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) {
                            
$authorityKey $this->getExtension('id-ce-authorityKeyIdentifier');
                            
$subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier'$ca);
                            switch (
true) {
                                case !
is_array($authorityKey):
                                case 
is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                                    
$signingCert $ca// working cert
                                    
break 2;
                            }
                        }
                    }
                }
                if (!isset(
$signingCert)) {
                    return 
false;
                }
                return 
$this->_validateSignature(
                    
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
                    
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
                    
$this->currentCert['signatureAlgorithm']['algorithm'],
                    
substr(base64_decode($this->currentCert['signature']), 1),
                    
$this->signatureSubject
                
);
            default:
                return 
false;
        }
    }

    
/**
     * Validates a signature
     *
     * Returns true if the signature is verified, false if it is not correct or null on error
     *
     * @param String $publicKeyAlgorithm
     * @param String $publicKey
     * @param String $signatureAlgorithm
     * @param String $signature
     * @param String $signatureSubject
     * @access private
     * @return Integer
     */
    
function _validateSignature($publicKeyAlgorithm$publicKey$signatureAlgorithm$signature$signatureSubject)
    {
        switch (
$publicKeyAlgorithm) {
            case 
'rsaEncryption':
                if (!
class_exists('Crypt_RSA')) {
                    include_once 
'Crypt/RSA.php';
                }
                
$rsa = new Crypt_RSA();
                
$rsa->loadKey($publicKey);

                switch (
$signatureAlgorithm) {
                    case 
'md2WithRSAEncryption':
                    case 
'md5WithRSAEncryption':
                    case 
'sha1WithRSAEncryption':
                    case 
'sha224WithRSAEncryption':
                    case 
'sha256WithRSAEncryption':
                    case 
'sha384WithRSAEncryption':
                    case 
'sha512WithRSAEncryption':
                        
$rsa->setHash(preg_replace('#WithRSAEncryption$#'''$signatureAlgorithm));
                        
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
                        if (!@
$rsa->verify($signatureSubject$signature)) {
                            return 
false;
                        }
                        break;
                    default:
                        return 
null;
                }
                break;
            default:
                return 
null;
        }

        return 
true;
    }

    
/**
     * Reformat public keys
     *
     * Reformats a public key to a format supported by phpseclib (if applicable)
     *
     * @param String $algorithm
     * @param String $key
     * @access private
     * @return String
     */
    
function _reformatKey($algorithm$key)
    {
        switch (
$algorithm) {
            case 
'rsaEncryption':
                return
                    
"-----BEGIN RSA PUBLIC KEY-----rn" .
                    
// subjectPublicKey is stored as a bit string in X.509 certs.  the first byte of a bit string represents how many bits
                    // in the last byte should be ignored.  the following only supports non-zero stuff but as none of the X.509 certs Firefox
                    // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
                    
chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) .
                    
'-----END RSA PUBLIC KEY-----';
            default:
                return 
$key;
        }
    }

    
/**
     * Decodes an IP address
     *
     * Takes in a base64 encoded "blob" and returns a human readable IP address
     *
     * @param String $ip
     * @access private
     * @return String
     */
    
function _decodeIP($ip)
    {
        
$ip base64_decode($ip);
        list(, 
$ip) = unpack('N'$ip);
        return 
long2ip($ip);
    }

    
/**
     * Encodes an IP address
     *
     * Takes a human readable IP address into a base64-encoded "blob"
     *
     * @param String $ip
     * @access private
     * @return String
     */
    
function _encodeIP($ip)
    {
        return 
base64_encode(pack('N'ip2long($ip)));
    }

    
/**
     * "Normalizes" a Distinguished Name property
     *
     * @param String $propName
     * @access private
     * @return Mixed
     */
    
function _translateDNProp($propName)
    {
        switch (
strtolower($propName)) {
            case 
'id-at-countryname':
            case 
'countryname':
            case 
'c':
                return 
'id-at-countryName';
            case 
'id-at-organizationname':
            case 
'organizationname':
            case 
'o':
                return 
'id-at-organizationName';
            case 
'id-at-dnqualifier':
            case 
'dnqualifier':
                return 
'id-at-dnQualifier';
            case 
'id-at-commonname':
            case 
'commonname':
            case 
'cn':
                return 
'id-at-commonName';
            case 
'id-at-stateorprovincename':
            case 
'stateorprovincename':
            case 
'state':
            case 
'province':
            case 
'provincename':
            case 
'st':
                return 
'id-at-stateOrProvinceName';
            case 
'id-at-localityname':
            case 
'localityname':
            case 
'l':
                return 
'id-at-localityName';
            case 
'id-emailaddress':
            case 
'emailaddress':
                return 
'pkcs-9-at-emailAddress';
            case 
'id-at-serialnumber':
            case 
'serialnumber':
                return 
'id-at-serialNumber';
            case 
'id-at-postalcode':
            case 
'postalcode':
                return 
'id-at-postalCode';
            case 
'id-at-streetaddress':
            case 
'streetaddress':
                return 
'id-at-streetAddress';
            case 
'id-at-name':
            case 
'name':
                return 
'id-at-name';
            case 
'id-at-givenname':
            case 
'givenname':
                return 
'id-at-givenName';
            case 
'id-at-surname':
            case 
'surname':
            case 
'sn':
                return 
'id-at-surname';
            case 
'id-at-initials':
            case 
'initials':
                return 
'id-at-initials';
            case 
'id-at-generationqualifier':
            case 
'generationqualifier':
                return 
'id-at-generationQualifier';
            case 
'id-at-organizationalunitname':
            case 
'organizationalunitname':
            case 
'ou':
                return 
'id-at-organizationalUnitName';
            case 
'id-at-pseudonym':
            case 
'pseudonym':
                return 
'id-at-pseudonym';
            case 
'id-at-title':
            case 
'title':
                return 
'id-at-title';
            case 
'id-at-description':
            case 
'description':
                return 
'id-at-description';
            case 
'id-at-role':
            case 
'role':
                return 
'id-at-role';
            case 
'id-at-uniqueidentifier':
            case 
'uniqueidentifier':
            case 
'x500uniqueidentifier':
                return 
'id-at-uniqueIdentifier';
            default:
                return 
false;
        }
    }

    
/**
     * Set a Distinguished Name property
     *
     * @param String $propName
     * @param Mixed $propValue
     * @param String $type optional
     * @access public
     * @return Boolean
     */
    
function setDNProp($propName$propValue$type 'utf8String')
    {
        if (empty(
$this->dn)) {
            
$this->dn = array('rdnSequence' => array());
        }

        if ((
$propName $this->_translateDNProp($propName)) === false) {
            return 
false;
        }

        foreach ((array) 
$propValue as $v) {
            if (!
is_array($v) && isset($type)) {
                
$v = array($type => $v);
            }
            
$this->dn['rdnSequence'][] = array(
                array(
                    
'type' => $propName,
                    
'value'=> $v
                
)
            );
        }

        return 
true;
    }

    
/**
     * Remove Distinguished Name properties
     *
     * @param String $propName
     * @access public
     */
    
function removeDNProp($propName)
    {
        if (empty(
$this->dn)) {
            return;
        }

        if ((
$propName $this->_translateDNProp($propName)) === false) {
            return;
        }

        
$dn = &$this->dn['rdnSequence'];
        
$size count($dn);
        for (
$i 0$i $size$i++) {
            if (
$dn[$i][0]['type'] == $propName) {
                unset(
$dn[$i]);
            }
        }

        
$dn array_values($dn);
    }

    
/**
     * Get Distinguished Name properties
     *
     * @param String $propName
     * @param Array $dn optional
     * @param Boolean $withType optional
     * @return Mixed
     * @access public
     */
    
function getDNProp($propName$dn null$withType false)
    {
        if (!isset(
$dn)) {
            
$dn $this->dn;
        }

        if (empty(
$dn)) {
            return 
false;
        }

        if ((
$propName $this->_translateDNProp($propName)) === false) {
            return 
false;
        }

        
$dn $dn['rdnSequence'];
        
$result = array();
        
$asn1 = new File_ASN1();
        for (
$i 0$i count($dn); $i++) {
            if (
$dn[$i][0]['type'] == $propName) {
                
$v $dn[$i][0]['value'];
                if (!
$withType && is_array($v)) {
                    foreach (
$v as $type => $s) {
                        
$type array_search($type$asn1->ANYmaptrue);
                        if (
$type !== false && isset($asn1->stringTypeSize[$type])) {
                            
$s $asn1->convert($s$type);
                            if (
$s !== false) {
                                
$v $s;
                                break;
                            }
                        }
                    }
                    if (
is_array($v)) {
                        
$v array_pop($v); // Always strip data type.
                    
}
                }
                
$result[] = $v;
            }
        }

        return 
$result;
    }

    
/**
     * Set a Distinguished Name
     *
     * @param Mixed $dn
     * @param Boolean $merge optional
     * @param String $type optional
     * @access public
     * @return Boolean
     */
    
function setDN($dn$merge false$type 'utf8String')
    {
        if (!
$merge) {
            
$this->dn null;
        }

        if (
is_array($dn)) {
            if (isset(
$dn['rdnSequence'])) {
                
$this->dn $dn// No merge here.
                
return true;
            }

            
// handles stuff generated by openssl_x509_parse()
            
foreach ($dn as $prop => $value) {
                if (!
$this->setDNProp($prop$value$type)) {
                    return 
false;
                }
            }
            return 
true;
        }

        
// handles everything else
        
$results preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=))#'$dn, -1PREG_SPLIT_DELIM_CAPTURE);
        for (
$i 1$i count($results); $i+=2) {
            
$prop trim($results[$i], ', =/');
            
$value $results[$i 1];
            if (!
$this->setDNProp($prop$value$type)) {
                return 
false;
            }
        }

        return 
true;
    }

    
/**
     * Get the Distinguished Name for a certificates subject
     *
     * @param Mixed $format optional
     * @param Array $dn optional
     * @access public
     * @return Boolean
     */
    
function getDN($format FILE_X509_DN_ARRAY$dn null)
    {
        if (!isset(
$dn)) {
            
$dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
        }

        switch ((int) 
$format) {
            case 
FILE_X509_DN_ARRAY:
                return 
$dn;
            case 
FILE_X509_DN_ASN1:
                
$asn1 = new File_ASN1();
                
$asn1->loadOIDs($this->oids);
                
$filters = array();
                
$filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
                
$asn1->loadFilters($filters);
                return 
$asn1->encodeDER($dn$this->Name);
            case 
FILE_X509_DN_OPENSSL:
                
$dn $this->getDN(FILE_X509_DN_STRING$dn);
                if (
$dn === false) {
                    return 
false;
                }
                
$attrs preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i'$dn, -1PREG_SPLIT_DELIM_CAPTURE);
                
$dn = array();
                for (
$i 1$i count($attrs); $i += 2) {
                    
$prop trim($attrs[$i], ', =/');
                    
$value $attrs[$i 1];
                    if (!isset(
$dn[$prop])) {
                        
$dn[$prop] = $value;
                    } else {
                        
$dn[$prop] = array_merge((array) $dn[$prop], array($value));
                    }
                }
                return 
$dn;
            case 
FILE_X509_DN_CANON:
                
//  No SEQUENCE around RDNs and all string values normalized as
                // trimmed lowercase UTF-8 with all spacing  as one blank.
                
$asn1 = new File_ASN1();
                
$asn1->loadOIDs($this->oids);
                
$filters = array();
                
$filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
                
$asn1->loadFilters($filters);
                
$result '';
                foreach (
$dn['rdnSequence'] as $rdn) {
                    foreach (
$rdn as $i => $attr) {
                        
$attr = &$rdn[$i];
                        if (
is_array($attr['value'])) {
                            foreach (
$attr['value'] as $type => $v) {
                                
$type array_search($type$asn1->ANYmaptrue);
                                if (
$type !== false && isset($asn1->stringTypeSize[$type])) {
                                    
$v $asn1->convert($v$type);
                                    if (
$v !== false) {
                                        
$v preg_replace('/s+/'' '$v);
                                        
$attr['value'] = strtolower(trim($v));
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    
$result .= $asn1->encodeDER($rdn$this->RelativeDistinguishedName);
                }
                return 
$result;
            case 
FILE_X509_DN_HASH:
                
$dn $this->getDN(FILE_X509_DN_CANON$dn);
                if (!
class_exists('Crypt_Hash')) {
                    include_once 
'Crypt/Hash.php';
                }
                
$hash = new Crypt_Hash('sha1');
                
$hash $hash->hash($dn);
                
extract(unpack('Vhash'$hash));
                return 
strtolower(bin2hex(pack('N'$hash)));
        }

        
// Default is to return a string.
        
$start true;
        
$output '';
        
$asn1 = new File_ASN1();
        foreach (
$dn['rdnSequence'] as $field) {
            
$prop $field[0]['type'];
            
$value $field[0]['value'];

            
$delim ', ';
            switch (
$prop) {
                case 
'id-at-countryName':
                    
$desc 'C=';
                    break;
                case 
'id-at-stateOrProvinceName':
                    
$desc 'ST=';
                    break;
                case 
'id-at-organizationName':
                    
$desc 'O=';
                    break;
                case 
'id-at-organizationalUnitName':
                    
$desc 'OU=';
                    break;
                case 
'id-at-commonName':
                    
$desc 'CN=';
                    break;
                case 
'id-at-localityName':
                    
$desc 'L=';
                    break;
                case 
'id-at-surname':
                    
$desc 'SN=';
                    break;
                case 
'id-at-uniqueIdentifier':
                    
$delim '/';
                    
$desc 'x500UniqueIdentifier=';
                    break;
                default:
                    
$delim '/';
                    
$desc preg_replace('#.+-([^-]+)$#''$1'$prop) . '=';
            }

            if (!
$start) {
                
$output.= $delim;
            }
            if (
is_array($value)) {
                foreach (
$value as $type => $v) {
                    
$type array_search($type$asn1->ANYmaptrue);
                    if (
$type !== false && isset($asn1->stringTypeSize[$type])) {
                        
$v $asn1->convert($v$type);
                        if (
$v !== false) {
                            
$value $v;
                            break;
                        }
                    }
                }
                if (
is_array($value)) {
                    
$value array_pop($value); // Always strip data type.
                
}
            }
            
$output.= $desc $value;
            
$start false;
        }

        return 
$output;
    }

    
/**
     * Get the Distinguished Name for a certificate/crl issuer
     *
     * @param Integer $format optional
     * @access public
     * @return Mixed
     */
    
function getIssuerDN($format FILE_X509_DN_ARRAY)
    {
        switch (
true) {
            case !isset(
$this->currentCert) || !is_array($this->currentCert):
                break;
            case isset(
$this->currentCert['tbsCertificate']):
                return 
$this->getDN($format$this->currentCert['tbsCertificate']['issuer']);
            case isset(
$this->currentCert['tbsCertList']):
                return 
$this->getDN($format$this->currentCert['tbsCertList']['issuer']);
        }

        return 
false;
    }

    
/**
     * Get the Distinguished Name for a certificate/csr subject
     * Alias of getDN()
     *
     * @param Integer $format optional
     * @access public
     * @return Mixed
     */
    
function getSubjectDN($format FILE_X509_DN_ARRAY)
    {
        switch (
true) {
            case !empty(
$this->dn):
                return 
$this->getDN($format);
            case !isset(
$this->currentCert) || !is_array($this->currentCert):
                break;
            case isset(
$this->currentCert['tbsCertificate']):
                return 
$this->getDN($format$this->currentCert['tbsCertificate']['subject']);
            case isset(
$this->currentCert['certificationRequestInfo']):
                return 
$this->getDN($format$this->currentCert['certificationRequestInfo']['subject']);
        }

        return 
false;
    }

    
/**
     * Get an individual Distinguished Name property for a certificate/crl issuer
     *
     * @param String $propName
     * @param Boolean $withType optional
     * @access public
     * @return Mixed
     */
    
function getIssuerDNProp($propName$withType false)
    {
        switch (
true) {
            case !isset(
$this->currentCert) || !is_array($this->currentCert):
                break;
            case isset(
$this->currentCert['tbsCertificate']):
                return 
$this->getDNProp($propName$this->currentCert['tbsCertificate']['issuer'], $withType);
            case isset(
$this->currentCert['tbsCertList']):
                return 
$this->getDNProp($propName$this->currentCert['tbsCertList']['issuer'], $withType);
        }

        return 
false;
    }

    
/**
     * Get an individual Distinguished Name property for a certificate/csr subject
     *
     * @param String $propName
     * @param Boolean $withType optional
     * @access public
     * @return Mixed
     */
    
function getSubjectDNProp($propName$withType false)
    {
        switch (
true) {
            case !empty(
$this->dn):
                return 
$this->getDNProp($propNamenull$withType);
            case !isset(
$this->currentCert) || !is_array($this->currentCert):
                break;
            case isset(
$this->currentCert['tbsCertificate']):
                return 
$this->getDNProp($propName$this->currentCert['tbsCertificate']['subject'], $withType);
            case isset(
$this->currentCert['certificationRequestInfo']):
                return 
$this->getDNProp($propName$this->currentCert['certificationRequestInfo']['subject'], $withType);
        }

        return 
false;
    }

    
/**
     * Get the certificate chain for the current cert
     *
     * @access public
     * @return Mixed
     */
    
function getChain()
    {
        
$chain = array($this->currentCert);

        if (!
is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
            return 
false;
        }
        if (empty(
$this->CAs)) {
            return 
$chain;
        }
        while (
true) {
            
$currentCert $chain[count($chain) - 1];
            for (
$i 0$i count($this->CAs); $i++) {
                
$ca $this->CAs[$i];
                if (
$currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
                    
$authorityKey $this->getExtension('id-ce-authorityKeyIdentifier'$currentCert);
                    
$subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier'$ca);
                    switch (
true) {
                        case !
is_array($authorityKey):
                        case 
is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                            if (
$currentCert === $ca) {
                                break 
3;
                            }
                            
$chain[] = $ca;
                            break 
2;
                    }
                }
            }
            if (
$i == count($this->CAs)) {
                break;
            }
        }
        foreach (
$chain as $key => $value) {
            
$chain[$key] = new File_X509();
            
$chain[$key]->loadX509($value);
        }
        return 
$chain;
    }

    
/**
     * Set public key
     *
     * Key needs to be a Crypt_RSA object
     *
     * @param Object $key
     * @access public
     * @return Boolean
     */
    
function setPublicKey($key)
    {
        
$key->setPublicKey();
        
$this->publicKey $key;
    }

    
/**
     * Set private key
     *
     * Key needs to be a Crypt_RSA object
     *
     * @param Object $key
     * @access public
     */
    
function setPrivateKey($key)
    {
        
$this->privateKey $key;
    }

    
/**
     * Set challenge
     *
     * Used for SPKAC CSR's
     *
     * @param String $challenge
     * @access public
     */
    
function setChallenge($challenge)
    {
        
$this->challenge $challenge;
    }

    
/**
     * Gets the public key
     *
     * Returns a Crypt_RSA object or a false.
     *
     * @access public
     * @return Mixed
     */
    
function getPublicKey()
    {
        if (isset(
$this->publicKey)) {
            return 
$this->publicKey;
        }

        if (isset(
$this->currentCert) && is_array($this->currentCert)) {
            foreach (array(
'tbsCertificate/subjectPublicKeyInfo''certificationRequestInfo/subjectPKInfo') as $path) {
                
$keyinfo $this->_subArray($this->currentCert$path);
                if (!empty(
$keyinfo)) {
                    break;
                }
            }
        }
        if (empty(
$keyinfo)) {
            return 
false;
        }

        
$key $keyinfo['subjectPublicKey'];

        switch (
$keyinfo['algorithm']['algorithm']) {
            case 
'rsaEncryption':
                if (!
class_exists('Crypt_RSA')) {
                    include_once 
'Crypt/RSA.php';
                }
                
$publicKey = new Crypt_RSA();
                
$publicKey->loadKey($key);
                
$publicKey->setPublicKey();
                break;
            default:
                return 
false;
        }

        return 
$publicKey;
    }

    
/**
     * Load a Certificate Signing Request
     *
     * @param String $csr
     * @access public
     * @return Mixed
     */
    
function loadCSR($csr)
    {
        if (
is_array($csr) && isset($csr['certificationRequestInfo'])) {
            unset(
$this->currentCert);
            unset(
$this->currentKeyIdentifier);
            unset(
$this->signatureSubject);
            
$this->dn $csr['certificationRequestInfo']['subject'];
            if (!isset(
$this->dn)) {
                return 
false;
            }

            
$this->currentCert $csr;
            return 
$csr;
        }

        
// see http://tools.ietf.org/html/rfc2986

        
$asn1 = new File_ASN1();

        
$csr $this->_extractBER($csr);
        
$orig $csr;

        if (
$csr === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$asn1->loadOIDs($this->oids);
        
$decoded $asn1->decodeBER($csr);

        if (empty(
$decoded)) {
            
$this->currentCert false;
            return 
false;
        }

        
$csr $asn1->asn1map($decoded[0], $this->CertificationRequest);
        if (!isset(
$csr) || $csr === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$this->dn $csr['certificationRequestInfo']['subject'];
        
$this->_mapInAttributes($csr'certificationRequestInfo/attributes'$asn1);

        
$this->signatureSubject substr($orig$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        
$algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
        
$key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
        
$key $this->_reformatKey($algorithm$key);

        switch (
$algorithm) {
            case 
'rsaEncryption':
                if (!
class_exists('Crypt_RSA')) {
                    include_once 
'Crypt/RSA.php';
                }
                
$this->publicKey = new Crypt_RSA();
                
$this->publicKey->loadKey($key);
                
$this->publicKey->setPublicKey();
                break;
            default:
                
$this->publicKey null;
        }

        
$this->currentKeyIdentifier null;
        
$this->currentCert $csr;

        return 
$csr;
    }

    
/**
     * Save CSR request
     *
     * @param Array $csr
     * @param Integer $format optional
     * @access public
     * @return String
     */
    
function saveCSR($csr$format FILE_X509_FORMAT_PEM)
    {
        if (!
is_array($csr) || !isset($csr['certificationRequestInfo'])) {
            return 
false;
        }

        switch (
true) {
            case !(
$algorithm $this->_subArray($csr'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
            case 
is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
                break;
            default:
                switch (
$algorithm) {
                    case 
'rsaEncryption':
                        
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
                            = 
base64_encode("" base64_decode(preg_replace('#-.+-|[rn]#'''$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
                }
        }

        
$asn1 = new File_ASN1();

        
$asn1->loadOIDs($this->oids);

        
$filters = array();
        
$filters['certificationRequestInfo']['subject']['rdnSequence']['value']
            = array(
'type' => FILE_ASN1_TYPE_UTF8_STRING);

        
$asn1->loadFilters($filters);

        
$this->_mapOutAttributes($csr'certificationRequestInfo/attributes'$asn1);
        
$csr $asn1->encodeDER($csr$this->CertificationRequest);

        switch (
$format) {
            case 
FILE_X509_FORMAT_DER:
                return 
$csr;
            
// case FILE_X509_FORMAT_PEM:
            
default:
                return 
"-----BEGIN CERTIFICATE REQUEST-----rn" chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
        }
    }

    
/**
     * Load a SPKAC CSR
     *
     * SPKAC's are produced by the HTML5 keygen element:
     *
     * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
     *
     * @param String $csr
     * @access public
     * @return Mixed
     */
    
function loadSPKAC($spkac)
    {
        if (
is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) {
            unset(
$this->currentCert);
            unset(
$this->currentKeyIdentifier);
            unset(
$this->signatureSubject);
            
$this->currentCert $spkac;
            return 
$spkac;
        }

        
// see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge

        
$asn1 = new File_ASN1();

        
// OpenSSL produces SPKAC's that are preceeded by the string SPKAC=
        
$temp preg_replace('#(?:SPKAC=)|[ rn\]#'''$spkac);
        
$temp preg_match('#^[a-zA-Zd/+]*={0,2}$#'$temp) ? base64_decode($temp) : false;
        if (
$temp != false) {
            
$spkac $temp;
        }
        
$orig $spkac;

        if (
$spkac === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$asn1->loadOIDs($this->oids);
        
$decoded $asn1->decodeBER($spkac);

        if (empty(
$decoded)) {
            
$this->currentCert false;
            return 
false;
        }

        
$spkac $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge);

        if (!isset(
$spkac) || $spkac === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$this->signatureSubject substr($orig$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        
$algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
        
$key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
        
$key $this->_reformatKey($algorithm$key);

        switch (
$algorithm) {
            case 
'rsaEncryption':
                if (!
class_exists('Crypt_RSA')) {
                    include_once 
'Crypt/RSA.php';
                }
                
$this->publicKey = new Crypt_RSA();
                
$this->publicKey->loadKey($key);
                
$this->publicKey->setPublicKey();
                break;
            default:
                
$this->publicKey null;
        }

        
$this->currentKeyIdentifier null;
        
$this->currentCert $spkac;

        return 
$spkac;
    }

    
/**
     * Save a SPKAC CSR request
     *
     * @param Array $csr
     * @param Integer $format optional
     * @access public
     * @return String
     */
    
function saveSPKAC($spkac$format FILE_X509_FORMAT_PEM)
    {
        if (!
is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) {
            return 
false;
        }

        
$algorithm $this->_subArray($spkac'publicKeyAndChallenge/spki/algorithm/algorithm');
        switch (
true) {
            case !
$algorithm:
            case 
is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
                break;
            default:
                switch (
$algorithm) {
                    case 
'rsaEncryption':
                        
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
                            = 
base64_encode("" base64_decode(preg_replace('#-.+-|[rn]#'''$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
                }
        }

        
$asn1 = new File_ASN1();

        
$asn1->loadOIDs($this->oids);
        
$spkac $asn1->encodeDER($spkac$this->SignedPublicKeyAndChallenge);

        switch (
$format) {
            case 
FILE_X509_FORMAT_DER:
                return 
$spkac;
            
// case FILE_X509_FORMAT_PEM:
            
default:
                
// OpenSSL's implementation of SPKAC requires the SPKAC be preceeded by SPKAC= and since there are pretty much
                // no other SPKAC decoders phpseclib will use that same format
                
return 'SPKAC=' base64_encode($spkac);
        }
    }

    
/**
     * Load a Certificate Revocation List
     *
     * @param String $crl
     * @access public
     * @return Mixed
     */
    
function loadCRL($crl)
    {
        if (
is_array($crl) && isset($crl['tbsCertList'])) {
            
$this->currentCert $crl;
            unset(
$this->signatureSubject);
            return 
$crl;
        }

        
$asn1 = new File_ASN1();

        
$crl $this->_extractBER($crl);
        
$orig $crl;

        if (
$crl === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$asn1->loadOIDs($this->oids);
        
$decoded $asn1->decodeBER($crl);

        if (empty(
$decoded)) {
            
$this->currentCert false;
            return 
false;
        }

        
$crl $asn1->asn1map($decoded[0], $this->CertificateList);
        if (!isset(
$crl) || $crl === false) {
            
$this->currentCert false;
            return 
false;
        }

        
$this->signatureSubject substr($orig$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        
$this->_mapInExtensions($crl'tbsCertList/crlExtensions'$asn1);
        
$rclist = &$this->_subArray($crl'tbsCertList/revokedCertificates');
        if (
is_array($rclist)) {
            foreach (
$rclist as $i => $extension) {
                
$this->_mapInExtensions($rclist"$i/crlEntryExtensions"$asn1);
            }
        }

        
$this->currentKeyIdentifier null;
        
$this->currentCert $crl;

        return 
$crl;
    }

    
/**
     * Save Certificate Revocation List.
     *
     * @param Array $crl
     * @param Integer $format optional
     * @access public
     * @return String
     */
    
function saveCRL($crl$format FILE_X509_FORMAT_PEM)
    {
        if (!
is_array($crl) || !isset($crl['tbsCertList'])) {
            return 
false;
        }

        
$asn1 = new File_ASN1();

        
$asn1->loadOIDs($this->oids);

        
$filters = array();
        
$filters['tbsCertList']['issuer']['rdnSequence']['value']
            = array(
'type' => FILE_ASN1_TYPE_UTF8_STRING);
        
$filters['tbsCertList']['signature']['parameters']
            = array(
'type' => FILE_ASN1_TYPE_UTF8_STRING);
        
$filters['signatureAlgorithm']['parameters']
            = array(
'type' => FILE_ASN1_TYPE_UTF8_STRING);

        if (empty(
$crl['tbsCertList']['signature']['parameters'])) {
            
$filters['tbsCertList']['signature']['parameters']
                = array(
'type' => FILE_ASN1_TYPE_NULL);
        }

        if (empty(
$crl['signatureAlgorithm']['parameters'])) {
            
$filters['signatureAlgorithm']['parameters']
                = array(
'type' => FILE_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 
FILE_X509_FORMAT_DER:
                return 
$crl;
            
// case FILE_X509_FORMAT_PEM:
            
default:
                return 
"-----BEGIN X509 CRL-----rn" chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
        }
    }

    
/**
     * Helper function to build a time field according to RFC 3280 section
     *  - 4.1.2.5 Validity
     *  - 5.1.2.4 This Update
     *  - 5.1.2.5 Next Update
     *  - 5.1.2.6 Revoked Certificates
     * by choosing utcTime iff year of date given is before 2050 and generalTime else.
     *
     * @param String $date in format date('D, d M Y H:i:s O')
     * @access private
     * @return Array
     */
    
function _timeField($date)
    {
        
$year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
        
if ($year 2050) {
            return array(
'utcTime' => $date);
        } else {
            return array(
'generalTime' => $date);
        }
    }

    
/**
     * Sign an X.509 certificate
     *
     * $issuer's private key needs to be loaded.
     * $subject can be either an existing X.509 cert (if you want to resign it),
     * a CSR or something with the DN and public key explicitly set.
     *
     * @param File_X509 $issuer
     * @param File_X509 $subject
     * @param String $signatureAlgorithm optional
     * @access public
     * @return Mixed
     */
    
function sign($issuer$subject$signatureAlgorithm 'sha1WithRSAEncryption')
    {
        if (!
is_object($issuer->privateKey) || empty($issuer->dn)) {
            return 
false;
        }

        if (isset(
$subject->publicKey) && !($subjectPublicKey $subject->_formatSubjectPublicKey())) {
            return 
false;
        }

        
$currentCert = isset($this->currentCert) ? $this->currentCert null;
        
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubjectnull;

        if (isset(
$subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
            
$this->currentCert $subject->currentCert;
            
$this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm;
            
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;

            if (!empty(
$this->startDate)) {
                
$this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate);
            }
            if (!empty(
$this->endDate)) {
                
$this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate);
            }
            if (!empty(
$this->serialNumber)) {
                
$this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
            }
            if (!empty(
$subject->dn)) {
                
$this->currentCert['tbsCertificate']['subject'] = $subject->dn;
            }
            if (!empty(
$subject->publicKey)) {
                
$this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
            }
            
$this->removeExtension('id-ce-authorityKeyIdentifier');
            if (isset(
$subject->domains)) {
                
$this->removeExtension('id-ce-subjectAltName');
            }
        } elseif (isset(
$subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) {
            return 
false;
        } else {
            if (!isset(
$subject->publicKey)) {
                return 
false;
            }

            
$startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
            
$endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O'strtotime('+1 year'));
            if (!empty(
$this->serialNumber)) {
                
$serialNumber $this->serialNumber;
            } else {
                if (!
function_exists('crypt_random_string')) {
                    include_once 
'Crypt/Random.php';
                }
                
/* "The serial number MUST be a positive integer"
                   "Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
                    -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2

                   for the integer to be positive the leading bit needs to be 0 hence the
                   application of a bitmap
                */
                
$serialNumber = new Math_BigInteger(crypt_random_string(20) & ("x7F" str_repeat("xFF"19)), 256);
            }

            
$this->currentCert = array(
                
'tbsCertificate' =>
                    array(
                        
'version' => 'v3',
                        
'serialNumber' => $serialNumber// $this->setserialNumber()
                        
'signature' => array('algorithm' => $signatureAlgorithm),
                        
'issuer' => false// this is going to be overwritten later
                        
'validity' => array(
                            
'notBefore' => $this->_timeField($startDate), // $this->setStartDate()
                            
'notAfter' => $this->_timeField($endDate)   // $this->setEndDate()
                        
),
                        
'subject' => $subject->dn,
                        
'subjectPublicKeyInfo' => $subjectPublicKey
                    
),
                    
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    
'signature'          => false // this is going to be overwritten later
            
);

            
// Copy extensions from CSR.
            
$csrexts $subject->getAttribute('pkcs-9-at-extensionRequest'0);

            if (!empty(
$csrexts)) {
                
$this->currentCert['tbsCertificate']['extensions'] = $csrexts;
            }
        }

        
$this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;

        if (isset(
$issuer->currentKeyIdentifier)) {
            
$this->setExtension('id-ce-authorityKeyIdentifier', array(
                    
//'authorityCertIssuer' => array(
                    //    array(
                    //        'directoryName' => $issuer->dn
                    //    )
                    //),
                    
'keyIdentifier' => $issuer->currentKeyIdentifier
                
));
            
//$extensions = &$this->currentCert['tbsCertificate']['extensions'];
            //if (isset($issuer->serialNumber)) {
            //    $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
            //}
            //unset($extensions);
        
}

        if (isset(
$subject->currentKeyIdentifier)) {
            
$this->setExtension('id-ce-subjectKeyIdentifier'$subject->currentKeyIdentifier);
        }

        
$altName = array();

        if (isset(
$subject->domains) && count($subject->domains) > 1) {
            
$altName array_map(array('File_X509''_dnsName'), $subject->domains);
        }

        if (isset(
$subject->ipAddresses) && count($subject->ipAddresses)) {
            
// should an IP address appear as the CN if no domain name is specified? idk
            //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1);
            
$ipAddresses = array();
            foreach (
$subject->ipAddresses as $ipAddress) {
                
$encoded $subject->_ipAddress($ipAddress);
                if (
$encoded !== false) {
                    
$ipAddresses[] = $encoded;
                }
            }
            if (
count($ipAddresses)) {
                
$altName array_merge($altName$ipAddresses);
            }
        }

        if (!empty(
$altName)) {
            
$this->setExtension('id-ce-subjectAltName'$altName);
        }

        if (
$this->caFlag) {
            
$keyUsage $this->getExtension('id-ce-keyUsage');
            if (!
$keyUsage) {
                
$keyUsage = array();
            }

            
$this->setExtension(
                
'id-ce-keyUsage',
                
array_values(array_unique(array_merge($keyUsage, array('cRLSign''keyCertSign'))))
            );

            
$basicConstraints $this->getExtension('id-ce-basicConstraints');
            if (!
$basicConstraints) {
                
$basicConstraints = array();
            }

            
$this->setExtension(
                
'id-ce-basicConstraints',
                
array_unique(array_merge(array('cA' => true), $basicConstraints)),
                
true
            
);

            if (!isset(
$subject->currentKeyIdentifier)) {
                
$this->setExtension('id-ce-subjectKeyIdentifier'base64_encode($this->computeKeyIdentifier($this->currentCert)), falsefalse);
            }
        }

        
// resync $this->signatureSubject
        // save $tbsCertificate in case there are any File_ASN1_Element objects in it
        
$tbsCertificate $this->currentCert['tbsCertificate'];
        
$this->loadX509($this->saveX509($this->currentCert));

        
$result $this->_sign($issuer->privateKey$signatureAlgorithm);
        
$result['tbsCertificate'] = $tbsCertificate;

        
$this->currentCert $currentCert;
        
$this->signatureSubject $signatureSubject;

        return 
$result;
    }

    
/**
     * Sign a CSR
     *
     * @access public
     * @return Mixed
     */
    
function signCSR($signatureAlgorithm 'sha1WithRSAEncryption')
    {
        if (!
is_object($this->privateKey) || empty($this->dn)) {
            return 
false;
        }

        
$origPublicKey $this->publicKey;
        
$class get_class($this->privateKey);
        
$this->publicKey = new $class();
        
$this->publicKey->loadKey($this->privateKey->getPublicKey());
        
$this->publicKey->setPublicKey();
        if (!(
$publicKey $this->_formatSubjectPublicKey())) {
            return 
false;
        }
        
$this->publicKey $origPublicKey;

        
$currentCert = isset($this->currentCert) ? $this->currentCert null;
        
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubjectnull;

        if (isset(
$this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
            
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
            if (!empty(
$this->dn)) {
                
$this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
            }
            
$this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
        } else {
            
$this->currentCert = array(
                
'certificationRequestInfo' =>
                    array(
                        
'version' => 'v1',
                        
'subject' => $this->dn,
                        
'subjectPKInfo' => $publicKey
                    
),
                    
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    
'signature'          => false // this is going to be overwritten later
            
);
        }

        
// resync $this->signatureSubject
        // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
        
$certificationRequestInfo $this->currentCert['certificationRequestInfo'];
        
$this->loadCSR($this->saveCSR($this->currentCert));

        
$result $this->_sign($this->privateKey$signatureAlgorithm);
        
$result['certificationRequestInfo'] = $certificationRequestInfo;

        
$this->currentCert $currentCert;
        
$this->signatureSubject $signatureSubject;

        return 
$result;
    }

    
/**
     * Sign a SPKAC
     *
     * @access public
     * @return Mixed
     */
    
function signSPKAC($signatureAlgorithm 'sha1WithRSAEncryption')
    {
        if (!
is_object($this->privateKey)) {
            return 
false;
        }

        
$origPublicKey $this->publicKey;
        
$class get_class($this->privateKey);
        
$this->publicKey = new $class();
        
$this->publicKey->loadKey($this->privateKey->getPublicKey());
        
$this->publicKey->setPublicKey();
        
$publicKey $this->_formatSubjectPublicKey();
        if (!
$publicKey) {
            return 
false;
        }
        
$this->publicKey $origPublicKey;

        
$currentCert = isset($this->currentCert) ? $this->currentCert null;
        
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubjectnull;

        
// re-signing a SPKAC seems silly but since everything else supports re-signing why not?
        
if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) {
            
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
            
$this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey;
            if (!empty(
$this->challenge)) {
                
// the bitwise AND ensures that the output is a valid IA5String
                
$this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge str_repeat("x7F"strlen($this->challenge));
            }
        } else {
            
$this->currentCert = array(
                
'publicKeyAndChallenge' =>
                    array(
                        
'spki' => $publicKey,
                        
// quoting <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
                        // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified."
                        // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way
                        // we could alternatively do this instead if we ignored the specs:
                        // crypt_random_string(8) & str_repeat("x7F", 8)
                        
'challenge' => !empty($this->challenge) ? $this->challenge ''
                    
),
                    
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    
'signature'          => false // this is going to be overwritten later
            
);
        }

        
// resync $this->signatureSubject
        // save $publicKeyAndChallenge in case there are any File_ASN1_Element objects in it
        
$publicKeyAndChallenge $this->currentCert['publicKeyAndChallenge'];
        
$this->loadSPKAC($this->saveSPKAC($this->currentCert));

        
$result $this->_sign($this->privateKey$signatureAlgorithm);
        
$result['publicKeyAndChallenge'] = $publicKeyAndChallenge;

        
$this->currentCert $currentCert;
        
$this->signatureSubject $signatureSubject;

        return 
$result;
    }

    
/**
     * Sign a CRL
     *
     * $issuer's private key needs to be loaded.
     *
     * @param File_X509 $issuer
     * @param File_X509 $crl
     * @param String $signatureAlgorithm optional
     * @access public
     * @return Mixed
     */
    
function signCRL($issuer$crl$signatureAlgorithm 'sha1WithRSAEncryption')
    {
        if (!
is_object($issuer->privateKey) || empty($issuer->dn)) {
            return 
false;
        }

        
$currentCert = isset($this->currentCert) ? $this->currentCert null;
        
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject null;
        
$thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');

        if (isset(
$crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
            
$this->currentCert $crl->currentCert;
            
$this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
            
$this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
        } else {
            
$this->currentCert = array(
                
'tbsCertList' =>
                    array(
                        
'version' => 'v2',
                        
'signature' => array('algorithm' => $signatureAlgorithm),
                        
'issuer' => false// this is going to be overwritten later
                        
'thisUpdate' => $this->_timeField($thisUpdate// $this->setStartDate()
                    
),
                    
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    
'signature'          => false // this is going to be overwritten later
            
);
        }

        
$tbsCertList = &$this->currentCert['tbsCertList'];
        
$tbsCertList['issuer'] = $issuer->dn;
        
$tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate);

        if (!empty(
$this->endDate)) {
            
$tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate()
        
} else {
            unset(
$tbsCertList['nextUpdate']);
        }

        if (!empty(
$this->serialNumber)) {
            
$crlNumber $this->serialNumber;
        } else {
            
$crlNumber $this->getExtension('id-ce-cRLNumber');
            
// "The CRL number is a non-critical CRL extension that conveys a
            //  monotonically increasing sequence number for a given CRL scope and
            //  CRL issuer.  This extension allows users to easily determine when a
            //  particular CRL supersedes another CRL."
            // -- https://tools.ietf.org/html/rfc5280#section-5.2.3
            
$crlNumber $crlNumber !== false $crlNumber->add(new Math_BigInteger(1)) : null;
        }

        
$this->removeExtension('id-ce-authorityKeyIdentifier');
        
$this->removeExtension('id-ce-issuerAltName');

        
// Be sure version >= v2 if some extension found.
        
$version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0;
        if (!
$version) {
            if (!empty(
$tbsCertList['crlExtensions'])) {
                
$version 1// v2.
            
} elseif (!empty($tbsCertList['revokedCertificates'])) {
                foreach (
$tbsCertList['revokedCertificates'] as $cert) {
                    if (!empty(
$cert['crlEntryExtensions'])) {
                        
$version 1// v2.
                    
}
                }
            }

            if (
$version) {
                
$tbsCertList['version'] = $version;
            }
        }

        
// Store additional extensions.
        
if (!empty($tbsCertList['version'])) { // At least v2.
            
if (!empty($crlNumber)) {
                
$this->setExtension('id-ce-cRLNumber'$crlNumber);
            }

            if (isset(
$issuer->currentKeyIdentifier)) {
                
$this->setExtension('id-ce-authorityKeyIdentifier', array(
                        
//'authorityCertIssuer' => array(
                        //    array(
                        //        'directoryName' => $issuer->dn
                        //    )
                        //),
                        
'keyIdentifier' => $issuer->currentKeyIdentifier
                    
));
                
//$extensions = &$tbsCertList['crlExtensions'];
                //if (isset($issuer->serialNumber)) {
                //    $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
                //}
                //unset($extensions);
            
}

            
$issuerAltName $this->getExtension('id-ce-subjectAltName'$issuer->currentCert);

            if (
$issuerAltName !== false) {
                
$this->setExtension('id-ce-issuerAltName'$issuerAltName);
            }
        }

        if (empty(
$tbsCertList['revokedCertificates'])) {
            unset(
$tbsCertList['revokedCertificates']);
        }

        unset(
$tbsCertList);

        
// resync $this->signatureSubject
        // save $tbsCertList in case there are any File_ASN1_Element objects in it
        
$tbsCertList $this->currentCert['tbsCertList'];
        
$this->loadCRL($this->saveCRL($this->currentCert));

        
$result $this->_sign($issuer->privateKey$signatureAlgorithm);
        
$result['tbsCertList'] = $tbsCertList;

        
$this->currentCert $currentCert;
        
$this->signatureSubject $signatureSubject;

        return 
$result;
    }

    
/**
     * X.509 certificate signing helper function.
     *
     * @param Object $key
     * @param File_X509 $subject
     * @param String $signatureAlgorithm
     * @access public
     * @return Mixed
     */
    
function _sign($key$signatureAlgorithm)
    {
        switch (
strtolower(get_class($key))) {
            case 
'crypt_rsa':
                switch (
$signatureAlgorithm) {
                    case 
'md2WithRSAEncryption':
                    case 
'md5WithRSAEncryption':
                    case 
'sha1WithRSAEncryption':
                    case 
'sha224WithRSAEncryption':
                    case 
'sha256WithRSAEncryption':
                    case 
'sha384WithRSAEncryption':
                    case 
'sha512WithRSAEncryption':
                        
$key->setHash(preg_replace('#WithRSAEncryption$#'''$signatureAlgorithm));
                        
$key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);

                        
$this->currentCert['signature'] = base64_encode("" $key->sign($this->signatureSubject));
                        return 
$this->currentCert;
                }
            default:
                return 
false;
        }
    }

    
/**
     * Set certificate start date
     *
     * @param String $date
     * @access public
     */
    
function setStartDate($date)
    {
        
$this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
    }

    
/**
     * Set certificate end date
     *
     * @param String $date
     * @access public
     */
    
function setEndDate($date)
    {
        
/*
          To indicate that a certificate has no well-defined expiration date,
          the notAfter SHOULD be assigned the GeneralizedTime value of
          99991231235959Z.

          -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
        */
        
if (strtolower($date) == 'lifetime') {
            
$temp '99991231235959Z';
            
$asn1 = new File_ASN1();
            
$temp chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
            
$this->endDate = new File_ASN1_Element($temp);
        } else {
            
$this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
        }
    }

    
/**
     * Set Serial Number
     *
     * @param String $serial
     * @param $base optional
     * @access public
     */
    
function setSerialNumber($serial$base = -256)
    {
        
$this->serialNumber = new Math_BigInteger($serial$base);
    }

    
/**
     * Turns the certificate into a certificate authority
     *
     * @access public
     */
    
function makeCA()
    {
        
$this->caFlag true;
    }

    
/**
     * Get a reference to a subarray
     *
     * @param array $root
     * @param String $path  absolute path with / as component separator
     * @param Boolean $create optional
     * @access private
     * @return array item ref or false
     */
    
function &_subArray(&$root$path$create false)
    {
        
$false false;

        if (!
is_array($root)) {
            return 
$false;
        }

        foreach (
explode('/'$path) as $i) {
            if (!
is_array($root)) {
                return 
$false;
            }

            if (!isset(
$root[$i])) {
                if (!
$create) {
                    return 
$false;
                }

                
$root[$i] = array();
            }

            
$root = &$root[$i];
        }

        return 
$root;
    }

    
/**
     * Get a reference to an extension subarray
     *
     * @param array $root
     * @param String $path optional absolute path with / as component separator
     * @param Boolean $create optional
     * @access private
     * @return array ref or false
     */
    
function &_extensions(&$root$path null$create false)
    {
        if (!isset(
$root)) {
            
$root $this->currentCert;
        }

        switch (
true) {
            case !empty(
$path):
            case !
is_array($root):
                break;
            case isset(
$root['tbsCertificate']):
                
$path 'tbsCertificate/extensions';
                break;
            case isset(
$root['tbsCertList']):
                
$path 'tbsCertList/crlExtensions';
                break;
            case isset(
$root['certificationRequestInfo']):
                
$pth 'certificationRequestInfo/attributes';
                
$attributes = &$this->_subArray($root$pth$create);

                if (
is_array($attributes)) {
                    foreach (
$attributes as $key => $value) {
                        if (
$value['type'] == 'pkcs-9-at-extensionRequest') {
                            
$path "$pth/$key/value/0";
                            break 
2;
                        }
                    }
                    if (
$create) {
                        
$key count($attributes);
                        
$attributes[] = array('type' => 'pkcs-9-at-extensionRequest''value' => array());
                        
$path "$pth/$key/value/0";
                    }
                }
                break;
        }

        
$extensions = &$this->_subArray($root$path$create);

        if (!
is_array($extensions)) {
            
$false false;
            return 
$false;
        }

        return 
$extensions;
    }

    
/**
     * Remove an Extension
     *
     * @param String $id
     * @param String $path optional
     * @access private
     * @return Boolean
     */
    
function _removeExtension($id$path null)
    {
        
$extensions = &$this->_extensions($this->currentCert$path);

        if (!
is_array($extensions)) {
            return 
false;
        }

        
$result false;
        foreach (
$extensions as $key => $value) {
            if (
$value['extnId'] == $id) {
                unset(
$extensions[$key]);
                
$result true;
            }
        }

        
$extensions array_values($extensions);
        return 
$result;
    }

    
/**
     * Get an Extension
     *
     * Returns the extension if it exists and false if not
     *
     * @param String $id
     * @param Array $cert optional
     * @param String $path optional
     * @access private
     * @return Mixed
     */
    
function _getExtension($id$cert null$path null)
    {
        
$extensions $this->_extensions($cert$path);

        if (!
is_array($extensions)) {
            return 
false;
        }

        foreach (
$extensions as $key => $value) {
            if (
$value['extnId'] == $id) {
                return 
$value['extnValue'];
            }
        }

        return 
false;
    }

    
/**
     * Returns a list of all extensions in use
     *
     * @param array $cert optional
     * @param String $path optional
     * @access private
     * @return Array
     */
    
function _getExtensions($cert null$path null)
    {
        
$exts $this->_extensions($cert$path);
        
$extensions = array();

        if (
is_array($exts)) {
            foreach (
$exts as $extension) {
                
$extensions[] = $extension['extnId'];
            }
        }

        return 
$extensions;
    }

    
/**
     * Set an Extension
     *
     * @param String $id
     * @param Mixed $value
     * @param Boolean $critical optional
     * @param Boolean $replace optional
     * @param String $path optional
     * @access private
     * @return Boolean
     */
    
function _setExtension($id$value$critical false$replace true$path null)
    {
        
$extensions = &$this->_extensions($this->currentCert$pathtrue);

        if (!
is_array($extensions)) {
            return 
false;
        }

        
$newext = array('extnId'  => $id'critical' => $critical'extnValue' => $value);

        foreach (
$extensions as $key => $value) {
            if (
$value['extnId'] == $id) {
                if (!
$replace) {
                    return 
false;
                }

                
$extensions[$key] = $newext;
                return 
true;
            }
        }

        
$extensions[] = $newext;
        return 
true;
    }

    
/**
     * Remove a certificate, CSR or CRL Extension
     *
     * @param String $id
     * @access public
     * @return Boolean
     */
    
function removeExtension($id)
    {
        return 
$this->_removeExtension($id);
    }

    
/**
     * Get a certificate, CSR or CRL Extension
     *
     * Returns the extension if it exists and false if not
     *
     * @param String $id
     * @param Array $cert optional
     * @access public
     * @return Mixed
     */
    
function getExtension($id$cert null)
    {
        return 
$this->_getExtension($id$cert);
    }

    
/**
     * Returns a list of all extensions in use in certificate, CSR or CRL
     *
     * @param array $cert optional
     * @access public
     * @return Array
     */
    
function getExtensions($cert null)
    {
        return 
$this->_getExtensions($cert);
    }

    
/**
     * Set a certificate, CSR or CRL Extension
     *
     * @param String $id
     * @param Mixed $value
     * @param Boolean $critical optional
     * @param Boolean $replace optional
     * @access public
     * @return Boolean
     */
    
function setExtension($id$value$critical false$replace true)
    {
        return 
$this->_setExtension($id$value$critical$replace);
    }

    
/**
     * Remove a CSR attribute.
     *
     * @param String $id
     * @param Integer $disposition optional
     * @access public
     * @return Boolean
     */
    
function removeAttribute($id$disposition FILE_X509_ATTR_ALL)
    {
        
$attributes = &$this->_subArray($this->currentCert'certificationRequestInfo/attributes');

        if (!
is_array($attributes)) {
            return 
false;
        }

        
$result false;
        foreach (
$attributes as $key => $attribute) {
            if (
$attribute['type'] == $id) {
                
$n count($attribute['value']);
                switch (
true) {
                    case 
$disposition == FILE_X509_ATTR_APPEND:
                    case 
$disposition == FILE_X509_ATTR_REPLACE:
                        return 
false;
                    case 
$disposition >= $n:
                        
$disposition -= $n;
                        break;
                    case 
$disposition == FILE_X509_ATTR_ALL:
                    case 
$n == 1:
                        unset(
$attributes[$key]);
                        
$result true;
                        break;
                    default:
                        unset(
$attributes[$key]['value'][$disposition]);
                        
$attributes[$key]['value'] = array_values($attributes[$key]['value']);
                        
$result true;
                        break;
                }
                if (
$result && $disposition != FILE_X509_ATTR_ALL) {
                    break;
                }
            }
        }

        
$attributes array_values($attributes);
        return 
$result;
    }

    
/**
     * Get a CSR attribute
     *
     * Returns the attribute if it exists and false if not
     *
     * @param String $id
     * @param Integer $disposition optional
     * @param Array $csr optional
     * @access public
     * @return Mixed
     */
    
function getAttribute($id$disposition FILE_X509_ATTR_ALL$csr null)
    {
        if (empty(
$csr)) {
            
$csr $this->currentCert;
        }

        
$attributes $this->_subArray($csr'certificationRequestInfo/attributes');

        if (!
is_array($attributes)) {
            return 
false;
        }

        foreach (
$attributes as $key => $attribute) {
            if (
$attribute['type'] == $id) {
                
$n count($attribute['value']);
                switch (
true) {
                    case 
$disposition == FILE_X509_ATTR_APPEND:
                    case 
$disposition == FILE_X509_ATTR_REPLACE:
                        return 
false;
                    case 
$disposition == FILE_X509_ATTR_ALL:
                        return 
$attribute['value'];
                    case 
$disposition >= $n:
                        
$disposition -= $n;
                        break;
                    default:
                        return 
$attribute['value'][$disposition];
                }
            }
        }

        return 
false;
    }

    
/**
     * Returns a list of all CSR attributes in use
     *
     * @param array $csr optional
     * @access public
     * @return Array
     */
    
function getAttributes($csr null)
    {
        if (empty(
$csr)) {
            
$csr $this->currentCert;
        }

        
$attributes $this->_subArray($csr'certificationRequestInfo/attributes');
        
$attrs = array();

        if (
is_array($attributes)) {
            foreach (
$attributes as $attribute) {
                
$attrs[] = $attribute['type'];
            }
        }

        return 
$attrs;
    }

    
/**
     * Set a CSR attribute
     *
     * @param String $id
     * @param Mixed $value
     * @param Boolean $disposition optional
     * @access public
     * @return Boolean
     */
    
function setAttribute($id$value$disposition FILE_X509_ATTR_ALL)
    {
        
$attributes = &$this->_subArray($this->currentCert'certificationRequestInfo/attributes'true);

        if (!
is_array($attributes)) {
            return 
false;
        }

        switch (
$disposition) {
            case 
FILE_X509_ATTR_REPLACE:
                
$disposition FILE_X509_ATTR_APPEND;
            case 
FILE_X509_ATTR_ALL:
                
$this->removeAttribute($id);
                break;
        }

        foreach (
$attributes as $key => $attribute) {
            if (
$attribute['type'] == $id) {
                
$n count($attribute['value']);
                switch (
true) {
                    case 
$disposition == FILE_X509_ATTR_APPEND:
                        
$last $key;
                        break;
                    case 
$disposition >= $n:
                        
$disposition -= $n;
                        break;
                    default:
                        
$attributes[$key]['value'][$disposition] = $value;
                        return 
true;
                }
            }
        }

        switch (
true) {
            case 
$disposition >= 0:
                return 
false;
            case isset(
$last):
                
$attributes[$last]['value'][] = $value;
                break;
            default:
                
$attributes[] = array('type' => $id'value' => $disposition == FILE_X509_ATTR_ALL $value: array($value));
                break;
        }

        return 
true;
    }

    
/**
     * Sets the subject key identifier
     *
     * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
     *
     * @param String $value
     * @access public
     */
    
function setKeyIdentifier($value)
    {
        if (empty(
$value)) {
            unset(
$this->currentKeyIdentifier);
        } else {
            
$this->currentKeyIdentifier base64_encode($value);
        }
    }

    
/**
     * Compute a public key identifier.
     *
     * Although key identifiers may be set to any unique value, this function
     * computes key identifiers from public key according to the two
     * recommended methods (4.2.1.2 RFC 3280).
     * Highly polymorphic: try to accept all possible forms of key:
     * - Key object
     * - File_X509 object with public or private key defined
     * - Certificate or CSR array
     * - File_ASN1_Element object
     * - PEM or DER string
     *
     * @param Mixed $key optional
     * @param Integer $method optional
     * @access public
     * @return String binary key identifier
     */
    
function computeKeyIdentifier($key null$method 1)
    {
        if (
is_null($key)) {
            
$key $this;
        }

        switch (
true) {
            case 
is_string($key):
                break;
            case 
is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
                return 
$this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
            case 
is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
                return 
$this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
            case !
is_object($key):
                return 
false;
            case 
strtolower(get_class($key)) == 'file_asn1_element':
                
// Assume the element is a bitstring-packed key.
                
$asn1 = new File_ASN1();
                
$decoded $asn1->decodeBER($key->element);
                if (empty(
$decoded)) {
                    return 
false;
                }
                
$raw $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING));
                if (empty(
$raw)) {
                    return 
false;
                }
                
$raw base64_decode($raw);
                
// If the key is private, compute identifier from its corresponding public key.
                
if (!class_exists('Crypt_RSA')) {
                    include_once 
'Crypt/RSA.php';
                }
                
$key = new Crypt_RSA();
                if (!
$key->loadKey($raw)) {
                    return 
false;   // Not an unencrypted RSA key.
                
}
                if (
$key->getPrivateKey() !== false) {  // If private.
                    
return $this->computeKeyIdentifier($key$method);
                }
                
$key $raw;    // Is a public key.
                
break;
            case 
strtolower(get_class($key)) == 'file_x509':
                if (isset(
$key->publicKey)) {
                    return 
$this->computeKeyIdentifier($key->publicKey$method);
                }
                if (isset(
$key->privateKey)) {
                    return 
$this->computeKeyIdentifier($key->privateKey$method);
                }
                if (isset(
$key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
                    return 
$this->computeKeyIdentifier($key->currentCert$method);
                }
                return 
false;
            default: 
// Should be a key object (i.e.: Crypt_RSA).
                
$key $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
                break;
        }

        
// If in PEM format, convert to binary.
        
$key $this->_extractBER($key);

        
// Now we have the key string: compute its sha-1 sum.
        
if (!class_exists('Crypt_Hash')) {
            include_once 
'Crypt/Hash.php';
        }
        
$hash = new Crypt_Hash('sha1');
        
$hash $hash->hash($key);

        if (
$method == 2) {
            
$hash substr($hash, -8);
            
$hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
        }

        return 
$hash;
    }

    
/**
     * Format a public key as appropriate
     *
     * @access private
     * @return Array
     */
    
function _formatSubjectPublicKey()
    {
        if (!isset(
$this->publicKey) || !is_object($this->publicKey)) {
            return 
false;
        }

        switch (
strtolower(get_class($this->publicKey))) {
            case 
'crypt_rsa':
                
// the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
                // the former is a good example of how to do fuzzing on the public key
                //return new File_ASN1_Element(base64_decode(preg_replace('#-.+-|[rn]#', '', $this->publicKey->getPublicKey())));
                
return array(
                    
'algorithm' => array('algorithm' => 'rsaEncryption'),
                    
'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
                );
            default:
                return 
false;
        }
    }

    
/**
     * Set the domain name's which the cert is to be valid for
     *
     * @access public
     * @return Array
     */
    
function setDomain()
    {
        
$this->domains func_get_args();
        
$this->removeDNProp('id-at-commonName');
        
$this->setDNProp('id-at-commonName'$this->domains[0]);
    }

    
/**
     * Set the IP Addresses's which the cert is to be valid for
     *
     * @access public
     * @param String $ipAddress optional
     */
    
function setIPAddress()
    {
        
$this->ipAddresses func_get_args();
        
/*
        if (!isset($this->domains)) {
            $this->removeDNProp('id-at-commonName');
            $this->setDNProp('id-at-commonName', $this->ipAddresses[0]);
        }
        */
    
}

    
/**
     * Helper function to build domain array
     *
     * @access private
     * @param String $domain
     * @return Array
     */
    
function _dnsName($domain)
    {
        return array(
'dNSName' => $domain);
    }

    
/**
     * Helper function to build IP Address array
     *
     * (IPv6 is not currently supported)
     *
     * @access private
     * @param String $address
     * @return Array
     */
    
function _iPAddress($address)
    {
        return array(
'iPAddress' => $address);
    }

    
/**
     * Get the index of a revoked certificate.
     *
     * @param array $rclist
     * @param String $serial
     * @param Boolean $create optional
     * @access private
     * @return Integer or false
     */
    
function _revokedCertificate(&$rclist$serial$create false)
    {
        
$serial = new Math_BigInteger($serial);

        foreach (
$rclist as $i => $rc) {
            if (!(
$serial->compare($rc['userCertificate']))) {
                return 
$i;
            }
        }

        if (!
$create) {
            return 
false;
        }

        
$i count($rclist);
        
$rclist[] = array('userCertificate' => $serial,
                          
'revocationDate'  => $this->_timeField(@date('D, d M Y H:i:s O')));
        return 
$i;
    }

    
/**
     * Revoke a certificate.
     *
     * @param String $serial
     * @param String $date optional
     * @access public
     * @return Boolean
     */
    
function revoke($serial$date null)
    {
        if (isset(
$this->currentCert['tbsCertList'])) {
            if (
is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'true))) {
                if (
$this->_revokedCertificate($rclist$serial) === false) { // If not yet revoked
                    
if (($i $this->_revokedCertificate($rclist$serialtrue)) !== false) {
                        if (!empty(
$date)) {
                            
$rclist[$i]['revocationDate'] = $this->_timeField($date);
                        }

                        return 
true;
                    }
                }
            }
        }

        return 
false;
    }

    
/**
     * Unrevoke a certificate.
     *
     * @param String $serial
     * @access public
     * @return Boolean
     */
    
function unrevoke($serial)
    {
        if (
is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'))) {
            if ((
$i $this->_revokedCertificate($rclist$serial)) !== false) {
                unset(
$rclist[$i]);
                
$rclist array_values($rclist);
                return 
true;
            }
        }

        return 
false;
    }

    
/**
     * Get a revoked certificate.
     *
     * @param String $serial
     * @access public
     * @return Mixed
     */
    
function getRevoked($serial)
    {
        if (
is_array($rclist $this->_subArray($this->currentCert'tbsCertList/revokedCertificates'))) {
            if ((
$i $this->_revokedCertificate($rclist$serial)) !== false) {
                return 
$rclist[$i];
            }
        }

        return 
false;
    }

    
/**
     * List revoked certificates
     *
     * @param array $crl optional
     * @access public
     * @return array
     */
    
function listRevoked($crl null)
    {
        if (!isset(
$crl)) {
            
$crl $this->currentCert;
        }

        if (!isset(
$crl['tbsCertList'])) {
            return 
false;
        }

        
$result = array();

        if (
is_array($rclist $this->_subArray($crl'tbsCertList/revokedCertificates'))) {
            foreach (
$rclist as $rc) {
                
$result[] = $rc['userCertificate']->toString();
            }
        }

        return 
$result;
    }

    
/**
     * Remove a Revoked Certificate Extension
     *
     * @param String $serial
     * @param String $id
     * @access public
     * @return Boolean
     */
    
function removeRevokedCertificateExtension($serial$id)
    {
        if (
is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'))) {
            if ((
$i $this->_revokedCertificate($rclist$serial)) !== false) {
                return 
$this->_removeExtension($id"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
            }
        }

        return 
false;
    }

    
/**
     * Get a Revoked Certificate Extension
     *
     * Returns the extension if it exists and false if not
     *
     * @param String $serial
     * @param String $id
     * @param Array $crl optional
     * @access public
     * @return Mixed
     */
    
function getRevokedCertificateExtension($serial$id$crl null)
    {
        if (!isset(
$crl)) {
            
$crl $this->currentCert;
        }

        if (
is_array($rclist $this->_subArray($crl'tbsCertList/revokedCertificates'))) {
            if ((
$i $this->_revokedCertificate($rclist$serial)) !== false) {
                return 
$this->_getExtension($id$crl"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
            }
        }

        return 
false;
    }

    
/**
     * Returns a list of all extensions in use for a given revoked certificate
     *
     * @param String $serial
     * @param array $crl optional
     * @access public
     * @return Array
     */
    
function getRevokedCertificateExtensions($serial$crl null)
    {
        if (!isset(
$crl)) {
            
$crl $this->currentCert;
        }

        if (
is_array($rclist $this->_subArray($crl'tbsCertList/revokedCertificates'))) {
            if ((
$i $this->_revokedCertificate($rclist$serial)) !== false) {
                return 
$this->_getExtensions($crl"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
            }
        }

        return 
false;
    }

    
/**
     * Set a Revoked Certificate Extension
     *
     * @param String $serial
     * @param String $id
     * @param Mixed $value
     * @param Boolean $critical optional
     * @param Boolean $replace optional
     * @access public
     * @return Boolean
     */
    
function setRevokedCertificateExtension($serial$id$value$critical false$replace true)
    {
        if (isset(
$this->currentCert['tbsCertList'])) {
            if (
is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'true))) {
                if ((
$i $this->_revokedCertificate($rclist$serialtrue)) !== false) {
                    return 
$this->_setExtension($id$value$critical$replace"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
                }
            }
        }

        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('#.*?^-+[^-]+-+#ms'''$str1);
        
// 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-Zd/+]*={0,2}$#'$temp) ? base64_decode($temp) : false;
        return 
$temp != false $temp $str;
    }
}
Онлайн: 1
Реклама