PHPSECLIB 0.3.1 added to the project to support SFTP transfers of lab orders and...
[openemr.git] / library / phpseclib / File / X509.php
blob08de83410cafc349b8bdbab9a7c316bdd77acbda
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 /**
5 * Pure-PHP X.509 Parser
7 * PHP versions 4 and 5
9 * Encode and decode X.509 certificates.
11 * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
12 * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
14 * Note that loading an X.509 certificate and resaving it may invalidate the signature. The reason being that the signature is based on a
15 * portion of the certificate that contains optional parameters with default values. ie. if the parameter isn't there the default value is
16 * 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
17 * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the
18 * the certificate all together unless the certificate is re-signed.
20 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
21 * of this software and associated documentation files (the "Software"), to deal
22 * in the Software without restriction, including without limitation the rights
23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 * copies of the Software, and to permit persons to whom the Software is
25 * furnished to do so, subject to the following conditions:
27 * The above copyright notice and this permission notice shall be included in
28 * all copies or substantial portions of the Software.
30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36 * THE SOFTWARE.
38 * @category File
39 * @package File_X509
40 * @author Jim Wigginton <terrafrost@php.net>
41 * @copyright MMXII Jim Wigginton
42 * @license http://www.opensource.org/licenses/mit-license.html MIT License
43 * @version $Id$
44 * @link htp://phpseclib.sourceforge.net
47 /**
48 * Include File_ASN1
50 if (!class_exists('File_ASN1')) {
51 require_once('File/ASN1.php');
54 /**
55 * Flag to only accept signatures signed by certificate authorities
57 * @access public
58 * @see File_X509::validateSignature()
60 define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1);
62 /**#@+
63 * @access public
64 * @see File_X509::getDN()
66 /**
67 * Return internal array representation
69 define('FILE_X509_DN_ARRAY', 0); // Internal array representation.
70 /**
71 * Return string
73 define('FILE_X509_DN_STRING', 1);
74 /**
75 * Return ASN.1 name string
77 define('FILE_X509_DN_ASN1', 2);
78 /**
79 * Return OpenSSL compatible array
81 define('FILE_X509_DN_OPENSSL', 3);
82 /**
83 * Return canonical ASN.1 RDNs string
85 define('FILE_X509_DN_CANON', 4);
86 /**
87 * Return name ash for file indexing
89 define('FILE_X509_DN_HASH', 5);
90 /**#@-*/
92 /**
93 * Pure-PHP X.509 Parser
95 * @author Jim Wigginton <terrafrost@php.net>
96 * @version 0.3.0
97 * @access public
98 * @package File_X509
100 class File_X509 {
102 * ASN.1 syntax for X.509 certificates
104 * @var Array
105 * @access private
107 var $Certificate;
109 /**#@+
110 * ASN.1 syntax for various extensions
112 * @access private
114 var $KeyUsage;
115 var $ExtKeyUsageSyntax;
116 var $BasicConstraints;
117 var $KeyIdentifier;
118 var $CRLDistributionPoints;
119 var $AuthorityKeyIdentifier;
120 var $CertificatePolicies;
121 var $AuthorityInfoAccessSyntax;
122 var $SubjectAltName;
123 var $PrivateKeyUsagePeriod;
124 var $IssuerAltName;
125 var $PolicyMappings;
126 var $NameConstraints;
128 var $CPSuri;
129 var $UserNotice;
131 var $netscape_cert_type;
132 var $netscape_comment;
133 var $netscape_ca_policy_url;
135 var $Name;
136 var $RelativeDistinguishedName;
137 var $CRLNumber;
138 var $CRLReason;
139 var $IssuingDistributionPoint;
140 var $InvalidityDate;
141 var $CertificateIssuer;
142 /**#@-*/
145 * ASN.1 syntax for Certificate Signing Requests (RFC2986)
147 * @var Array
148 * @access private
150 var $CertificationRequest;
153 * ASN.1 syntax for Certificate Revocation Lists (RFC5280)
155 * @var Array
156 * @access private
158 var $CertificateList;
161 * Distinguished Name
163 * @var Array
164 * @access private
166 var $dn;
169 * Public key
171 * @var String
172 * @access private
174 var $publicKey;
177 * Private key
179 * @var String
180 * @access private
182 var $privateKey;
185 * Object identifiers for X.509 certificates
187 * @var Array
188 * @access private
189 * @link http://en.wikipedia.org/wiki/Object_identifier
191 var $oids;
194 * The certificate authorities
196 * @var Array
197 * @access private
199 var $CAs;
202 * The currently loaded certificate
204 * @var Array
205 * @access private
207 var $currentCert;
210 * The signature subject
212 * There's no guarantee File_X509 is going to reencode an X.509 cert in the same way it was originally
213 * encoded so we take save the portion of the original cert that the signature would have made for.
215 * @var String
216 * @access private
218 var $signatureSubject;
221 * Certificate Start Date
223 * @var String
224 * @access private
226 var $startDate;
229 * Certificate End Date
231 * @var String
232 * @access private
234 var $endDate;
237 * Serial Number
239 * @var String
240 * @access private
242 var $serialNumber;
245 * Key Identifier
247 * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and
248 * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
250 * @var String
251 * @access private
253 var $currentKeyIdentifier;
256 * CA Flag
258 * @var Boolean
259 * @access private
261 var $caFlag = false;
264 * Default Constructor.
266 * @return File_X509
267 * @access public
269 function File_X509()
271 // Explicitly Tagged Module, 1988 Syntax
272 // http://tools.ietf.org/html/rfc5280#appendix-A.1
274 $DirectoryString = array(
275 'type' => FILE_ASN1_TYPE_CHOICE,
276 'children' => array(
277 'teletexString' => array('type' => FILE_ASN1_TYPE_TELETEX_STRING),
278 'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
279 'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING),
280 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING),
281 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING)
285 $AttributeValue = array('type' => FILE_ASN1_TYPE_ANY);
287 $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
289 $AttributeTypeAndValue = array(
290 'type' => FILE_ASN1_TYPE_SEQUENCE,
291 'children' => array(
292 'type' => $AttributeType,
293 'value'=> $AttributeValue
298 In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
299 but they can be useful at times when either there is no unique attribute in the entry or you
300 want to ensure that the entry's DN contains some useful identifying information.
302 - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
304 $this->RelativeDistinguishedName = array(
305 'type' => FILE_ASN1_TYPE_SET,
306 'min' => 1,
307 'max' => -1,
308 'children' => $AttributeTypeAndValue
311 // http://tools.ietf.org/html/rfc5280#section-4.1.2.4
312 $RDNSequence = array(
313 'type' => FILE_ASN1_TYPE_SEQUENCE,
314 // RDNSequence does not define a min or a max, which means it doesn't have one
315 'min' => 0,
316 'max' => -1,
317 'children' => $this->RelativeDistinguishedName
320 $this->Name = array(
321 'type' => FILE_ASN1_TYPE_CHOICE,
322 'children' => array(
323 'rdnSequence' => $RDNSequence
327 // http://tools.ietf.org/html/rfc5280#section-4.1.1.2
328 $AlgorithmIdentifier = array(
329 'type' => FILE_ASN1_TYPE_SEQUENCE,
330 'children' => array(
331 'algorithm' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
332 'parameters' => array(
333 'type' => FILE_ASN1_TYPE_ANY,
334 'optional' => true
340 A certificate using system MUST reject the certificate if it encounters
341 a critical extension it does not recognize; however, a non-critical
342 extension may be ignored if it is not recognized.
344 http://tools.ietf.org/html/rfc5280#section-4.2
346 $Extension = array(
347 'type' => FILE_ASN1_TYPE_SEQUENCE,
348 'children' => array(
349 'extnId' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
350 'critical' => array(
351 'type' => FILE_ASN1_TYPE_BOOLEAN,
352 'optional' => true,
353 'default' => false
355 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING)
359 $Extensions = array(
360 'type' => FILE_ASN1_TYPE_SEQUENCE,
361 'min' => 1,
362 // technically, it's MAX, but we'll assume anything < 0 is MAX
363 'max' => -1,
364 // if 'children' isn't an array then 'min' and 'max' must be defined
365 'children' => $Extension
368 $SubjectPublicKeyInfo = array(
369 'type' => FILE_ASN1_TYPE_SEQUENCE,
370 'children' => array(
371 'algorithm' => $AlgorithmIdentifier,
372 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
376 $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING);
378 $Time = array(
379 'type' => FILE_ASN1_TYPE_CHOICE,
380 'children' => array(
381 'utcTime' => array('type' => FILE_ASN1_TYPE_UTC_TIME),
382 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
386 // http://tools.ietf.org/html/rfc5280#section-4.1.2.5
387 $Validity = array(
388 'type' => FILE_ASN1_TYPE_SEQUENCE,
389 'children' => array(
390 'notBefore' => $Time,
391 'notAfter' => $Time
395 $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
397 $Version = array(
398 'type' => FILE_ASN1_TYPE_INTEGER,
399 'mapping' => array('v1', 'v2', 'v3')
402 // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
403 $TBSCertificate = array(
404 'type' => FILE_ASN1_TYPE_SEQUENCE,
405 'children' => array(
406 // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
407 // reenforce that fact
408 'version' => array(
409 'constant' => 0,
410 'optional' => true,
411 'explicit' => true,
412 'default' => 'v1'
413 ) + $Version,
414 'serialNumber' => $CertificateSerialNumber,
415 'signature' => $AlgorithmIdentifier,
416 'issuer' => $this->Name,
417 'validity' => $Validity,
418 'subject' => $this->Name,
419 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
420 // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
421 'issuerUniqueID' => array(
422 'constant' => 1,
423 'optional' => true,
424 'implicit' => true
425 ) + $UniqueIdentifier,
426 'subjectUniqueID' => array(
427 'constant' => 2,
428 'optional' => true,
429 'implicit' => true
430 ) + $UniqueIdentifier,
431 // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
432 // it's not IMPLICIT, it's EXPLICIT
433 'extensions' => array(
434 'constant' => 3,
435 'optional' => true,
436 'explicit' => true
437 ) + $Extensions
441 $this->Certificate = array(
442 'type' => FILE_ASN1_TYPE_SEQUENCE,
443 'children' => array(
444 'tbsCertificate' => $TBSCertificate,
445 'signatureAlgorithm' => $AlgorithmIdentifier,
446 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
450 $this->KeyUsage = array(
451 'type' => FILE_ASN1_TYPE_BIT_STRING,
452 'mapping' => array(
453 'digitalSignature',
454 'nonRepudiation',
455 'keyEncipherment',
456 'dataEncipherment',
457 'keyAgreement',
458 'keyCertSign',
459 'cRLSign',
460 'encipherOnly',
461 'decipherOnly'
465 $this->BasicConstraints = array(
466 'type' => FILE_ASN1_TYPE_SEQUENCE,
467 'children' => array(
468 'cA' => array(
469 'type' => FILE_ASN1_TYPE_BOOLEAN,
470 'optional' => true,
471 'default' => false
473 'pathLenConstraint' => array(
474 'type' => FILE_ASN1_TYPE_INTEGER,
475 'optional' => true
480 $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING);
482 $OrganizationalUnitNames = array(
483 'type' => FILE_ASN1_TYPE_SEQUENCE,
484 'min' => 1,
485 'max' => 4, // ub-organizational-units
486 'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
489 $PersonalName = array(
490 'type' => FILE_ASN1_TYPE_SET,
491 'children' => array(
492 'surname' => array(
493 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
494 'constant' => 0,
495 'optional' => true,
496 'implicit' => true
498 'given-name' => array(
499 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
500 'constant' => 1,
501 'optional' => true,
502 'implicit' => true
504 'initials' => array(
505 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
506 'constant' => 2,
507 'optional' => true,
508 'implicit' => true
510 'generation-qualifier' => array(
511 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
512 'constant' => 3,
513 'optional' => true,
514 'implicit' => true
519 $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
521 $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
523 $PrivateDomainName = array(
524 'type' => FILE_ASN1_TYPE_CHOICE,
525 'children' => array(
526 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
527 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
531 $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
533 $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
535 $AdministrationDomainName = array(
536 'type' => FILE_ASN1_TYPE_CHOICE,
537 // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
538 // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
539 'class' => FILE_ASN1_CLASS_APPLICATION,
540 'cast' => 2,
541 'children' => array(
542 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
543 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
547 $CountryName = array(
548 'type' => FILE_ASN1_TYPE_CHOICE,
549 // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
550 // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
551 'class' => FILE_ASN1_CLASS_APPLICATION,
552 'cast' => 1,
553 'children' => array(
554 'x121-dcc-code' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
555 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
559 $AnotherName = array(
560 'type' => FILE_ASN1_TYPE_SEQUENCE,
561 'children' => array(
562 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
563 'value' => array(
564 'type' => FILE_ASN1_TYPE_ANY,
565 'constant' => 0,
566 'optional' => true,
567 'explicit' => true
572 $ExtensionAttribute = array(
573 'type' => FILE_ASN1_TYPE_SEQUENCE,
574 'children' => array(
575 'extension-attribute-type' => array(
576 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
577 'constant' => 0,
578 'optional' => true,
579 'implicit' => true
581 'extension-attribute-value' => array(
582 'type' => FILE_ASN1_TYPE_ANY,
583 'constant' => 1,
584 'optional' => true,
585 'explicit' => true
590 $ExtensionAttributes = array(
591 'type' => FILE_ASN1_TYPE_SET,
592 'min' => 1,
593 'max' => 256, // ub-extension-attributes
594 'children' => $ExtensionAttribute
597 $BuiltInDomainDefinedAttribute = array(
598 'type' => FILE_ASN1_TYPE_SEQUENCE,
599 'children' => array(
600 'type' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
601 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
605 $BuiltInDomainDefinedAttributes = array(
606 'type' => FILE_ASN1_TYPE_SEQUENCE,
607 'min' => 1,
608 'max' => 4, // ub-domain-defined-attributes
609 'children' => $BuiltInDomainDefinedAttribute
612 $BuiltInStandardAttributes = array(
613 'type' => FILE_ASN1_TYPE_SEQUENCE,
614 'children' => array(
615 'country-name' => array('optional' => true) + $CountryName,
616 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
617 'network-address' => array(
618 'constant' => 0,
619 'optional' => true,
620 'implicit' => true
621 ) + $NetworkAddress,
622 'terminal-identifier' => array(
623 'constant' => 1,
624 'optional' => true,
625 'implicit' => true
626 ) + $TerminalIdentifier,
627 'private-domain-name' => array(
628 'constant' => 2,
629 'optional' => true,
630 'explicit' => true
631 ) + $PrivateDomainName,
632 'organization-name' => array(
633 'constant' => 3,
634 'optional' => true,
635 'implicit' => true
636 ) + $OrganizationName,
637 'numeric-user-identifier' => array(
638 'constant' => 4,
639 'optional' => true,
640 'implicit' => true
641 ) + $NumericUserIdentifier,
642 'personal-name' => array(
643 'constant' => 5,
644 'optional' => true,
645 'implicit' => true
646 ) + $PersonalName,
647 'organizational-unit-names' => array(
648 'constant' => 6,
649 'optional' => true,
650 'implicit' => true
651 ) + $OrganizationalUnitNames
655 $ORAddress = array(
656 'type' => FILE_ASN1_TYPE_SEQUENCE,
657 'children' => array(
658 'built-in-standard-attributes' => $BuiltInStandardAttributes,
659 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
660 'extension-attributes' => array('optional' => true) + $ExtensionAttributes
664 $EDIPartyName = array(
665 'type' => FILE_ASN1_TYPE_SEQUENCE,
666 'children' => array(
667 'nameAssigner' => array(
668 'constant' => 0,
669 'optional' => true,
670 'implicit' => true
671 ) + $DirectoryString,
672 // partyName is technically required but File_ASN1 doesn't currently support non-optional constants and
673 // setting it to optional gets the job done in any event.
674 'partyName' => array(
675 'constant' => 1,
676 'optional' => true,
677 'implicit' => true
678 ) + $DirectoryString
682 $GeneralName = array(
683 'type' => FILE_ASN1_TYPE_CHOICE,
684 'children' => array(
685 'otherName' => array(
686 'constant' => 0,
687 'optional' => true,
688 'implicit' => true
689 ) + $AnotherName,
690 'rfc822Name' => array(
691 'type' => FILE_ASN1_TYPE_IA5_STRING,
692 'constant' => 1,
693 'optional' => true,
694 'implicit' => true
696 'dNSName' => array(
697 'type' => FILE_ASN1_TYPE_IA5_STRING,
698 'constant' => 2,
699 'optional' => true,
700 'implicit' => true
702 'x400Address' => array(
703 'constant' => 3,
704 'optional' => true,
705 'implicit' => true
706 ) + $ORAddress,
707 'directoryName' => array(
708 'constant' => 4,
709 'optional' => true,
710 'explicit' => true
711 ) + $this->Name,
712 'ediPartyName' => array(
713 'constant' => 5,
714 'optional' => true,
715 'implicit' => true
716 ) + $EDIPartyName,
717 'uniformResourceIdentifier' => array(
718 'type' => FILE_ASN1_TYPE_IA5_STRING,
719 'constant' => 6,
720 'optional' => true,
721 'implicit' => true
723 'iPAddress' => array(
724 'type' => FILE_ASN1_TYPE_OCTET_STRING,
725 'constant' => 7,
726 'optional' => true,
727 'implicit' => true
729 'registeredID' => array(
730 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER,
731 'constant' => 8,
732 'optional' => true,
733 'implicit' => true
738 $GeneralNames = array(
739 'type' => FILE_ASN1_TYPE_SEQUENCE,
740 'min' => 1,
741 'max' => -1,
742 'children' => $GeneralName
745 $this->IssuerAltName = $GeneralNames;
747 $ReasonFlags = array(
748 'type' => FILE_ASN1_TYPE_BIT_STRING,
749 'mapping' => array(
750 'unused',
751 'keyCompromise',
752 'cACompromise',
753 'affiliationChanged',
754 'superseded',
755 'cessationOfOperation',
756 'certificateHold',
757 'privilegeWithdrawn',
758 'aACompromise'
762 $DistributionPointName = array(
763 'type' => FILE_ASN1_TYPE_CHOICE,
764 'children' => array(
765 'fullName' => array(
766 'constant' => 0,
767 'optional' => true,
768 'implicit' => true
769 ) + $GeneralNames,
770 'nameRelativeToCRLIssuer' => array(
771 'constant' => 1,
772 'optional' => true,
773 'implicit' => true
774 ) + $this->RelativeDistinguishedName
778 $DistributionPoint = array(
779 'type' => FILE_ASN1_TYPE_SEQUENCE,
780 'children' => array(
781 'distributionPoint' => array(
782 'constant' => 0,
783 'optional' => true,
784 'explicit' => true
785 ) + $DistributionPointName,
786 'reasons' => array(
787 'constant' => 1,
788 'optional' => true,
789 'implicit' => true
790 ) + $ReasonFlags,
791 'cRLIssuer' => array(
792 'constant' => 2,
793 'optional' => true,
794 'implicit' => true
795 ) + $GeneralNames
799 $this->CRLDistributionPoints = array(
800 'type' => FILE_ASN1_TYPE_SEQUENCE,
801 'min' => 1,
802 'max' => -1,
803 'children' => $DistributionPoint
806 $this->AuthorityKeyIdentifier = array(
807 'type' => FILE_ASN1_TYPE_SEQUENCE,
808 'children' => array(
809 'keyIdentifier' => array(
810 'constant' => 0,
811 'optional' => true,
812 'implicit' => true
813 ) + $this->KeyIdentifier,
814 'authorityCertIssuer' => array(
815 'constant' => 1,
816 'optional' => true,
817 'implicit' => true
818 ) + $GeneralNames,
819 'authorityCertSerialNumber' => array(
820 'constant' => 2,
821 'optional' => true,
822 'implicit' => true
823 ) + $CertificateSerialNumber
827 $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
829 $PolicyQualifierInfo = array(
830 'type' => FILE_ASN1_TYPE_SEQUENCE,
831 'children' => array(
832 'policyQualifierId' => $PolicyQualifierId,
833 'qualifier' => array('type' => FILE_ASN1_TYPE_ANY)
837 $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
839 $PolicyInformation = array(
840 'type' => FILE_ASN1_TYPE_SEQUENCE,
841 'children' => array(
842 'policyIdentifier' => $CertPolicyId,
843 'policyQualifiers' => array(
844 'type' => FILE_ASN1_TYPE_SEQUENCE,
845 'min' => 0,
846 'max' => -1,
847 'optional' => true,
848 'children' => $PolicyQualifierInfo
853 $this->CertificatePolicies = array(
854 'type' => FILE_ASN1_TYPE_SEQUENCE,
855 'min' => 1,
856 'max' => -1,
857 'children' => $PolicyInformation
860 $this->PolicyMappings = array(
861 'type' => FILE_ASN1_TYPE_SEQUENCE,
862 'min' => 1,
863 'max' => -1,
864 'children' => array(
865 'type' => FILE_ASN1_TYPE_SEQUENCE,
866 'children' => array(
867 'issuerDomainPolicy' => $CertPolicyId,
868 'subjectDomainPolicy' => $CertPolicyId
873 $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
875 $this->ExtKeyUsageSyntax = array(
876 'type' => FILE_ASN1_TYPE_SEQUENCE,
877 'min' => 1,
878 'max' => -1,
879 'children' => $KeyPurposeId
882 $AccessDescription = array(
883 'type' => FILE_ASN1_TYPE_SEQUENCE,
884 'children' => array(
885 'accessMethod' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
886 'accessLocation' => $GeneralName
890 $this->AuthorityInfoAccessSyntax = array(
891 'type' => FILE_ASN1_TYPE_SEQUENCE,
892 'min' => 1,
893 'max' => -1,
894 'children' => $AccessDescription
897 $this->SubjectAltName = $GeneralNames;
899 $this->PrivateKeyUsagePeriod = array(
900 'type' => FILE_ASN1_TYPE_SEQUENCE,
901 'children' => array(
902 'notBefore' => array(
903 'constant' => 0,
904 'optional' => true,
905 'implicit' => true,
906 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME),
907 'notAfter' => array(
908 'constant' => 1,
909 'optional' => true,
910 'implicit' => true,
911 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
915 $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER);
917 $GeneralSubtree = array(
918 'type' => FILE_ASN1_TYPE_SEQUENCE,
919 'children' => array(
920 'base' => $GeneralName,
921 'minimum' => array(
922 'constant' => 0,
923 'optional' => true,
924 'implicit' => true,
925 'default' => new Math_BigInteger(0)
926 ) + $BaseDistance,
927 'maximum' => array(
928 'constant' => 1,
929 'optional' => true,
930 'implicit' => true,
931 ) + $BaseDistance
935 $GeneralSubtrees = array(
936 'type' => FILE_ASN1_TYPE_SEQUENCE,
937 'min' => 1,
938 'max' => -1,
939 'children' => $GeneralSubtree
942 $this->NameConstraints = array(
943 'type' => FILE_ASN1_TYPE_SEQUENCE,
944 'children' => array(
945 'permittedSubtrees' => array(
946 'constant' => 0,
947 'optional' => true,
948 'implicit' => true
949 ) + $GeneralSubtrees,
950 'excludedSubtrees' => array(
951 'constant' => 1,
952 'optional' => true,
953 'implicit' => true
954 ) + $GeneralSubtrees
958 $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING);
960 $DisplayText = array(
961 'type' => FILE_ASN1_TYPE_CHOICE,
962 'children' => array(
963 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING),
964 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING),
965 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING),
966 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING)
970 $NoticeReference = array(
971 'type' => FILE_ASN1_TYPE_SEQUENCE,
972 'children' => array(
973 'organization' => $DisplayText,
974 'noticeNumbers' => array(
975 'type' => FILE_ASN1_TYPE_SEQUENCE,
976 'min' => 1,
977 'max' => 200,
978 'children' => array('type' => FILE_ASN1_TYPE_INTEGER)
983 $this->UserNotice = array(
984 'type' => FILE_ASN1_TYPE_SEQUENCE,
985 'children' => array(
986 'noticeRef' => array(
987 'optional' => true,
988 'implicit' => true
989 ) + $NoticeReference,
990 'explicitText' => array(
991 'optional' => true,
992 'implicit' => true
993 ) + $DisplayText
997 // mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
998 $this->netscape_cert_type = array(
999 'type' => FILE_ASN1_TYPE_BIT_STRING,
1000 'mapping' => array(
1001 'SSLClient',
1002 'SSLServer',
1003 'Email',
1004 'ObjectSigning',
1005 'Reserved',
1006 'SSLCA',
1007 'EmailCA',
1008 'ObjectSigningCA'
1012 $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING);
1013 $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING);
1015 // attribute is used in RFC2986 but we're using the RFC5280 definition
1017 $Attribute = array(
1018 'type' => FILE_ASN1_TYPE_SEQUENCE,
1019 'children' => array(
1020 'type' => $AttributeType,
1021 'value'=> array(
1022 'type' => FILE_ASN1_TYPE_SET,
1023 'min' => 1,
1024 'max' => -1,
1025 'children' => $AttributeValue
1030 // adapted from <http://tools.ietf.org/html/rfc2986>
1032 $Attributes = array(
1033 'type' => FILE_ASN1_TYPE_SET,
1034 'min' => 1,
1035 'max' => -1,
1036 'children' => $Attribute
1039 $CertificationRequestInfo = array(
1040 'type' => FILE_ASN1_TYPE_SEQUENCE,
1041 'children' => array(
1042 'version' => array(
1043 'type' => FILE_ASN1_TYPE_INTEGER,
1044 'mapping' => array('v1')
1046 'subject' => $this->Name,
1047 'subjectPKInfo' => $SubjectPublicKeyInfo,
1048 'attributes' => array(
1049 'constant' => 0,
1050 'optional' => true,
1051 'implicit' => true
1052 ) + $Attributes,
1056 $this->CertificationRequest = array(
1057 'type' => FILE_ASN1_TYPE_SEQUENCE,
1058 'children' => array(
1059 'certificationRequestInfo' => $CertificationRequestInfo,
1060 'signatureAlgorithm' => $AlgorithmIdentifier,
1061 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
1065 $RevokedCertificate = array(
1066 'type' => FILE_ASN1_TYPE_SEQUENCE,
1067 'children' => array(
1068 'userCertificate' => $CertificateSerialNumber,
1069 'revocationDate' => $Time,
1070 'crlEntryExtensions' => array(
1071 'optional' => true
1072 ) + $Extensions
1076 $TBSCertList = array(
1077 'type' => FILE_ASN1_TYPE_SEQUENCE,
1078 'children' => array(
1079 'version' => array(
1080 'optional' => true,
1081 'default' => 'v1'
1082 ) + $Version,
1083 'signature' => $AlgorithmIdentifier,
1084 'issuer' => $this->Name,
1085 'thisUpdate' => $Time,
1086 'nextUpdate' => array(
1087 'optional' => true
1088 ) + $Time,
1089 'revokedCertificates' => array(
1090 'type' => FILE_ASN1_TYPE_SEQUENCE,
1091 'optional' => true,
1092 'min' => 0,
1093 'max' => -1,
1094 'children' => $RevokedCertificate
1096 'crlExtensions' => array(
1097 'constant' => 0,
1098 'optional' => true,
1099 'explicit' => true
1100 ) + $Extensions
1104 $this->CertificateList = array(
1105 'type' => FILE_ASN1_TYPE_SEQUENCE,
1106 'children' => array(
1107 'tbsCertList' => $TBSCertList,
1108 'signatureAlgorithm' => $AlgorithmIdentifier,
1109 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
1113 $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
1115 $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED,
1116 'mapping' => array(
1117 'unspecified',
1118 'keyCompromise',
1119 'cACompromise',
1120 'affiliationChanged',
1121 'superseded',
1122 'cessationOfOperation',
1123 'certificateHold',
1124 // Value 7 is not used.
1125 8 => 'removeFromCRL',
1126 'privilegeWithdrawn',
1127 'aACompromise'
1131 $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE,
1132 'children' => array(
1133 'distributionPoint' => array(
1134 'constant' => 0,
1135 'optional' => true,
1136 'explicit' => true
1137 ) + $DistributionPointName,
1138 'onlyContainsUserCerts' => array(
1139 'type' => FILE_ASN1_TYPE_BOOLEAN,
1140 'constant' => 1,
1141 'optional' => true,
1142 'default' => false,
1143 'implicit' => true
1145 'onlyContainsCACerts' => array(
1146 'type' => FILE_ASN1_TYPE_BOOLEAN,
1147 'constant' => 2,
1148 'optional' => true,
1149 'default' => false,
1150 'implicit' => true
1152 'onlySomeReasons' => array(
1153 'constant' => 3,
1154 'optional' => true,
1155 'implicit' => true
1156 ) + $ReasonFlags,
1157 'indirectCRL' => array(
1158 'type' => FILE_ASN1_TYPE_BOOLEAN,
1159 'constant' => 4,
1160 'optional' => true,
1161 'default' => false,
1162 'implicit' => true
1164 'onlyContainsAttributeCerts' => array(
1165 'type' => FILE_ASN1_TYPE_BOOLEAN,
1166 'constant' => 5,
1167 'optional' => true,
1168 'default' => false,
1169 'implicit' => true
1174 $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME);
1176 $this->CertificateIssuer = $GeneralNames;
1178 // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
1179 $this->oids = array(
1180 '1.3.6.1.5.5.7' => 'id-pkix',
1181 '1.3.6.1.5.5.7.1' => 'id-pe',
1182 '1.3.6.1.5.5.7.2' => 'id-qt',
1183 '1.3.6.1.5.5.7.3' => 'id-kp',
1184 '1.3.6.1.5.5.7.48' => 'id-ad',
1185 '1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
1186 '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
1187 '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
1188 '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
1189 '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
1190 '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
1191 '2.5.4' => 'id-at',
1192 '2.5.4.41' => 'id-at-name',
1193 '2.5.4.4' => 'id-at-surname',
1194 '2.5.4.42' => 'id-at-givenName',
1195 '2.5.4.43' => 'id-at-initials',
1196 '2.5.4.44' => 'id-at-generationQualifier',
1197 '2.5.4.3' => 'id-at-commonName',
1198 '2.5.4.7' => 'id-at-localityName',
1199 '2.5.4.8' => 'id-at-stateOrProvinceName',
1200 '2.5.4.10' => 'id-at-organizationName',
1201 '2.5.4.11' => 'id-at-organizationalUnitName',
1202 '2.5.4.12' => 'id-at-title',
1203 '2.5.4.13' => 'id-at-description',
1204 '2.5.4.46' => 'id-at-dnQualifier',
1205 '2.5.4.6' => 'id-at-countryName',
1206 '2.5.4.5' => 'id-at-serialNumber',
1207 '2.5.4.65' => 'id-at-pseudonym',
1208 '2.5.4.17' => 'id-at-postalCode',
1209 '2.5.4.9' => 'id-at-streetAddress',
1210 '2.5.4.45' => 'id-at-uniqueIdentifier',
1211 '2.5.4.72' => 'id-at-role',
1213 '0.9.2342.19200300.100.1.25' => 'id-domainComponent',
1214 '1.2.840.113549.1.9' => 'pkcs-9',
1215 '1.2.840.113549.1.9.1' => 'id-emailAddress',
1216 '2.5.29' => 'id-ce',
1217 '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
1218 '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
1219 '2.5.29.15' => 'id-ce-keyUsage',
1220 '2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
1221 '2.5.29.32' => 'id-ce-certificatePolicies',
1222 '2.5.29.32.0' => 'anyPolicy',
1224 '2.5.29.33' => 'id-ce-policyMappings',
1225 '2.5.29.17' => 'id-ce-subjectAltName',
1226 '2.5.29.18' => 'id-ce-issuerAltName',
1227 '2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
1228 '2.5.29.19' => 'id-ce-basicConstraints',
1229 '2.5.29.30' => 'id-ce-nameConstraints',
1230 '2.5.29.36' => 'id-ce-policyConstraints',
1231 '2.5.29.31' => 'id-ce-cRLDistributionPoints',
1232 '2.5.29.37' => 'id-ce-extKeyUsage',
1233 '2.5.29.37.0' => 'anyExtendedKeyUsage',
1234 '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
1235 '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
1236 '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
1237 '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
1238 '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
1239 '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
1240 '2.5.29.54' => 'id-ce-inhibitAnyPolicy',
1241 '2.5.29.46' => 'id-ce-freshestCRL',
1242 '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
1243 '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
1244 '2.5.29.20' => 'id-ce-cRLNumber',
1245 '2.5.29.28' => 'id-ce-issuingDistributionPoint',
1246 '2.5.29.27' => 'id-ce-deltaCRLIndicator',
1247 '2.5.29.21' => 'id-ce-cRLReasons',
1248 '2.5.29.29' => 'id-ce-certificateIssuer',
1249 '2.5.29.23' => 'id-ce-holdInstructionCode',
1250 '2.2.840.10040.2' => 'holdInstruction',
1251 '2.2.840.10040.2.1' => 'id-holdinstruction-none',
1252 '2.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
1253 '2.2.840.10040.2.3' => 'id-holdinstruction-reject',
1254 '2.5.29.24' => 'id-ce-invalidityDate',
1256 '1.2.840.113549.2.2' => 'md2',
1257 '1.2.840.113549.2.5' => 'md5',
1258 '1.3.14.3.2.26' => 'id-sha1',
1259 '1.2.840.10040.4.1' => 'id-dsa',
1260 '1.2.840.10040.4.3' => 'id-dsa-with-sha1',
1261 '1.2.840.113549.1.1' => 'pkcs-1',
1262 '1.2.840.113549.1.1.1' => 'rsaEncryption',
1263 '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
1264 '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
1265 '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
1266 '1.2.840.10046.2.1' => 'dhpublicnumber',
1267 '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
1268 '1.2.840.10045' => 'ansi-X9-62',
1269 '1.2.840.10045.4' => 'id-ecSigType',
1270 '1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
1271 '1.2.840.10045.1' => 'id-fieldType',
1272 '1.2.840.10045.1.1' => 'prime-field',
1273 '1.2.840.10045.1.2' => 'characteristic-two-field',
1274 '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
1275 '1.2.840.10045.1.2.3.1' => 'gnBasis',
1276 '1.2.840.10045.1.2.3.2' => 'tpBasis',
1277 '1.2.840.10045.1.2.3.3' => 'ppBasis',
1278 '1.2.840.10045.2' => 'id-publicKeyType',
1279 '1.2.840.10045.2.1' => 'id-ecPublicKey',
1280 '1.2.840.10045.3' => 'ellipticCurve',
1281 '1.2.840.10045.3.0' => 'c-TwoCurve',
1282 '1.2.840.10045.3.0.1' => 'c2pnb163v1',
1283 '1.2.840.10045.3.0.2' => 'c2pnb163v2',
1284 '1.2.840.10045.3.0.3' => 'c2pnb163v3',
1285 '1.2.840.10045.3.0.4' => 'c2pnb176w1',
1286 '1.2.840.10045.3.0.5' => 'c2pnb191v1',
1287 '1.2.840.10045.3.0.6' => 'c2pnb191v2',
1288 '1.2.840.10045.3.0.7' => 'c2pnb191v3',
1289 '1.2.840.10045.3.0.8' => 'c2pnb191v4',
1290 '1.2.840.10045.3.0.9' => 'c2pnb191v5',
1291 '1.2.840.10045.3.0.10' => 'c2pnb208w1',
1292 '1.2.840.10045.3.0.11' => 'c2pnb239v1',
1293 '1.2.840.10045.3.0.12' => 'c2pnb239v2',
1294 '1.2.840.10045.3.0.13' => 'c2pnb239v3',
1295 '1.2.840.10045.3.0.14' => 'c2pnb239v4',
1296 '1.2.840.10045.3.0.15' => 'c2pnb239v5',
1297 '1.2.840.10045.3.0.16' => 'c2pnb272w1',
1298 '1.2.840.10045.3.0.17' => 'c2pnb304w1',
1299 '1.2.840.10045.3.0.18' => 'c2pnb359v1',
1300 '1.2.840.10045.3.0.19' => 'c2pnb368w1',
1301 '1.2.840.10045.3.0.20' => 'c2pnb431r1',
1302 '1.2.840.10045.3.1' => 'primeCurve',
1303 '1.2.840.10045.3.1.1' => 'prime192v1',
1304 '1.2.840.10045.3.1.2' => 'prime192v2',
1305 '1.2.840.10045.3.1.3' => 'prime192v3',
1306 '1.2.840.10045.3.1.4' => 'prime239v1',
1307 '1.2.840.10045.3.1.5' => 'prime239v2',
1308 '1.2.840.10045.3.1.6' => 'prime239v3',
1309 '1.2.840.10045.3.1.7' => 'prime256v1',
1310 '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
1311 '1.2.840.113549.1.1.9' => 'id-pSpecified',
1312 '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
1313 '1.2.840.113549.1.1.8' => 'id-mgf1',
1314 '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
1315 '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
1316 '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
1317 '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
1318 '2.16.840.1.101.3.4.2.4' => 'id-sha224',
1319 '2.16.840.1.101.3.4.2.1' => 'id-sha256',
1320 '2.16.840.1.101.3.4.2.2' => 'id-sha384',
1321 '2.16.840.1.101.3.4.2.3' => 'id-sha512',
1322 '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
1323 '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
1324 '1.2.643.2.2.20' => 'id-GostR3410-2001',
1325 '1.2.643.2.2.19' => 'id-GostR3410-94',
1326 // Netscape Object Identifiers from "Netscape Certificate Extensions"
1327 '2.16.840.1.113730' => 'netscape',
1328 '2.16.840.1.113730.1' => 'netscape-cert-extension',
1329 '2.16.840.1.113730.1.1' => 'netscape-cert-type',
1330 '2.16.840.1.113730.1.13' => 'netscape-comment',
1331 '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
1332 // the following are X.509 extensions not supported by phpseclib
1333 '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
1334 '1.2.840.113533.7.65.0' => 'entrustVersInfo',
1335 '2.16.840.1.113733.1.6.9' => 'verisignPrivate',
1336 // for Certificate Signing Requests
1337 // see http://tools.ietf.org/html/rfc2985
1338 '1.2.840.113549.1.9.2' => 'unstructuredName', // PKCS #9 unstructured name
1339 '1.2.840.113549.1.9.7' => 'challengePassword' // Challenge password for certificate revocations
1344 * Load X.509 certificate
1346 * Returns an associative array describing the X.509 cert or a false if the cert failed to load
1348 * @param String $cert
1349 * @access public
1350 * @return Mixed
1352 function loadX509($cert)
1354 if (is_array($cert) && isset($cert['tbsCertificate'])) {
1355 $this->currentCert = $cert;
1356 unset($this->signatureSubject);
1357 return false;
1360 $asn1 = new File_ASN1();
1363 X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie.
1364 some may have the following preceeding the -----BEGIN CERTIFICATE----- line:
1366 subject=/O=organization/OU=org unit/CN=common name
1367 issuer=/O=organization/CN=common name
1369 $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $cert);
1370 $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
1371 if ($temp != false) {
1372 $cert = $temp;
1375 if ($cert === false) {
1376 $this->currentCert = false;
1377 return false;
1380 $asn1->loadOIDs($this->oids);
1381 $decoded = $asn1->decodeBER($cert);
1383 if (!empty($decoded)) {
1384 $x509 = $asn1->asn1map($decoded[0], $this->Certificate);
1386 if (!isset($x509) || $x509 === false) {
1387 $this->currentCert = false;
1388 return false;
1391 $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
1393 $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);
1395 $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
1396 $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
1398 $this->currentCert = $x509;
1399 $this->dn = $x509['tbsCertificate']['subject'];
1401 $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
1402 $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : NULL;
1404 return $x509;
1408 * Save X.509 certificate
1410 * @param Array $cert
1411 * @access public
1412 * @return String
1414 function saveX509($cert)
1416 if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
1417 return false;
1420 if (is_array($cert['tbsCertificate']['subjectPublicKeyInfo'])) {
1421 switch ($cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm']) {
1422 case 'rsaEncryption':
1423 $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] =
1424 base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
1428 $asn1 = new File_ASN1();
1430 $asn1->loadOIDs($this->oids);
1432 $filters = array();
1433 $filters['tbsCertificate']['signature']['parameters'] =
1434 $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] =
1435 $filters['tbsCertificate']['issuer']['rdnSequence']['value'] =
1436 $filters['tbsCertificate']['subject']['rdnSequence']['value'] =
1437 $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] =
1438 $filters['signatureAlgorithm']['parameters'] =
1439 $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] =
1440 //$filters['policyQualifiers']['qualifier'] =
1441 $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] =
1442 $filters['directoryName']['rdnSequence']['value'] =
1443 array('type' => FILE_ASN1_TYPE_UTF8_STRING);
1444 /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING.
1445 FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
1446 characters.
1448 $filters['policyQualifiers']['qualifier'] =
1449 array('type' => FILE_ASN1_TYPE_IA5_STRING);
1451 $asn1->loadFilters($filters);
1453 $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1);
1455 $cert = $asn1->encodeDER($cert, $this->Certificate);
1457 return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert)) . '-----END CERTIFICATE-----';
1461 * Map extension values from octet string to extension-specific internal
1462 * format.
1464 * @param Array ref $root
1465 * @param String $path
1466 * @param Object $asn1
1467 * @access private
1469 function _mapInExtensions(&$root, $path, $asn1)
1471 $extensions = &$this->_subArray($root, $path);
1473 if (is_array($extensions)) {
1474 for ($i = 0; $i < count($extensions); $i++) {
1475 $id = $extensions[$i]['extnId'];
1476 $value = &$extensions[$i]['extnValue'];
1477 $value = base64_decode($value);
1478 $decoded = $asn1->decodeBER($value);
1479 /* [extnValue] contains the DER encoding of an ASN.1 value
1480 corresponding to the extension type identified by extnID */
1481 $map = $this->_getMapping($id);
1482 if (!is_bool($map)) {
1483 $mapped = $asn1->asn1map($decoded[0], $map);
1484 $value = $mapped === false ? $decoded[0] : $mapped;
1486 if ($id == 'id-ce-certificatePolicies') {
1487 for ($j = 0; $j < count($value); $j++) {
1488 if (!isset($value[$j]['policyQualifiers'])) {
1489 continue;
1491 for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
1492 $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
1493 $map = $this->_getMapping($subid);
1494 $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
1495 if ($map !== false) {
1496 $decoded = $asn1->decodeBER($subvalue);
1497 $mapped = $asn1->asn1map($decoded[0], $map);
1498 $subvalue = $mapped === false ? $decoded[0] : $mapped;
1503 } elseif ($map) {
1504 $value = base64_encode($value);
1511 * Map extension values from extension-specific internal format to
1512 * octet string.
1514 * @param Array ref $root
1515 * @param String $path
1516 * @param Object $asn1
1517 * @access private
1519 function _mapOutExtensions(&$root, $path, $asn1)
1521 $extensions = &$this->_subArray($root, $path);
1523 if (is_array($extensions)) {
1524 $size = count($extensions);
1525 for ($i = 0; $i < $size; $i++) {
1526 $id = $extensions[$i]['extnId'];
1527 $value = &$extensions[$i]['extnValue'];
1529 switch ($id) {
1530 case 'id-ce-certificatePolicies':
1531 for ($j = 0; $j < count($value); $j++) {
1532 if (!isset($value[$j]['policyQualifiers'])) {
1533 continue;
1535 for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
1536 $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
1537 $map = $this->_getMapping($subid);
1538 $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
1539 if ($map !== false) {
1540 // by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's
1541 // actual type is FILE_ASN1_TYPE_ANY
1542 $subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue, $map));
1546 break;
1547 case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string
1548 if (isset($value['authorityCertSerialNumber'])) {
1549 if ($value['authorityCertSerialNumber']->toBytes() == '') {
1550 $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0";
1551 $value['authorityCertSerialNumber'] = new File_ASN1_Element($temp);
1556 /* [extnValue] contains the DER encoding of an ASN.1 value
1557 corresponding to the extension type identified by extnID */
1558 $map = $this->_getMapping($id);
1559 if (is_bool($map)) {
1560 if (!$map) {
1561 user_error($id . ' is not a currently supported extension', E_USER_NOTICE);
1562 unset($extensions[$i]);
1564 } else {
1565 $temp = $asn1->encodeDER($value, $map);
1566 $value = base64_encode($temp);
1573 * Associate an extension ID to an extension mapping
1575 * @param String $extnId
1576 * @access private
1577 * @return Mixed
1579 function _getMapping($extnId)
1581 switch ($extnId) {
1582 case 'id-ce-keyUsage':
1583 return $this->KeyUsage;
1584 case 'id-ce-basicConstraints':
1585 return $this->BasicConstraints;
1586 case 'id-ce-subjectKeyIdentifier':
1587 return $this->KeyIdentifier;
1588 case 'id-ce-cRLDistributionPoints':
1589 return $this->CRLDistributionPoints;
1590 case 'id-ce-authorityKeyIdentifier':
1591 return $this->AuthorityKeyIdentifier;
1592 case 'id-ce-certificatePolicies':
1593 return $this->CertificatePolicies;
1594 case 'id-ce-extKeyUsage':
1595 return $this->ExtKeyUsageSyntax;
1596 case 'id-pe-authorityInfoAccess':
1597 return $this->AuthorityInfoAccessSyntax;
1598 case 'id-ce-subjectAltName':
1599 return $this->SubjectAltName;
1600 case 'id-ce-privateKeyUsagePeriod':
1601 return $this->PrivateKeyUsagePeriod;
1602 case 'id-ce-issuerAltName':
1603 return $this->IssuerAltName;
1604 case 'id-ce-policyMappings':
1605 return $this->PolicyMappings;
1606 case 'id-ce-nameConstraints':
1607 return $this->NameConstraints;
1609 case 'netscape-cert-type':
1610 return $this->netscape_cert_type;
1611 case 'netscape-comment':
1612 return $this->netscape_comment;
1613 case 'netscape-ca-policy-url':
1614 return $this->netscape_ca_policy_url;
1616 // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
1617 // back around to asn1map() and we don't want it decoded again.
1618 //case 'id-qt-cps':
1619 // return $this->CPSuri;
1620 case 'id-qt-unotice':
1621 return $this->UserNotice;
1623 // the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
1624 case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt
1625 case 'entrustVersInfo':
1626 // http://support.microsoft.com/kb/287547
1627 case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION
1628 case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION
1629 // "SET Secure Electronic Transaction Specification"
1630 // http://www.maithean.com/docs/set_bk3.pdf
1631 case '2.23.42.7.0': // id-set-hashedRootKey
1632 return true;
1634 // CRL extensions.
1635 case 'id-ce-cRLNumber':
1636 return $this->CRLNumber;
1637 case 'id-ce-deltaCRLIndicator':
1638 return $this->CRLNumber;
1639 case 'id-ce-issuingDistributionPoint':
1640 return $this->IssuingDistributionPoint;
1641 case 'id-ce-freshestCRL':
1642 return $this->CRLDistributionPoints;
1643 case 'id-ce-cRLReasons':
1644 return $this->CRLReason;
1645 case 'id-ce-invalidityDate':
1646 return $this->InvalidityDate;
1647 case 'id-ce-certificateIssuer':
1648 return $this->CertificateIssuer;
1651 return false;
1655 * Load an X.509 certificate as a certificate authority
1657 * @param String $cert
1658 * @access public
1659 * @return Boolean
1661 function loadCA($cert)
1663 $olddn = $this->dn;
1664 $oldcert = $this->currentCert;
1665 $oldsigsubj = $this->signatureSubject;
1667 $cert = $this->loadX509($cert);
1668 if (!$cert) {
1669 $this->dn = $olddn;
1670 $this->currentCert = $oldcert;
1671 $this->signatureSubject = $oldsigsubj;
1673 return false;
1676 /* From RFC5280 "PKIX Certificate and CRL Profile":
1678 If the keyUsage extension is present, then the subject public key
1679 MUST NOT be used to verify signatures on certificates or CRLs unless
1680 the corresponding keyCertSign or cRLSign bit is set. */
1681 //$keyUsage = $this->getExtension('id-ce-keyUsage');
1682 //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
1683 // return false;
1686 /* From RFC5280 "PKIX Certificate and CRL Profile":
1688 The cA boolean indicates whether the certified public key may be used
1689 to verify certificate signatures. If the cA boolean is not asserted,
1690 then the keyCertSign bit in the key usage extension MUST NOT be
1691 asserted. If the basic constraints extension is not present in a
1692 version 3 certificate, or the extension is present but the cA boolean
1693 is not asserted, then the certified public key MUST NOT be used to
1694 verify certificate signatures. */
1695 //$basicConstraints = $this->getExtension('id-ce-basicConstraints');
1696 //if (!$basicConstraints || !$basicConstraints['cA']) {
1697 // return false;
1700 $this->CAs[] = $cert;
1702 $this->dn = $olddn;
1703 $this->currentCert = $oldcert;
1704 $this->signatureSubject = $oldsigsubj;
1706 return true;
1710 * Validate an X.509 certificate against a URL
1712 * From RFC2818 "HTTP over TLS":
1714 * Matching is performed using the matching rules specified by
1715 * [RFC2459]. If more than one identity of a given type is present in
1716 * the certificate (e.g., more than one dNSName name, a match in any one
1717 * of the set is considered acceptable.) Names may contain the wildcard
1718 * character * which is considered to match any single domain name
1719 * component or component fragment. E.g., *.a.com matches foo.a.com but
1720 * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
1722 * @param String $url
1723 * @access public
1724 * @return Boolean
1726 function validateURL($url)
1728 if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
1729 return false;
1732 $components = parse_url($url);
1733 if (!isset($components['host'])) {
1734 return false;
1737 if ($names = $this->getExtension('id-ce-subjectAltName')) {
1738 foreach ($names as $key => $value) {
1739 $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
1740 switch ($key) {
1741 case 'dNSName':
1742 /* From RFC2818 "HTTP over TLS":
1744 If a subjectAltName extension of type dNSName is present, that MUST
1745 be used as the identity. Otherwise, the (most specific) Common Name
1746 field in the Subject field of the certificate MUST be used. Although
1747 the use of the Common Name is existing practice, it is deprecated and
1748 Certification Authorities are encouraged to use the dNSName instead. */
1749 if (preg_match('#^' . $value . '$#', $components['host'])) {
1750 return true;
1752 break;
1753 case 'iPAddress':
1754 /* From RFC2818 "HTTP over TLS":
1756 In some cases, the URI is specified as an IP address rather than a
1757 hostname. In this case, the iPAddress subjectAltName must be present
1758 in the certificate and must exactly match the IP in the URI. */
1759 if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
1760 return true;
1764 return false;
1767 if ($value = $this->getDNProp('id-at-commonName')) {
1768 $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
1769 return preg_match('#^' . $value . '$#', $components['host']);
1772 return false;
1776 * Validate a date
1778 * If $date isn't defined it is assumed to be the current date.
1780 * @param Integer $date optional
1781 * @access public
1783 function validateDate($date = NULL)
1785 if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
1786 return false;
1789 if (!isset($date)) {
1790 $date = time();
1793 $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
1794 $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime'];
1796 $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
1797 $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
1799 switch (true) {
1800 case $date < @strtotime($notBefore):
1801 case $date > @strtotime($notAfter):
1802 return false;
1805 return true;
1809 * Validate a signature
1811 * Works on X.509 certs, CSR's and CRL's.
1812 * Returns true if the signature is verified, false if it is not correct or NULL on error
1814 * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
1816 * @param Integer $options optional
1817 * @access public
1818 * @return Mixed
1820 function validateSignature($options = 0)
1822 if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
1823 return 0;
1826 /* TODO:
1827 "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
1828 -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
1830 implement pathLenConstraint in the id-ce-basicConstraints extension */
1832 switch (true) {
1833 case isset($this->currentCert['tbsCertificate']):
1834 // self-signed cert
1835 if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) {
1836 $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
1837 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
1838 switch (true) {
1839 case !is_array($authorityKey):
1840 case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
1841 $signingCert = $this->currentCert; // working cert
1845 if (!empty($this->CAs)) {
1846 for ($i = 0; $i < count($this->CAs); $i++) {
1847 // even if the cert is a self-signed one we still want to see if it's a CA;
1848 // if not, we'll conditionally return an error
1849 $ca = $this->CAs[$i];
1850 if ($this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
1851 $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
1852 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
1853 switch (true) {
1854 case !is_array($authorityKey):
1855 case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
1856 $signingCert = $ca; // working cert
1857 break 2;
1861 if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) {
1862 return false;
1864 } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) {
1865 return false;
1867 return $this->_validateSignature(
1868 $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
1869 $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
1870 $this->currentCert['signatureAlgorithm']['algorithm'],
1871 substr(base64_decode($this->currentCert['signature']), 1),
1872 $this->signatureSubject
1874 case isset($this->currentCert['certificationRequestInfo']):
1875 return $this->_validateSignature(
1876 $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
1877 $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
1878 $this->currentCert['signatureAlgorithm']['algorithm'],
1879 substr(base64_decode($this->currentCert['signature']), 1),
1880 $this->signatureSubject
1882 case isset($this->currentCert['tbsCertList']):
1883 if (!empty($this->CAs)) {
1884 for ($i = 0; $i < count($this->CAs); $i++) {
1885 $ca = $this->CAs[$i];
1886 if ($this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) {
1887 $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
1888 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
1889 switch (true) {
1890 case !is_array($authorityKey):
1891 case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
1892 $signingCert = $ca; // working cert
1893 break 2;
1898 if (!isset($signingCert)) {
1899 return false;
1901 return $this->_validateSignature(
1902 $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
1903 $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
1904 $this->currentCert['signatureAlgorithm']['algorithm'],
1905 substr(base64_decode($this->currentCert['signature']), 1),
1906 $this->signatureSubject
1908 default:
1909 return false;
1914 * Validates a signature
1916 * Returns true if the signature is verified, false if it is not correct or NULL on error
1918 * @param String $publicKeyAlgorithm
1919 * @param String $publicKey
1920 * @param String $signatureAlgorithm
1921 * @param String $signature
1922 * @param String $signatureSubject
1923 * @access private
1924 * @return Integer
1926 function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
1928 switch ($publicKeyAlgorithm) {
1929 case 'rsaEncryption':
1930 if (!class_exists('Crypt_RSA')) {
1931 require_once('Crypt/RSA.php');
1933 $rsa = new Crypt_RSA();
1934 $rsa->loadKey($publicKey);
1936 switch ($signatureAlgorithm) {
1937 case 'md2WithRSAEncryption':
1938 case 'md5WithRSAEncryption':
1939 case 'sha1WithRSAEncryption':
1940 case 'sha224WithRSAEncryption':
1941 case 'sha256WithRSAEncryption':
1942 case 'sha384WithRSAEncryption':
1943 case 'sha512WithRSAEncryption':
1944 $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
1945 $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
1947 if (!@$rsa->verify($signatureSubject, $signature)) {
1948 return false;
1950 break;
1951 default:
1952 return NULL;
1954 break;
1955 default:
1956 return NULL;
1959 return true;
1963 * Reformat public keys
1965 * Reformats a public key to a format supported by phpseclib (if applicable)
1967 * @param String $algorithm
1968 * @param String $key
1969 * @access private
1970 * @return String
1972 function _reformatKey($algorithm, $key)
1974 switch ($algorithm) {
1975 case 'rsaEncryption':
1976 return
1977 "-----BEGIN PUBLIC KEY-----\r\n" .
1978 // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits
1979 // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox
1980 // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
1981 chunk_split(base64_encode(substr(base64_decode($key), 1))) .
1982 '-----END PUBLIC KEY-----';
1983 default:
1984 return $key;
1989 * "Normalizes" a Distinguished Name property
1991 * @param String $propName
1992 * @access private
1993 * @return Mixed
1995 function _translateDNProp($propName)
1997 switch (strtolower($propName)) {
1998 case 'id-at-countryname':
1999 case 'countryname':
2000 case 'c':
2001 return 'id-at-countryName';
2002 case 'id-at-organizationname':
2003 case 'organizationname':
2004 case 'o':
2005 return 'id-at-organizationName';
2006 case 'id-at-dnqualifier':
2007 case 'dnqualifier':
2008 return 'id-at-dnQualifier';
2009 case 'id-at-commonname':
2010 case 'commonname':
2011 case 'cn':
2012 return 'id-at-commonName';
2013 case 'id-at-stateorprovinceName':
2014 case 'stateorprovincename':
2015 case 'state':
2016 case 'province':
2017 case 'provincename':
2018 case 'st':
2019 return 'id-at-stateOrProvinceName';
2020 case 'id-at-localityname':
2021 case 'localityname':
2022 case 'l':
2023 return 'id-at-localityName';
2024 case 'id-emailaddress':
2025 case 'emailaddress':
2026 return 'id-emailAddress';
2027 case 'id-at-serialnumber':
2028 case 'serialnumber':
2029 return 'id-at-serialNumber';
2030 case 'id-at-postalcode':
2031 case 'postalcode':
2032 return 'id-at-postalCode';
2033 case 'id-at-streetaddress':
2034 case 'streetaddress':
2035 return 'id-at-streetAddress';
2036 case 'id-at-name':
2037 case 'name':
2038 return 'id-at-name';
2039 case 'id-at-givenname':
2040 case 'givenname':
2041 return 'id-at-givenName';
2042 case 'id-at-surname':
2043 case 'surname':
2044 case 'sn':
2045 return 'id-at-surname';
2046 case 'id-at-initials':
2047 case 'initials':
2048 return 'id-at-initials';
2049 case 'id-at-generationqualifier':
2050 case 'generationqualifier':
2051 return 'id-at-generationQualifier';
2052 case 'id-at-organizationalunitname':
2053 case 'organizationalunitname':
2054 case 'ou':
2055 return 'id-at-organizationalUnitName';
2056 case 'id-at-pseudonym':
2057 case 'pseudonym':
2058 return 'id-at-pseudonym';
2059 case 'id-at-title':
2060 case 'title':
2061 return 'id-at-title';
2062 case 'id-at-description':
2063 case 'description':
2064 return 'id-at-description';
2065 case 'id-at-role':
2066 case 'role':
2067 return 'id-at-role';
2068 case 'id-at-uniqueidentifier':
2069 case 'uniqueidentifier':
2070 case 'x500uniqueidentifier':
2071 return 'id-at-uniqueIdentifier';
2072 default:
2073 return false;
2078 * Set a Distinguished Name property
2080 * @param String $propName
2081 * @param Mixed $propValue
2082 * @param String $type optional
2083 * @access public
2084 * @return Boolean
2086 function setDNProp($propName, $propValue, $type = 'utf8String')
2088 if (empty($this->dn)) {
2089 $this->dn = array('rdnSequence' => array());
2092 if (($propName = $this->_translateDNProp($propName)) === false) {
2093 return false;
2096 foreach ((array) $propValue as $v) {
2097 if (!is_array($v) && isset($type)) {
2098 $v = array($type => $v);
2100 $this->dn['rdnSequence'][] = array(
2101 array(
2102 'type' => $propName,
2103 'value'=> $v
2108 return true;
2112 * Remove Distinguished Name properties
2114 * @param String $propName
2115 * @access public
2117 function removeDNProp($propName)
2119 if (empty($this->dn)) {
2120 return;
2123 if (($propName = $this->_translateDNProp($propName)) === false) {
2124 return;
2127 $dn = &$this->dn['rdnSequence'];
2128 $size = count($dn);
2129 for ($i = 0; $i < $size; $i++) {
2130 if ($dn[$i][0]['type'] == $propName) {
2131 unset($dn[$i]);
2135 $dn = array_values($dn);
2139 * Get Distinguished Name properties
2141 * @param String $propName
2142 * @param Array $dn optional
2143 * @param Boolean $withType optional
2144 * @return Mixed
2145 * @access public
2147 function getDNProp($propName, $dn = NULL, $withType = false)
2149 if (!isset($dn)) {
2150 $dn = $this->dn;
2153 if (empty($dn)) {
2154 return false;
2157 if (($propName = $this->_translateDNProp($propName)) === false) {
2158 return false;
2161 $dn = $dn['rdnSequence'];
2162 $result = array();
2163 $asn1 = new File_ASN1();
2164 for ($i = 0; $i < count($dn); $i++) {
2165 if ($dn[$i][0]['type'] == $propName) {
2166 $v = $dn[$i][0]['value'];
2167 if (!$withType && is_array($v)) {
2168 foreach ($v as $type => $s) {
2169 $type = array_search($type, $asn1->ANYmap, true);
2170 if ($type !== false && isset($asn1->stringTypeSize[$type])) {
2171 $s = $asn1->convert($s, $type);
2172 if ($s !== false) {
2173 $v = $s;
2174 break;
2178 if (is_array($v)) {
2179 $v = array_pop($v); // Always strip data type.
2182 $result[] = $v;
2186 return $result;
2190 * Set a Distinguished Name
2192 * @param Mixed $dn
2193 * @param Boolean $merge optional
2194 * @param String $type optional
2195 * @access public
2196 * @return Boolean
2198 function setDN($dn, $merge = false, $type = 'utf8String')
2200 if (!$merge) {
2201 $this->dn = NULL;
2204 if (is_array($dn)) {
2205 if (isset($dn['rdnSequence'])) {
2206 $this->dn = $dn; // No merge here.
2207 return true;
2210 // handles stuff generated by openssl_x509_parse()
2211 foreach ($dn as $prop => $value) {
2212 if (!$this->setDNProp($prop, $value, $type)) {
2213 return false;
2216 return true;
2219 // handles everything else
2220 $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
2221 for ($i = 1; $i < count($results); $i+=2) {
2222 $prop = trim($results[$i], ', =/');
2223 $value = $results[$i + 1];
2224 if (!$this->setDNProp($prop, $value, $type)) {
2225 return false;
2229 return true;
2233 * Get the Distinguished Name for a certificates subject
2235 * @param Mixed $format optional
2236 * @param Array $dn optional
2237 * @access public
2238 * @return Boolean
2240 function getDN($format = FILE_X509_DN_ARRAY, $dn = NULL)
2242 if (!isset($dn)) {
2243 $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
2246 switch ((int) $format) {
2247 case FILE_X509_DN_ARRAY:
2248 return $dn;
2249 case FILE_X509_DN_ASN1:
2250 $asn1 = new File_ASN1();
2251 $asn1->loadOIDs($this->oids);
2252 $filters = array();
2253 $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
2254 $asn1->loadFilters($filters);
2255 return $asn1->encodeDER($dn, $this->Name);
2256 case FILE_X509_DN_OPENSSL:
2257 $dn = $this->getDN(FILE_X509_DN_STRING, $dn);
2258 if ($dn === false) {
2259 return false;
2261 $attrs = preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
2262 $dn = array();
2263 for ($i = 1; $i < count($attrs); $i += 2) {
2264 $prop = trim($attrs[$i], ', =/');
2265 $value = $attrs[$i + 1];
2266 if (!isset($dn[$prop])) {
2267 $dn[$prop] = $value;
2268 } else {
2269 $dn[$prop] = array_merge((array) $dn[$prop], array($value));
2272 return $dn;
2273 case FILE_X509_DN_CANON:
2274 // No SEQUENCE around RDNs and all string values normalized as
2275 // trimmed lowercase UTF-8 with all spacing as one blank.
2276 $asn1 = new File_ASN1();
2277 $asn1->loadOIDs($this->oids);
2278 $filters = array();
2279 $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
2280 $asn1->loadFilters($filters);
2281 $result = '';
2282 foreach ($dn['rdnSequence'] as $rdn) {
2283 foreach ($rdn as &$attr) {
2284 if (is_array($attr['value'])) {
2285 foreach ($attr['value'] as $type => $v) {
2286 $type = array_search($type, $asn1->ANYmap, true);
2287 if ($type !== false && isset($asn1->stringTypeSize[$type])) {
2288 $v = $asn1->convert($v, $type);
2289 if ($v !== false) {
2290 $v = preg_replace('/\s+/', ' ', $v);
2291 $attr['value'] = strtolower(trim($v));
2292 break;
2298 $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
2300 return $result;
2301 case FILE_X509_DN_HASH:
2302 $dn = $this->getDN(FILE_X509_DN_CANON, $dn);
2303 if (!class_exists('Crypt_Hash')) {
2304 require_once('Crypt/Hash.php');
2306 $hash = new Crypt_Hash('sha1');
2307 $hash = $hash->hash($dn);
2308 extract(unpack('Vhash', $hash));
2309 return strtolower(bin2hex(pack('N', $hash)));
2312 // Defaut is to return a string.
2313 $start = true;
2314 $output = '';
2315 $asn1 = new File_ASN1();
2316 foreach ($dn['rdnSequence'] as $field) {
2317 $prop = $field[0]['type'];
2318 $value = $field[0]['value'];
2320 $delim = ', ';
2321 switch ($prop) {
2322 case 'id-at-countryName':
2323 $desc = 'C=';
2324 break;
2325 case 'id-at-stateOrProvinceName':
2326 $desc = 'ST=';
2327 break;
2328 case 'id-at-organizationName':
2329 $desc = 'O=';
2330 break;
2331 case 'id-at-organizationalUnitName':
2332 $desc = 'OU=';
2333 break;
2334 case 'id-at-commonName':
2335 $desc = 'CN=';
2336 break;
2337 case 'id-at-localityName':
2338 $desc = 'L=';
2339 break;
2340 case 'id-at-surname':
2341 $desc = 'SN=';
2342 break;
2343 case 'id-at-uniqueIdentifier':
2344 $delim = '/';
2345 $desc = 'x500UniqueIdentifier=';
2346 break;
2347 default:
2348 $delim = '/';
2349 $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop) . '=';
2352 if (!$start) {
2353 $output.= $delim;
2355 if (is_array($value)) {
2356 foreach ($value as $type => $v) {
2357 $type = array_search($type, $asn1->ANYmap, true);
2358 if ($type !== false && isset($asn1->stringTypeSize[$type])) {
2359 $v = $asn1->convert($v, $type);
2360 if ($v !== false) {
2361 $value = $v;
2362 break;
2366 if (is_array($value)) {
2367 $value = array_pop($value); // Always strip data type.
2370 $output.= $desc . $value;
2371 $start = false;
2374 return $output;
2378 * Get the Distinguished Name for a certificate/crl issuer
2380 * @param Integer $format optional
2381 * @access public
2382 * @return Mixed
2384 function getIssuerDN($format = FILE_X509_DN_ARRAY)
2386 switch (true) {
2387 case !isset($this->currentCert) || !is_array($this->currentCert):
2388 break;
2389 case isset($this->currentCert['tbsCertificate']):
2390 return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']);
2391 case isset($this->currentCert['tbsCertList']):
2392 return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']);
2395 return false;
2399 * Get the Distinguished Name for a certificate/csr subject
2400 * Alias of getDN()
2402 * @param Integer $format optional
2403 * @access public
2404 * @return Mixed
2406 function getSubjectDN($format = FILE_X509_DN_ARRAY)
2408 switch (true) {
2409 case !empty($this->dn):
2410 return $this->getDN($format);
2411 case !isset($this->currentCert) || !is_array($this->currentCert):
2412 break;
2413 case isset($this->currentCert['tbsCertificate']):
2414 return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']);
2415 case isset($this->currentCert['certificationRequestInfo']):
2416 return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']);
2419 return false;
2423 * Get an individual Distinguished Name property for a certificate/crl issuer
2425 * @param String $propName
2426 * @param Boolean $withType optional
2427 * @access public
2428 * @return Mixed
2430 function getIssuerDNProp($propName, $withType = false)
2432 switch (true) {
2433 case !isset($this->currentCert) || !is_array($this->currentCert):
2434 break;
2435 case isset($this->currentCert['tbsCertificate']):
2436 return $this->getDNProp($propname, $this->currentCert['tbsCertificate']['issuer'], $withType);
2437 case isset($this->currentCert['tbsCertList']):
2438 return $this->getDNProp($propname, $this->currentCert['tbsCertList']['issuer'], $withType);
2441 return false;
2445 * Get an individual Distinguished Name property for a certificate/csr subject
2447 * @param String $propName
2448 * @param Boolean $withType optional
2449 * @access public
2450 * @return Mixed
2452 function getSubjectDNProp($propName, $withType = false)
2454 switch (true) {
2455 case !empty($this->dn):
2456 return $this->getDNProp($propName, NULL, $withType);
2457 case !isset($this->currentCert) || !is_array($this->currentCert):
2458 break;
2459 case isset($this->currentCert['tbsCertificate']):
2460 return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType);
2461 case isset($this->currentCert['certificationRequestInfo']):
2462 return $this->getDNProp($propname, $this->currentCert['certificationRequestInfo']['subject'], $withType);
2465 return false;
2469 * Set public key
2471 * Key needs to be a Crypt_RSA object
2473 * @param Object $key
2474 * @access public
2475 * @return Boolean
2477 function setPublicKey($key)
2479 $this->publicKey = $key;
2483 * Set private key
2485 * Key needs to be a Crypt_RSA object
2487 * @param Object $key
2488 * @access public
2490 function setPrivateKey($key)
2492 $this->privateKey = $key;
2496 * Gets the public key
2498 * Returns a Crypt_RSA object or a false.
2500 * @access public
2501 * @return Mixed
2503 function getPublicKey()
2505 if (isset($this->publicKey)) {
2506 return $this->publicKey;
2509 if (isset($this->currentCert) && is_array($this->currentCert)) {
2510 foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) {
2511 $keyinfo = $this->_subArray($this->currentCert, $path);
2512 if (!empty($keyinfo)) {
2513 break;
2517 if (empty($keyinfo)) {
2518 return false;
2521 $key = $keyinfo['subjectPublicKey'];
2523 switch ($keyinfo['algorithm']['algorithm']) {
2524 case 'rsaEncryption':
2525 if (!class_exists('Crypt_RSA')) {
2526 require_once('Crypt/RSA.php');
2528 $publicKey = new Crypt_RSA();
2529 $publicKey->loadKey($key);
2530 $publicKey->setPublicKey();
2531 break;
2532 default:
2533 return false;
2536 return $publicKey;
2540 * Load a Certificate Signing Request
2542 * @param String $csr
2543 * @access public
2544 * @return Mixed
2546 function loadCSR($csr)
2548 // see http://tools.ietf.org/html/rfc2986
2550 $asn1 = new File_ASN1();
2552 $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $csr);
2553 $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2554 if ($temp != false) {
2555 $csr = $temp;
2557 $orig = $csr;
2559 if ($csr === false) {
2560 $this->currentCert = false;
2561 return false;
2564 $asn1->loadOIDs($this->oids);
2565 $decoded = $asn1->decodeBER($csr);
2567 if (empty($decoded)) {
2568 $this->currentCert = false;
2569 return false;
2572 $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
2573 if (!isset($csr) || $csr === false) {
2574 $this->currentCert = false;
2575 return false;
2578 $this->dn = $csr['certificationRequestInfo']['subject'];
2580 $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
2582 $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
2583 $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
2584 $key = $this->_reformatKey($algorithm, $key);
2586 switch ($algorithm) {
2587 case 'rsaEncryption':
2588 if (!class_exists('Crypt_RSA')) {
2589 require_once('Crypt/RSA.php');
2591 $this->publicKey = new Crypt_RSA();
2592 $this->publicKey->loadKey($key);
2593 $this->publicKey->setPublicKey();
2594 break;
2595 default:
2596 $this->publicKey = NULL;
2599 $this->currentKeyIdentifier = NULL;
2600 $this->currentCert = $csr;
2602 return $csr;
2606 * Save CSR request
2608 * @param Array $csr
2609 * @access public
2610 * @return String
2612 function saveCSR($csr)
2614 if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
2615 return false;
2618 switch ($csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']) {
2619 case 'rsaEncryption':
2620 $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
2621 base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
2624 $asn1 = new File_ASN1();
2626 $asn1->loadOIDs($this->oids);
2628 $filters = array();
2629 $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] =
2630 array('type' => FILE_ASN1_TYPE_UTF8_STRING);
2632 $asn1->loadFilters($filters);
2634 $csr = $asn1->encodeDER($csr, $this->CertificationRequest);
2636 return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr)) . '-----END CERTIFICATE REQUEST-----';
2640 * Load a Certificate Revocation List
2642 * @param String $crl
2643 * @access public
2644 * @return Mixed
2646 function loadCRL($crl)
2648 $asn1 = new File_ASN1();
2650 $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $csr);
2651 $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
2652 if ($temp != false) {
2653 $crl = $temp;
2655 $orig = $crl;
2657 if ($crl === false) {
2658 $this->currentCert = false;
2659 return false;
2662 $asn1->loadOIDs($this->oids);
2663 $decoded = $asn1->decodeBER($crl);
2665 if (empty($decoded)) {
2666 $this->currentCert = false;
2667 return false;
2670 $crl = $asn1->asn1map($decoded[0], $this->CertificateList);
2671 if (!isset($crl) || $crl === false) {
2672 $this->currentCert = false;
2673 return false;
2676 $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
2678 $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
2679 $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates');
2680 if (is_array($rclist)) {
2681 foreach ($rclist as $i => $extension) {
2682 $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1);
2686 $this->currentKeyIdentifier = NULL;
2687 $this->currentCert = $crl;
2689 return $crl;
2693 * Save Certificate Revocation List.
2695 * @param Array $crl
2696 * @access public
2697 * @return String
2699 function saveCRL($crl)
2701 if (!is_array($crl) || !isset($crl['tbsCertList'])) {
2702 return false;
2705 $asn1 = new File_ASN1();
2707 $asn1->loadOIDs($this->oids);
2709 $filters = array();
2710 $filters['tbsCertList']['issuer']['rdnSequence']['value'] =
2711 $filters['tbsCertList']['signature']['parameters'] =
2712 $filters['signatureAlgorithm']['parameters'] =
2713 array('type' => FILE_ASN1_TYPE_UTF8_STRING);
2715 if (empty($crl['tbsCertList']['signature']['parameters'])) {
2716 $filters['tbsCertList']['signature']['parameters'] =
2717 array('type' => FILE_ASN1_TYPE_NULL);
2720 if (empty($crl['signatureAlgorithm']['parameters'])) {
2721 $filters['signatureAlgorithm']['parameters'] =
2722 array('type' => FILE_ASN1_TYPE_NULL);
2725 $asn1->loadFilters($filters);
2727 $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
2728 $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates');
2729 if (is_array($rclist)) {
2730 foreach ($rclist as $i => $extension) {
2731 $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1);
2735 $crl = $asn1->encodeDER($crl, $this->CertificateList);
2737 return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl)) . '-----END X509 CRL-----';
2741 * Sign an X.509 certificate
2743 * $issuer's private key needs to be loaded.
2744 * $subject can be either an existing X.509 cert (if you want to resign it),
2745 * a CSR or something with the DN and public key explicitly set.
2747 * @param File_X509 $issuer
2748 * @param File_X509 $subject
2749 * @param String $signatureAlgorithm optional
2750 * @access public
2751 * @return Mixed
2753 function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
2755 if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
2756 return false;
2759 if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) {
2760 return false;
2763 $currentCert = isset($this->currentCert) ? $this->currentCert : NULL;
2764 $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL;
2766 if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
2767 $this->currentCert = $subject->currentCert;
2768 $this->currentCert['tbsCertificate']['signature']['algorithm'] =
2769 $this->currentCert['signatureAlgorithm']['algorithm'] =
2770 $signatureAlgorithm;
2771 if (!empty($this->startDate)) {
2772 $this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate;
2773 unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']);
2775 if (!empty($this->endDate)) {
2776 $this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime'] = $this->endDate;
2777 unset($this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime']);
2779 if (!empty($this->serialNumber)) {
2780 $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
2782 if (!empty($subject->dn)) {
2783 $this->currentCert['tbsCertificate']['subject'] = $subject->dn;
2785 if (!empty($subject->publicKey)) {
2786 $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
2788 $this->removeExtension('id-ce-authorityKeyIdentifier');
2789 if (isset($subject->domains)) {
2790 $this->removeExtension('id-ce-subjectAltName');
2792 } else if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) {
2793 return false;
2794 } else {
2795 if (!isset($subject->publicKey)) {
2796 return false;
2799 $startDate = !empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T');
2800 $endDate = !empty($this->endDate) ? $this->endDate : @date('M j H:i:s Y T', strtotime('+1 year'));
2801 $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new Math_BigInteger();
2803 $this->currentCert = array(
2804 'tbsCertificate' =>
2805 array(
2806 'version' => 'v3',
2807 'serialNumber' => $serialNumber, // $this->setserialNumber()
2808 'signature' => array('algorithm' => $signatureAlgorithm),
2809 'issuer' => false, // this is going to be overwritten later
2810 'validity' => array(
2811 'notBefore' => array('generalTime' => $startDate), // $this->setStartDate()
2812 'notAfter' => array('generalTime' => $endDate) // $this->setEndDate()
2814 'subject' => $subject->dn,
2815 'subjectPublicKeyInfo' => $subjectPublicKey
2817 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
2818 'signature' => false // this is going to be overwritten later
2822 $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;
2824 if (isset($issuer->currentKeyIdentifier)) {
2825 $this->setExtension('id-ce-authorityKeyIdentifier', array(
2826 //'authorityCertIssuer' => array(
2827 // array(
2828 // 'directoryName' => $issuer->dn
2829 // )
2830 //),
2831 'keyIdentifier' => $issuer->currentKeyIdentifier
2834 //$extensions = &$this->currentCert['tbsCertificate']['extensions'];
2835 //if (isset($issuer->serialNumber)) {
2836 // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
2838 //unset($extensions);
2841 if (isset($subject->currentKeyIdentifier)) {
2842 $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier);
2845 if (isset($subject->domains) && count($subject->domains) > 1) {
2846 $this->setExtension('id-ce-subjectAltName',
2847 array_map(array('File_X509', '_dnsName'), $subject->domains));
2850 if ($this->caFlag) {
2851 $keyUsage = $this->getExtension('id-ce-keyUsage');
2852 if (!$keyUsage) {
2853 $keyUsage = array();
2856 $this->setExtension('id-ce-keyUsage',
2857 array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign'))))
2860 $basicConstraints = $this->getExtension('id-ce-basicConstraints');
2861 if (!$basicConstraints) {
2862 $basicConstraints = array();
2865 $this->setExtension('id-ce-basicConstraints',
2866 array_unique(array_merge(array('cA' => true), $basicConstraints)), true);
2868 if (!isset($subject->currentKeyIdentifier)) {
2869 $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false);
2873 // resync $this->signatureSubject
2874 // save $tbsCertificate in case there are any File_ASN1_Element objects in it
2875 $tbsCertificate = $this->currentCert['tbsCertificate'];
2876 $this->loadX509($this->saveX509($this->currentCert));
2878 $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
2879 $result['tbsCertificate'] = $tbsCertificate;
2881 $this->currentCert = $currentCert;
2882 $this->signatureSubject = $signatureSubject;
2884 return $result;
2888 * Sign a CSR
2890 * @access public
2891 * @return Mixed
2893 function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
2895 if (!is_object($this->privateKey) || empty($this->dn)) {
2896 return false;
2899 $origPublicKey = $this->publicKey;
2900 $class = get_class($this->privateKey);
2901 $this->publicKey = new $class();
2902 $this->publicKey->loadKey($this->privateKey->getPublicKey());
2903 $this->publicKey->setPublicKey();
2904 if (!($publicKey = $this->_formatSubjectPublicKey())) {
2905 return false;
2907 $this->publicKey = $origPublicKey;
2909 $currentCert = isset($this->currentCert) ? $this->currentCert : NULL;
2910 $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL;
2912 if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
2913 $this->currentCert['signatureAlgorithm']['algorithm'] =
2914 $signatureAlgorithm;
2915 if (!empty($this->dn)) {
2916 $this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
2918 $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
2919 } else {
2920 $this->currentCert = array(
2921 'certificationRequestInfo' =>
2922 array(
2923 'version' => 'v1',
2924 'subject' => $this->dn,
2925 'subjectPKInfo' => $publicKey
2927 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
2928 'signature' => false // this is going to be overwritten later
2932 // resync $this->signatureSubject
2933 // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
2934 $certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
2935 $this->loadCSR($this->saveCSR($this->currentCert));
2937 $result = $this->_sign($this->privateKey, $signatureAlgorithm);
2938 $result['certificationRequestInfo'] = $certificationRequestInfo;
2940 $this->currentCert = $currentCert;
2941 $this->signatureSubject = $signatureSubject;
2943 return $result;
2947 * Sign a CRL
2949 * $issuer's private key needs to be loaded.
2951 * @param File_X509 $issuer
2952 * @param File_X509 $crl
2953 * @param String $signatureAlgorithm optional
2954 * @access public
2955 * @return Mixed
2957 function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption')
2959 if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
2960 return false;
2963 $currentCert = isset($this->currentCert) ? $this->currentCert : NULL;
2964 $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : NULL;
2965 $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T');
2967 if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
2968 $this->currentCert = $crl->currentCert;
2969 $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
2970 $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
2971 } else {
2972 $this->currentCert = array(
2973 'tbsCertList' =>
2974 array(
2975 'version' => 'v2',
2976 'signature' => array('algorithm' => $signatureAlgorithm),
2977 'issuer' => false, // this is going to be overwritten later
2978 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate()
2980 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
2981 'signature' => false // this is going to be overwritten later
2985 $tbsCertList = &$this->currentCert['tbsCertList'];
2986 $tbsCertList['issuer'] = $issuer->dn;
2987 $tbsCertList['thisUpdate'] = array('generalTime' => $thisUpdate);
2989 if (!empty($this->endDate)) {
2990 $tbsCertList['nextUpdate'] = array('generalTime' => $this->endDate); // $this->setEndDate()
2992 else {
2993 unset($tbsCertList['nextUpdate']);
2996 if (!empty($this->serialNumber)) {
2997 $crlNumber = $this->serialNumber;
2999 else {
3000 $crlNumber = $this->getExtension('id-ce-cRLNumber');
3001 $crlNumber = $crlNumber !== false ? $crlNumber->add(new Math_BigInteger(1)) : NULL;
3004 $this->removeExtension('id-ce-authorityKeyIdentifier');
3005 $this->removeExtension('id-ce-issuerAltName');
3007 // Be sure version >= v2 if some extension found.
3008 $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0;
3009 if (!$version) {
3010 if (!empty($tbsCertList['crlExtensions'])) {
3011 $version = 1; // v2.
3013 elseif (!empty($tbsCertList['revokedCertificates'])) {
3014 foreach ($tbsCertList['revokedCertificates'] as $cert) {
3015 if (!empty($cert['crlEntryExtensions'])) {
3016 $version = 1; // v2.
3021 if ($version) {
3022 $tbsCertList['version'] = $version;
3026 // Store additional extensions.
3027 if (!empty($tbsCertList['version'])) { // At least v2.
3028 if (!empty($crlNumber)) {
3029 $this->setExtension('id-ce-cRLNumber', $crlNumber);
3032 if (isset($issuer->currentKeyIdentifier)) {
3033 $this->setExtension('id-ce-authorityKeyIdentifier', array(
3034 //'authorityCertIssuer' => array(
3035 // array(
3036 // 'directoryName' => $issuer->dn
3037 // )
3038 //),
3039 'keyIdentifier' => $issuer->currentKeyIdentifier
3042 //$extensions = &$tbsCertList['crlExtensions'];
3043 //if (isset($issuer->serialNumber)) {
3044 // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
3046 //unset($extensions);
3049 $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert);
3051 if ($issuerAltName !== false) {
3052 $this->setExtension('id-ce-issuerAltName', $issuerAltName);
3056 if (empty($tbsCertList['revokedCertificates'])) {
3057 unset($tbsCertList['revokedCertificates']);
3060 unset($tbsCertList);
3062 // resync $this->signatureSubject
3063 // save $tbsCertList in case there are any File_ASN1_Element objects in it
3064 $tbsCertList = $this->currentCert['tbsCertList'];
3065 $this->loadCRL($this->saveCRL($this->currentCert));
3067 $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
3068 $result['tbsCertList'] = $tbsCertList;
3070 $this->currentCert = $currentCert;
3071 $this->signatureSubject = $signatureSubject;
3073 return $result;
3077 * X.509 certificate signing helper function.
3079 * @param Object $key
3080 * @param File_X509 $subject
3081 * @param String $signatureAlgorithm
3082 * @access public
3083 * @return Mixed
3085 function _sign($key, $signatureAlgorithm)
3087 switch (strtolower(get_class($key))) {
3088 case 'crypt_rsa':
3089 switch ($signatureAlgorithm) {
3090 case 'md2WithRSAEncryption':
3091 case 'md5WithRSAEncryption':
3092 case 'sha1WithRSAEncryption':
3093 case 'sha224WithRSAEncryption':
3094 case 'sha256WithRSAEncryption':
3095 case 'sha384WithRSAEncryption':
3096 case 'sha512WithRSAEncryption':
3097 $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
3098 $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
3100 $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
3101 return $this->currentCert;
3103 default:
3104 return false;
3109 * Set certificate start date
3111 * @param String $date
3112 * @access public
3114 function setStartDate($date)
3116 $this->startDate = @date('M j H:i:s Y T', @strtotime($date));
3120 * Set certificate end date
3122 * @param String $date
3123 * @access public
3125 function setEndDate($date)
3128 To indicate that a certificate has no well-defined expiration date,
3129 the notAfter SHOULD be assigned the GeneralizedTime value of
3130 99991231235959Z.
3132 -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
3134 if (strtolower($date) == 'lifetime') {
3135 $temp = '99991231235959Z';
3136 $asn1 = new File_ASN1();
3137 $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
3138 $this->endDate = new File_ASN1_Element($temp);
3139 } else {
3140 $this->endDate = @date('M j H:i:s Y T', @strtotime($date));
3145 * Set Serial Number
3147 * @param String $serial
3148 * @param $base optional
3149 * @access public
3151 function setSerialNumber($serial, $base = -256)
3153 $this->serialNumber = new Math_BigInteger($serial, $base);
3157 * Turns the certificate into a certificate authority
3159 * @access public
3161 function makeCA()
3163 $this->caFlag = true;
3167 * Get a reference to a subarray
3169 * @param array $root
3170 * @param String $path absolute path with / as component separator
3171 * @param Boolean $create optional
3172 * @access private
3173 * @return array item ref or false
3175 function &_subArray(&$root, $path, $create = false)
3177 $false = false;
3179 if (!is_array($root)) {
3180 return $false;
3183 foreach (explode('/', $path) as $i) {
3184 if (!is_array($root)) {
3185 return $false;
3188 if (!isset($root[$i])) {
3189 if (!$create) {
3190 return $false;
3193 $root[$i] = array();
3196 $root = &$root[$i];
3199 return $root;
3203 * Get a reference to an extension subarray
3205 * @param array $root
3206 * @param String $path optional absolute path with / as component separator
3207 * @param Boolean $create optional
3208 * @access private
3209 * @return array ref or false
3211 function &_extensions(&$root, $path = NULL, $create = false)
3213 if (!isset($root)) {
3214 $root = $this->currentCert;
3217 switch (true) {
3218 case !empty($path):
3219 case !is_array($root):
3220 break;
3221 case isset($root['tbsCertificate']):
3222 $path = 'tbsCertificate/extensions';
3223 break;
3224 case isset($root['tbsCertList']):
3225 $path = 'tbsCertList/crlExtensions';
3226 break;
3229 $extensions = &$this->_subArray($root, $path, $create);
3231 if (!is_array($extensions)) {
3232 $false = false;
3233 return $false;
3236 return $extensions;
3240 * Remove an Extension
3242 * @param String $id
3243 * @param String $path optional
3244 * @access private
3245 * @return Boolean
3247 function _removeExtension($id, $path = NULL)
3249 $extensions = &$this->_extensions($this->currentCert, $path);
3251 if (!is_array($extensions)) {
3252 return false;
3255 $result = false;
3256 foreach ($extensions as $key => $value) {
3257 if ($value['extnId'] == $id) {
3258 unset($extensions[$key]);
3259 $result = true;
3263 $extensions = array_values($extensions);
3264 return $result;
3268 * Get an Extension
3270 * Returns the extension if it exists and false if not
3272 * @param String $id
3273 * @param Array $cert optional
3274 * @param String $path optional
3275 * @access private
3276 * @return Mixed
3278 function _getExtension($id, $cert = NULL, $path = NULL)
3280 $extensions = $this->_extensions($cert, $path);
3282 if (!is_array($extensions)) {
3283 return false;
3286 foreach ($extensions as $key => $value) {
3287 if ($value['extnId'] == $id) {
3288 return $value['extnValue'];
3292 return false;
3296 * Returns a list of all extensions in use
3298 * @param array $cert optional
3299 * @param String $path optional
3300 * @access private
3301 * @return Array
3303 function _getExtensions($cert = NULL, $path = NULL)
3305 $exts = $this->_extensions($cert, $path);
3306 $extensions = array();
3308 if (is_array($exts)) {
3309 foreach ($exts as $extension) {
3310 $extensions[] = $extension['extnId'];
3314 return $extensions;
3318 * Set an Extension
3320 * @param String $id
3321 * @param Mixed $value
3322 * @param Boolean $critical optional
3323 * @param Boolean $replace optional
3324 * @param String $path optional
3325 * @access private
3326 * @return Boolean
3328 function _setExtension($id, $value, $critical = false, $replace = true, $path = NULL)
3330 $extensions = &$this->_extensions($this->currentCert, $path, true);
3332 if (!is_array($extensions)) {
3333 return false;
3336 $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value);
3338 foreach ($extensions as $key => $value) {
3339 if ($value['extnId'] == $id) {
3340 if (!$replace) {
3341 return false;
3344 $extensions[$key] = $newext;
3345 return true;
3349 $extensions[] = $newext;
3350 return true;
3354 * Remove a certificate or CRL Extension
3356 * @param String $id
3357 * @access public
3358 * @return Boolean
3360 function removeExtension($id)
3362 return $this->_removeExtension($id);
3366 * Get a certificate or CRL Extension
3368 * Returns the extension if it exists and false if not
3370 * @param String $id
3371 * @param Array $cert optional
3372 * @access public
3373 * @return Mixed
3375 function getExtension($id, $cert = NULL)
3377 return $this->_getExtension($id, $cert);
3381 * Returns a list of all extensions in use in certificate or CRL
3383 * @param array $cert optional
3384 * @access public
3385 * @return Array
3387 function getExtensions($cert = NULL)
3389 return $this->_getExtensions($cert);
3393 * Set a certificate or CRL Extension
3395 * @param String $id
3396 * @param Mixed $value
3397 * @param Boolean $critical optional
3398 * @param Boolean $replace optional
3399 * @access public
3400 * @return Boolean
3402 function setExtension($id, $value, $critical = false, $replace = true)
3404 return $this->_setExtension($id, $value, $critical, $replace);
3408 * Sets the subject key identifier
3410 * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
3412 * @param String $value
3413 * @access public
3415 function setKeyIdentifier($value)
3417 if (empty($value)) {
3418 unset($this->currentKeyIdentifier);
3419 } else {
3420 $this->currentKeyIdentifier = base64_encode($value);
3425 * Compute a public key identifier.
3427 * Although key identifiers may be set to any unique value, this function
3428 * computes key identifiers from public key according to the two
3429 * recommended methods (4.2.1.2 RFC 3280).
3430 * Highly polymorphic: try to accept all possible forms of key:
3431 * - Key object
3432 * - File_X509 object with public or private key defined
3433 * - Certificate or CSR array
3434 * - File_ASN1_Element object
3435 * - PEM or DER string
3437 * @param Mixed $key optional
3438 * @param Integer $method optional
3439 * @access public
3440 * @return String binary key identifier
3442 function computeKeyIdentifier($key = NULL, $method = 1)
3444 if (is_null($key)) {
3445 $key = $this;
3448 switch (true) {
3449 case is_string($key):
3450 break;
3451 case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
3452 return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
3453 case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
3454 return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
3455 case !is_object($key):
3456 return false;
3457 case strtolower(get_class($key)) == 'file_asn1_element':
3458 $asn1 = new File_ASN1();
3459 $decoded = $asn1->decodeBER($cert);
3460 if (empty($decoded)) {
3461 return false;
3463 $key = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING));
3464 break;
3465 case strtolower(get_class($key)) == 'file_x509':
3466 if (isset($key->publicKey)) {
3467 return $this->computeKeyIdentifier($key->publicKey, $method);
3469 if (isset($key->privateKey)) {
3470 return $this->computeKeyIdentifier($key->privateKey, $method);
3472 if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
3473 return $this->computeKeyIdentifier($key->currentCert, $method);
3475 return false;
3476 default: // Should be a key object (i.e.: Crypt_RSA).
3477 $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW);
3478 break;
3481 // If in PEM format, convert to binary.
3482 if (preg_match('#^-----BEGIN #', $key)) {
3483 $key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key));
3486 // Now we have the key string: compute its sha-1 sum.
3487 if (!class_exists('Crypt_Hash')) {
3488 require_once('Crypt/Hash.php');
3490 $hash = new Crypt_Hash('sha1');
3491 $hash = $hash->hash($key);
3493 if ($method == 2) {
3494 $hash = substr($hash, -8);
3495 $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
3498 return $hash;
3502 * Format a public key as appropriate
3504 * @access private
3505 * @return Array
3507 function _formatSubjectPublicKey()
3509 if (!isset($this->publicKey) || !is_object($this->publicKey)) {
3510 return false;
3513 switch (strtolower(get_class($this->publicKey))) {
3514 case 'crypt_rsa':
3515 // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
3516 // the former is a good example of how to do fuzzing on the public key
3517 //return new File_ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey())));
3518 return array(
3519 'algorithm' => array('algorithm' => 'rsaEncryption'),
3520 'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW)
3522 default:
3523 return false;
3528 * Set the domain name's which the cert is to be valid for
3530 * @access public
3531 * @return Array
3533 function setDomain()
3535 $this->domains = func_get_args();
3536 $this->removeDNProp('id-at-commonName');
3537 $this->setDNProp('id-at-commonName', $this->domains[0]);
3541 * Helper function to build domain array
3543 * @access private
3544 * @param String $domain
3545 * @return Array
3547 function _dnsName($domain)
3549 return array('dNSName' => $domain);
3553 * Get the index of a revoked certificate.
3555 * @param array $rclist
3556 * @param String $serial
3557 * @param Boolean $create optional
3558 * @access private
3559 * @return Integer or false
3561 function _revokedCertificate(&$rclist, $serial, $create = false)
3563 $serial = new Math_BigInteger($serial);
3565 foreach ($rclist as $i => $rc) {
3566 if (!($serial->compare($rc['userCertificate']))) {
3567 return $i;
3571 if (!$create) {
3572 return false;
3575 $i = count($rclist);
3576 $rclist[] = array('userCertificate' => $serial,
3577 'revocationDate' => array('generalTime' => @date('M j H:i:s Y T')));
3578 return $i;
3582 * Revoke a certificate.
3584 * @param String $serial
3585 * @param String $date optional
3586 * @access public
3587 * @return Boolean
3589 function revoke($serial, $date = NULL)
3591 if (isset($this->currentCert['tbsCertList'])) {
3592 if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
3593 if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked
3594 if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
3596 if (!empty($date)) {
3597 $rclist[$i]['revocationDate'] = array('generalTime' => $date);
3600 return true;
3606 return false;
3610 * Unrevoke a certificate.
3612 * @param String $serial
3613 * @access public
3614 * @return Boolean
3616 function unrevoke($serial)
3618 if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
3619 if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
3620 unset($rclist[$i]);
3621 $rclist = array_values($rclist);
3622 return true;
3626 return false;
3630 * Get a revoked certificate.
3632 * @param String $serial
3633 * @access public
3634 * @return Mixed
3636 function getRevoked($serial)
3638 if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
3639 if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
3640 return $rclist[$i];
3644 return false;
3648 * List revoked certificates
3650 * @param array $crl optional
3651 * @access public
3652 * @return array
3654 function listRevoked($crl = NULL)
3656 if (!isset($crl)) {
3657 $crl = $this->currentCert;
3660 if (!isset($crl['tbsCertList'])) {
3661 return false;
3664 $result = array();
3666 if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
3667 foreach ($rclist as $rc) {
3668 $result[] = $rc['userCertificate']->toString();
3672 return $result;
3676 * Remove a Revoked Certificate Extension
3678 * @param String $serial
3679 * @param String $id
3680 * @access public
3681 * @return Boolean
3683 function removeRevokedCertificateExtension($serial, $id)
3685 if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
3686 if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
3687 return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
3691 return false;
3695 * Get a Revoked Certificate Extension
3697 * Returns the extension if it exists and false if not
3699 * @param String $serial
3700 * @param String $id
3701 * @param Array $crl optional
3702 * @access public
3703 * @return Mixed
3705 function getRevokedCertificateExtension($serial, $id, $crl = NULL)
3707 if (!isset($crl)) {
3708 $crl = $this->currentCert;
3711 if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
3712 if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
3713 return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
3717 return false;
3721 * Returns a list of all extensions in use for a given revoked certificate
3723 * @param String $serial
3724 * @param array $crl optional
3725 * @access public
3726 * @return Array
3728 function getRevokedCertificateExtensions($serial, $crl = NULL)
3730 if (!isset($crl)) {
3731 $crl = $this->currentCert;
3734 if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
3735 if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
3736 return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
3740 return false;
3744 * Set a Revoked Certificate Extension
3746 * @param String $serial
3747 * @param String $id
3748 * @param Mixed $value
3749 * @param Boolean $critical optional
3750 * @param Boolean $replace optional
3751 * @access public
3752 * @return Boolean
3754 function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
3756 if (isset($this->currentCert['tbsCertList'])) {
3757 if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
3758 if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
3759 return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
3764 return false;