PageRenderTime 61ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/vendors/phpseclib/File/X509.php

https://bitbucket.org/ttalov/fgcu_pci
PHP | 2582 lines | 1773 code | 245 blank | 564 comment | 152 complexity | f1a7938a31c368b2a98cd292c03d0a64 MD5 | raw file
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP X.509 Parser
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * Encode and decode X.509 certificates.
  9. *
  10. * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
  11. * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
  12. *
  13. * Note that loading an X.509 certificate and resaving it may invalidate the signature. The reason being that the signature is based on a
  14. * portion of the certificate that contains optional parameters with default values. ie. if the parameter isn't there the default value is
  15. * 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
  16. * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the
  17. * the certificate all together unless the certificate is re-signed.
  18. *
  19. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  20. * of this software and associated documentation files (the "Software"), to deal
  21. * in the Software without restriction, including without limitation the rights
  22. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  23. * copies of the Software, and to permit persons to whom the Software is
  24. * furnished to do so, subject to the following conditions:
  25. *
  26. * The above copyright notice and this permission notice shall be included in
  27. * all copies or substantial portions of the Software.
  28. *
  29. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  30. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  31. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  32. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  33. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  34. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  35. * THE SOFTWARE.
  36. *
  37. * @category File
  38. * @package File_X509
  39. * @author Jim Wigginton <terrafrost@php.net>
  40. * @copyright MMXII Jim Wigginton
  41. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  42. * @version $Id$
  43. * @link htp://phpseclib.sourceforge.net
  44. */
  45. /**
  46. * Include File_ASN1
  47. */
  48. if (!class_exists('File_ASN1')) {
  49. require_once('File/ASN1.php');
  50. }
  51. /**
  52. * Flag to only accept signatures signed by certificate authorities
  53. *
  54. * @access public
  55. * @see File_X509::validateSignature()
  56. */
  57. define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1);
  58. /**
  59. * Pure-PHP X.509 Parser
  60. *
  61. * @author Jim Wigginton <terrafrost@php.net>
  62. * @version 0.3.0
  63. * @access public
  64. * @package File_X509
  65. */
  66. class File_X509 {
  67. /**
  68. * ASN.1 syntax for X.509 certificates
  69. *
  70. * @var Array
  71. * @access private
  72. */
  73. var $Certificate;
  74. /**#@+
  75. * ASN.1 syntax for various extensions
  76. *
  77. * @access private
  78. */
  79. var $KeyUsage;
  80. var $ExtKeyUsageSyntax;
  81. var $BasicConstraints;
  82. var $KeyIdentifier;
  83. var $CRLDistributionPoints;
  84. var $AuthorityKeyIdentifier;
  85. var $CertificatePolicies;
  86. var $AuthorityInfoAccessSyntax;
  87. var $SubjectAltName;
  88. var $PrivateKeyUsagePeriod;
  89. var $IssuerAltName;
  90. var $PolicyMappings;
  91. var $NameConstraints;
  92. var $CPSuri;
  93. var $UserNotice;
  94. var $netscape_cert_type;
  95. var $netscape_comment;
  96. /**#@-*/
  97. /**
  98. * ASN.1 syntax for Certificate Signing Requests (RFC2986)
  99. *
  100. * @var Array
  101. * @access private
  102. */
  103. var $CertificationRequest;
  104. /**
  105. * Distinguished Name
  106. *
  107. * @var Array
  108. * @access private
  109. */
  110. var $dn;
  111. /**
  112. * Public key
  113. *
  114. * @var String
  115. * @access private
  116. */
  117. var $publicKey;
  118. /**
  119. * Private key
  120. *
  121. * @var String
  122. * @access private
  123. */
  124. var $privateKey;
  125. /**
  126. * Object identifiers for X.509 certificates
  127. *
  128. * @var Array
  129. * @access private
  130. * @link http://en.wikipedia.org/wiki/Object_identifier
  131. */
  132. var $oids;
  133. /**
  134. * The certificate authorities
  135. *
  136. * @var Array
  137. * @access private
  138. */
  139. var $CAs;
  140. /**
  141. * The currently loaded certificate
  142. *
  143. * @var Array
  144. * @access private
  145. */
  146. var $currentCert;
  147. /**
  148. * The signature subject
  149. *
  150. * There's no guarantee File_X509 is going to reencode an X.509 cert in the same way it was originally
  151. * encoded so we take save the portion of the original cert that the signature would have made for.
  152. *
  153. * @var String
  154. * @access private
  155. */
  156. var $signatureSubject;
  157. /**
  158. * Certificate Start Date
  159. *
  160. * @var String
  161. * @access private
  162. */
  163. var $startDate;
  164. /**
  165. * Certificate End Date
  166. *
  167. * @var String
  168. * @access private
  169. */
  170. var $endDate;
  171. /**
  172. * Serial Number
  173. *
  174. * @var String
  175. * @access private
  176. */
  177. var $serialNumber;
  178. /**
  179. * Key Identifier
  180. *
  181. * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and
  182. * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
  183. *
  184. * @var String
  185. * @access private
  186. */
  187. var $keyIdentifier;
  188. /**
  189. * CA Flag
  190. *
  191. * @var Boolean
  192. * @access private
  193. */
  194. var $caFlag = false;
  195. /**
  196. * Default Constructor.
  197. *
  198. * @return File_X509
  199. * @access public
  200. */
  201. function File_X509()
  202. {
  203. // Explicitly Tagged Module, 1988 Syntax
  204. // http://tools.ietf.org/html/rfc5280#appendix-A.1
  205. $temp = array('min' => 1, 'max' => -1);
  206. $DirectoryString = array(
  207. 'type' => FILE_ASN1_TYPE_CHOICE,
  208. 'children' => array(
  209. 'teletexString' => $temp + array('type' => FILE_ASN1_TYPE_TELETEX_STRING),
  210. 'printableString' => $temp + array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
  211. 'universalString' => $temp + array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING),
  212. 'utf8String' => $temp + array('type' => FILE_ASN1_TYPE_UTF8_STRING),
  213. 'bmpString' => $temp + array('type' => FILE_ASN1_TYPE_BMP_STRING)
  214. )
  215. );
  216. $AttributeValue = array('type' => FILE_ASN1_TYPE_ANY);
  217. $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  218. $AttributeTypeAndValue = array(
  219. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  220. 'children' => array(
  221. 'type' => $AttributeType,
  222. 'value'=> $AttributeValue
  223. )
  224. );
  225. /*
  226. In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
  227. but they can be useful at times when either there is no unique attribute in the entry or you
  228. want to ensure that the entry's DN contains some useful identifying information.
  229. - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
  230. */
  231. $RelativeDistinguishedName = array(
  232. 'type' => FILE_ASN1_TYPE_SET,
  233. 'min' => 1,
  234. 'max' => -1,
  235. 'children' => $AttributeTypeAndValue
  236. );
  237. // http://tools.ietf.org/html/rfc5280#section-4.1.2.4
  238. $RDNSequence = array(
  239. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  240. // RDNSequence does not define a min or a max, which means it doesn't have one
  241. 'min' => 0,
  242. 'max' => -1,
  243. 'children' => $RelativeDistinguishedName
  244. );
  245. $Name = array(
  246. 'type' => FILE_ASN1_TYPE_CHOICE,
  247. 'children' => array(
  248. 'rdnSequence' => $RDNSequence
  249. )
  250. );
  251. // http://tools.ietf.org/html/rfc5280#section-4.1.1.2
  252. $AlgorithmIdentifier = array(
  253. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  254. 'children' => array(
  255. 'algorithm' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  256. 'parameters' => array(
  257. 'type' => FILE_ASN1_TYPE_ANY,
  258. 'optional' => true
  259. )
  260. )
  261. );
  262. /*
  263. A certificate using system MUST reject the certificate if it encounters
  264. a critical extension it does not recognize; however, a non-critical
  265. extension may be ignored if it is not recognized.
  266. http://tools.ietf.org/html/rfc5280#section-4.2
  267. */
  268. $Extension = array(
  269. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  270. 'children' => array(
  271. 'extnId' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  272. 'critical' => array(
  273. 'type' => FILE_ASN1_TYPE_BOOLEAN,
  274. 'optional' => true,
  275. 'default' => false
  276. ),
  277. 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING)
  278. )
  279. );
  280. $Extensions = array(
  281. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  282. 'min' => 1,
  283. // technically, it's MAX, but we'll assume anything < 0 is MAX
  284. 'max' => -1,
  285. // if 'children' isn't an array then 'min' and 'max' must be defined
  286. 'children' => $Extension
  287. );
  288. $SubjectPublicKeyInfo = array(
  289. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  290. 'children' => array(
  291. 'algorithm' => $AlgorithmIdentifier,
  292. 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  293. )
  294. );
  295. $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING);
  296. $Time = array(
  297. 'type' => FILE_ASN1_TYPE_CHOICE,
  298. 'children' => array(
  299. 'utcTime' => array('type' => FILE_ASN1_TYPE_UTC_TIME),
  300. 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
  301. )
  302. );
  303. // http://tools.ietf.org/html/rfc5280#section-4.1.2.5
  304. $Validity = array(
  305. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  306. 'children' => array(
  307. 'notBefore' => $Time,
  308. 'notAfter' => $Time
  309. )
  310. );
  311. $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
  312. $Version = array(
  313. 'type' => FILE_ASN1_TYPE_INTEGER,
  314. 'mapping' => array('v1', 'v2', 'v3')
  315. );
  316. // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
  317. $TBSCertificate = array(
  318. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  319. 'children' => array(
  320. // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
  321. // reenforce that fact
  322. 'version' => array(
  323. 'constant' => 0,
  324. 'optional' => true,
  325. 'explicit' => true,
  326. 'default' => 'v1'
  327. ) + $Version,
  328. 'serialNumber' => $CertificateSerialNumber,
  329. 'signature' => $AlgorithmIdentifier,
  330. 'issuer' => $Name,
  331. 'validity' => $Validity,
  332. 'subject' => $Name,
  333. 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
  334. // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
  335. 'issuerUniqueID' => array(
  336. 'constant' => 1,
  337. 'optional' => true,
  338. 'implicit' => true
  339. ) + $UniqueIdentifier,
  340. 'subjectUniqueID' => array(
  341. 'constant' => 2,
  342. 'optional' => true,
  343. 'implicit' => true
  344. ) + $UniqueIdentifier,
  345. // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
  346. // it's not IMPLICIT, it's EXPLICIT
  347. 'extensions' => array(
  348. 'constant' => 3,
  349. 'optional' => true,
  350. 'explicit' => true
  351. ) + $Extensions
  352. )
  353. );
  354. $this->Certificate = array(
  355. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  356. 'children' => array(
  357. 'tbsCertificate' => $TBSCertificate,
  358. 'signatureAlgorithm' => $AlgorithmIdentifier,
  359. 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  360. )
  361. );
  362. $this->KeyUsage = array(
  363. 'type' => FILE_ASN1_TYPE_BIT_STRING,
  364. 'mapping' => array(
  365. 'digitalSignature',
  366. 'nonRepudiation',
  367. 'keyEncipherment',
  368. 'dataEncipherment',
  369. 'keyAgreement',
  370. 'keyCertSign',
  371. 'cRLSign',
  372. 'encipherOnly',
  373. 'decipherOnly'
  374. )
  375. );
  376. $this->BasicConstraints = array(
  377. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  378. 'children' => array(
  379. 'cA' => array(
  380. 'type' => FILE_ASN1_TYPE_BOOLEAN,
  381. 'optional' => true,
  382. 'default' => false
  383. ),
  384. 'pathLenConstraint' => array(
  385. 'type' => FILE_ASN1_TYPE_INTEGER,
  386. 'optional' => true
  387. )
  388. )
  389. );
  390. $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING);
  391. $OrganizationalUnitNames = array(
  392. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  393. 'min' => 1,
  394. 'max' => 4, // ub-organizational-units
  395. 'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  396. );
  397. $PersonalName = array(
  398. 'type' => FILE_ASN1_TYPE_SET,
  399. 'children' => array(
  400. 'surname' => array(
  401. 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  402. 'constant' => 0,
  403. 'optional' => true,
  404. 'implicit' => true
  405. ),
  406. 'given-name' => array(
  407. 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  408. 'constant' => 1,
  409. 'optional' => true,
  410. 'implicit' => true
  411. ),
  412. 'initials' => array(
  413. 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  414. 'constant' => 2,
  415. 'optional' => true,
  416. 'implicit' => true
  417. ),
  418. 'generation-qualifier' => array(
  419. 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  420. 'constant' => 3,
  421. 'optional' => true,
  422. 'implicit' => true
  423. )
  424. )
  425. );
  426. $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
  427. $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
  428. $PrivateDomainName = array(
  429. 'type' => FILE_ASN1_TYPE_CHOICE,
  430. 'children' => array(
  431. 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
  432. 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  433. )
  434. );
  435. $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
  436. $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
  437. $AdministrationDomainName = array(
  438. 'type' => FILE_ASN1_TYPE_CHOICE,
  439. // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
  440. // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
  441. 'class' => FILE_ASN1_CLASS_APPLICATION,
  442. 'cast' => 2,
  443. 'children' => array(
  444. 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
  445. 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  446. )
  447. );
  448. $CountryName = array(
  449. 'type' => FILE_ASN1_TYPE_CHOICE,
  450. // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
  451. // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
  452. 'class' => FILE_ASN1_CLASS_APPLICATION,
  453. 'cast' => 1,
  454. 'children' => array(
  455. 'x121-dcc-code' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
  456. 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  457. )
  458. );
  459. $AnotherName = array(
  460. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  461. 'children' => array(
  462. 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  463. 'value' => array(
  464. 'type' => FILE_ASN1_TYPE_ANY,
  465. 'constant' => 0,
  466. 'optional' => true,
  467. 'explicit' => true
  468. )
  469. )
  470. );
  471. $ExtensionAttribute = array(
  472. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  473. 'children' => array(
  474. 'extension-attribute-type' => array(
  475. 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  476. 'constant' => 0,
  477. 'optional' => true,
  478. 'implicit' => true
  479. ),
  480. 'extension-attribute-value' => array(
  481. 'type' => FILE_ASN1_TYPE_ANY,
  482. 'constant' => 1,
  483. 'optional' => true,
  484. 'explicit' => true
  485. )
  486. )
  487. );
  488. $ExtensionAttributes = array(
  489. 'type' => FILE_ASN1_TYPE_SET,
  490. 'min' => 1,
  491. 'max' => 256, // ub-extension-attributes
  492. 'children' => $ExtensionAttribute
  493. );
  494. $BuiltInDomainDefinedAttribute = array(
  495. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  496. 'children' => array(
  497. 'type' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
  498. 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  499. )
  500. );
  501. $BuiltInDomainDefinedAttributes = array(
  502. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  503. 'min' => 1,
  504. 'max' => 4, // ub-domain-defined-attributes
  505. 'children' => $BuiltInDomainDefinedAttribute
  506. );
  507. $BuiltInStandardAttributes = array(
  508. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  509. 'children' => array(
  510. 'country-name' => array('optional' => true) + $CountryName,
  511. 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
  512. 'network-address' => array(
  513. 'constant' => 0,
  514. 'optional' => true,
  515. 'implicit' => true
  516. ) + $NetworkAddress,
  517. 'terminal-identifier' => array(
  518. 'constant' => 1,
  519. 'optional' => true,
  520. 'implicit' => true
  521. ) + $TerminalIdentifier,
  522. 'private-domain-name' => array(
  523. 'constant' => 2,
  524. 'optional' => true,
  525. 'explicit' => true
  526. ) + $PrivateDomainName,
  527. 'organization-name' => array(
  528. 'constant' => 3,
  529. 'optional' => true,
  530. 'implicit' => true
  531. ) + $OrganizationName,
  532. 'numeric-user-identifier' => array(
  533. 'constant' => 4,
  534. 'optional' => true,
  535. 'implicit' => true
  536. ) + $NumericUserIdentifier,
  537. 'personal-name' => array(
  538. 'constant' => 5,
  539. 'optional' => true,
  540. 'implicit' => true
  541. ) + $PersonalName,
  542. 'organizational-unit-names' => array(
  543. 'constant' => 6,
  544. 'optional' => true,
  545. 'implicit' => true
  546. ) + $OrganizationalUnitNames
  547. )
  548. );
  549. $ORAddress = array(
  550. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  551. 'children' => array(
  552. 'built-in-standard-attributes' => $BuiltInStandardAttributes,
  553. 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
  554. 'extension-attributes' => array('optional' => true) + $ExtensionAttributes
  555. )
  556. );
  557. $EDIPartyName = array(
  558. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  559. 'children' => array(
  560. 'nameAssigner' => array(
  561. 'constant' => 0,
  562. 'optional' => true,
  563. 'implicit' => true
  564. ) + $DirectoryString,
  565. // partyName is technically required but File_ASN1 doesn't currently support non-optional constants and
  566. // setting it to optional gets the job done in any event.
  567. 'partyName' => array(
  568. 'constant' => 1,
  569. 'optional' => true,
  570. 'implicit' => true
  571. ) + $DirectoryString
  572. )
  573. );
  574. $GeneralName = array(
  575. 'type' => FILE_ASN1_TYPE_CHOICE,
  576. 'children' => array(
  577. 'otherName' => array(
  578. 'constant' => 0,
  579. 'optional' => true,
  580. 'implicit' => true
  581. ) + $AnotherName,
  582. 'rfc822Name' => array(
  583. 'type' => FILE_ASN1_TYPE_IA5_STRING,
  584. 'constant' => 1,
  585. 'optional' => true,
  586. 'implicit' => true
  587. ),
  588. 'dNSName' => array(
  589. 'type' => FILE_ASN1_TYPE_IA5_STRING,
  590. 'constant' => 2,
  591. 'optional' => true,
  592. 'implicit' => true
  593. ),
  594. 'x400Address' => array(
  595. 'constant' => 3,
  596. 'optional' => true,
  597. 'implicit' => true
  598. ) + $ORAddress,
  599. 'directoryName' => array(
  600. 'constant' => 4,
  601. 'optional' => true,
  602. 'explicit' => true
  603. ) + $Name,
  604. 'ediPartyName' => array(
  605. 'constant' => 5,
  606. 'optional' => true,
  607. 'implicit' => true
  608. ) + $EDIPartyName,
  609. 'uniformResourceIdentifier' => array(
  610. 'type' => FILE_ASN1_TYPE_IA5_STRING,
  611. 'constant' => 6,
  612. 'optional' => true,
  613. 'implicit' => true
  614. ),
  615. 'iPAddress' => array(
  616. 'type' => FILE_ASN1_TYPE_OCTET_STRING,
  617. 'constant' => 7,
  618. 'optional' => true,
  619. 'implicit' => true
  620. ),
  621. 'registeredID' => array(
  622. 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER,
  623. 'constant' => 8,
  624. 'optional' => true,
  625. 'implicit' => true
  626. )
  627. )
  628. );
  629. $GeneralNames = array(
  630. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  631. 'min' => 1,
  632. 'max' => -1,
  633. 'children' => $GeneralName
  634. );
  635. $this->IssuerAltName = $GeneralNames;
  636. $ReasonFlags = array(
  637. 'type' => FILE_ASN1_TYPE_BIT_STRING,
  638. 'mapping' => array(
  639. 'unused',
  640. 'keyCompromise',
  641. 'cACompromise',
  642. 'affiliationChanged',
  643. 'superseded',
  644. 'cessationOfOperation',
  645. 'certificateHold',
  646. 'privilegeWithdrawn',
  647. 'aACompromise'
  648. )
  649. );
  650. $DistributionPointName = array(
  651. 'type' => FILE_ASN1_TYPE_CHOICE,
  652. 'children' => array(
  653. 'fullName' => array(
  654. 'constant' => 0,
  655. 'optional' => true,
  656. 'implicit' => true
  657. ) + $GeneralNames,
  658. 'nameRelativeToCRLIssuer' => array(
  659. 'constant' => 1,
  660. 'optional' => true,
  661. 'implicit' => true
  662. ) + $RelativeDistinguishedName
  663. )
  664. );
  665. $DistributionPoint = array(
  666. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  667. 'children' => array(
  668. 'distributionPoint' => array(
  669. 'constant' => 0,
  670. 'optional' => true,
  671. 'explicit' => true
  672. ) + $DistributionPointName,
  673. 'reasons' => array(
  674. 'constant' => 1,
  675. 'optional' => true,
  676. 'implicit' => true
  677. ) + $ReasonFlags,
  678. 'cRLIssuer' => array(
  679. 'constant' => 2,
  680. 'optional' => true,
  681. 'implicit' => true
  682. ) + $GeneralNames
  683. )
  684. );
  685. $this->CRLDistributionPoints = array(
  686. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  687. 'min' => 1,
  688. 'max' => -1,
  689. 'children' => $DistributionPoint
  690. );
  691. $this->AuthorityKeyIdentifier = array(
  692. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  693. 'children' => array(
  694. 'keyIdentifier' => array(
  695. 'constant' => 0,
  696. 'optional' => true,
  697. 'implicit' => true
  698. ) + $this->KeyIdentifier,
  699. 'authorityCertIssuer' => array(
  700. 'constant' => 1,
  701. 'optional' => true,
  702. 'implicit' => true
  703. ) + $GeneralNames,
  704. 'authorityCertSerialNumber' => array(
  705. 'constant' => 2,
  706. 'optional' => true,
  707. 'implicit' => true
  708. ) + $CertificateSerialNumber
  709. )
  710. );
  711. $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  712. $PolicyQualifierInfo = array(
  713. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  714. 'children' => array(
  715. 'policyQualifierId' => $PolicyQualifierId,
  716. 'qualifier' => array('type' => FILE_ASN1_TYPE_ANY)
  717. )
  718. );
  719. $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  720. $PolicyInformation = array(
  721. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  722. 'children' => array(
  723. 'policyIdentifier' => $CertPolicyId,
  724. 'policyQualifiers' => array(
  725. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  726. 'min' => 0,
  727. 'max' => -1,
  728. 'optional' => true,
  729. 'children' => $PolicyQualifierInfo
  730. )
  731. )
  732. );
  733. $this->CertificatePolicies = array(
  734. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  735. 'min' => 1,
  736. 'max' => -1,
  737. 'children' => $PolicyInformation
  738. );
  739. $this->PolicyMappings = array(
  740. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  741. 'min' => 1,
  742. 'max' => -1,
  743. 'children' => array(
  744. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  745. 'children' => array(
  746. 'issuerDomainPolicy' => $CertPolicyId,
  747. 'subjectDomainPolicy' => $CertPolicyId
  748. )
  749. )
  750. );
  751. $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  752. $this->ExtKeyUsageSyntax = array(
  753. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  754. 'min' => 1,
  755. 'max' => -1,
  756. 'children' => $KeyPurposeId
  757. );
  758. $AccessDescription = array(
  759. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  760. 'children' => array(
  761. 'accessMethod' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  762. 'accessLocation' => $GeneralName
  763. )
  764. );
  765. $this->AuthorityInfoAccessSyntax = array(
  766. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  767. 'min' => 1,
  768. 'max' => -1,
  769. 'children' => $AccessDescription
  770. );
  771. $this->SubjectAltName = $GeneralNames;
  772. $this->PrivateKeyUsagePeriod = array(
  773. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  774. 'children' => array(
  775. 'notBefore' => array(
  776. 'constant' => 0,
  777. 'optional' => true,
  778. 'implicit' => true,
  779. 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME),
  780. 'notAfter' => array(
  781. 'constant' => 1,
  782. 'optional' => true,
  783. 'implicit' => true,
  784. 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
  785. )
  786. );
  787. $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER);
  788. $GeneralSubtree = array(
  789. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  790. 'children' => array(
  791. 'base' => $GeneralName,
  792. 'minimum' => array(
  793. 'constant' => 0,
  794. 'optional' => true,
  795. 'implicit' => true,
  796. 'default' => new Math_BigInteger(0)
  797. ) + $BaseDistance,
  798. 'maximum' => array(
  799. 'constant' => 1,
  800. 'optional' => true,
  801. 'implicit' => true,
  802. ) + $BaseDistance
  803. )
  804. );
  805. $GeneralSubtrees = array(
  806. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  807. 'min' => 1,
  808. 'max' => -1,
  809. 'children' => $GeneralSubtree
  810. );
  811. $this->NameConstraints = array(
  812. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  813. 'children' => array(
  814. 'permittedSubtrees' => array(
  815. 'constant' => 0,
  816. 'optional' => true,
  817. 'implicit' => true
  818. ) + $GeneralSubtrees,
  819. 'excludedSubtrees' => array(
  820. 'constant' => 1,
  821. 'optional' => true,
  822. 'implicit' => true
  823. ) + $GeneralSubtrees
  824. )
  825. );
  826. $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING);
  827. $DisplayText = array(
  828. 'type' => FILE_ASN1_TYPE_CHOICE,
  829. 'children' => array(
  830. 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING),
  831. 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING),
  832. 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING),
  833. 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING)
  834. )
  835. );
  836. $NoticeReference = array(
  837. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  838. 'children' => array(
  839. 'organization' => $DisplayText,
  840. 'noticeNumbers' => array(
  841. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  842. 'min' => 1,
  843. 'max' => 200,
  844. 'children' => array('type' => FILE_ASN1_TYPE_INTEGER)
  845. )
  846. )
  847. );
  848. $this->UserNotice = array(
  849. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  850. 'children' => array(
  851. 'noticeRef' => array(
  852. 'optional' => true,
  853. 'implicit' => true
  854. ) + $NoticeReference,
  855. 'explicitText' => array(
  856. 'optional' => true,
  857. 'implicit' => true
  858. ) + $DisplayText
  859. )
  860. );
  861. // mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
  862. $this->netscape_cert_type = array(
  863. 'type' => FILE_ASN1_TYPE_BIT_STRING,
  864. 'mapping' => array(
  865. 'SSLClient',
  866. 'SSLServer',
  867. 'Email',
  868. 'ObjectSigning',
  869. 'Reserved',
  870. 'SSLCA',
  871. 'EmailCA',
  872. 'ObjectSigningCA'
  873. )
  874. );
  875. $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING);
  876. // attribute is used in RFC2986 but we're using the RFC5280 definition
  877. $Attribute = array(
  878. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  879. 'children' => array(
  880. 'type' => $AttributeType,
  881. 'value'=> array(
  882. 'type' => FILE_ASN1_TYPE_SET,
  883. 'min' => 1,
  884. 'max' => -1,
  885. 'children' => $AttributeValue
  886. )
  887. )
  888. );
  889. // adapted from <http://tools.ietf.org/html/rfc2986>
  890. $Attributes = array(
  891. 'type' => FILE_ASN1_TYPE_SET,
  892. 'min' => 1,
  893. 'max' => -1,
  894. 'children' => $Attribute
  895. );
  896. $CertificationRequestInfo = array(
  897. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  898. 'children' => array(
  899. 'version' => array(
  900. 'type' => FILE_ASN1_TYPE_INTEGER,
  901. 'mapping' => array('v1')
  902. ),
  903. 'subject' => $Name,
  904. 'subjectPKInfo' => $SubjectPublicKeyInfo,
  905. 'attributes' => array(
  906. 'constant' => 0,
  907. 'optional' => true,
  908. 'implicit' => true
  909. ) + $Attributes,
  910. )
  911. );
  912. $this->CertificationRequest = array(
  913. 'type' => FILE_ASN1_TYPE_SEQUENCE,
  914. 'children' => array(
  915. 'certificationRequestInfo' => $CertificationRequestInfo,
  916. 'signatureAlgorithm' => $AlgorithmIdentifier,
  917. 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  918. )
  919. );
  920. // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
  921. $this->oids = array(
  922. '1.3.6.1.5.5.7' => 'id-pkix',
  923. '1.3.6.1.5.5.7.1' => 'id-pe',
  924. '1.3.6.1.5.5.7.2' => 'id-qt',
  925. '1.3.6.1.5.5.7.3' => 'id-kp',
  926. '1.3.6.1.5.5.7.48' => 'id-ad',
  927. '1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
  928. '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
  929. '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
  930. '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
  931. '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
  932. '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
  933. '2.5.4' => 'id-at',
  934. '2.5.4.41' => 'id-at-name',
  935. '2.5.4.4' => 'id-at-surname',
  936. '2.5.4.42' => 'id-at-givenName',
  937. '2.5.4.43' => 'id-at-initials',
  938. '2.5.4.44' => 'id-at-generationQualifier',
  939. '2.5.4.3' => 'id-at-commonName',
  940. '2.5.4.7' => 'id-at-localityName',
  941. '2.5.4.8' => 'id-at-stateOrProvinceName',
  942. '2.5.4.10' => 'id-at-organizationName',
  943. '2.5.4.11' => 'id-at-organizationalUnitName',
  944. '2.5.4.12' => 'id-at-title',
  945. '2.5.4.46' => 'id-at-dnQualifier',
  946. '2.5.4.6' => 'id-at-countryName',
  947. '2.5.4.5' => 'id-at-serialNumber',
  948. '2.5.4.65' => 'id-at-pseudonym',
  949. '2.5.4.17' => 'id-at-postalCode',
  950. '2.5.4.9' => 'id-at-streetAddress',
  951. '0.9.2342.19200300.100.1.25' => 'id-domainComponent',
  952. '1.2.840.113549.1.9' => 'pkcs-9',
  953. '1.2.840.113549.1.9.1' => 'id-emailAddress',
  954. '2.5.29' => 'id-ce',
  955. '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
  956. '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
  957. '2.5.29.15' => 'id-ce-keyUsage',
  958. '2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
  959. '2.5.29.32' => 'id-ce-certificatePolicies',
  960. '2.5.29.32.0' => 'anyPolicy',
  961. '2.5.29.33' => 'id-ce-policyMappings',
  962. '2.5.29.17' => 'id-ce-subjectAltName',
  963. '2.5.29.18' => 'id-ce-issuerAltName',
  964. '2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
  965. '2.5.29.19' => 'id-ce-basicConstraints',
  966. '2.5.29.30' => 'id-ce-nameConstraints',
  967. '2.5.29.36' => 'id-ce-policyConstraints',
  968. '2.5.29.31' => 'id-ce-cRLDistributionPoints',
  969. '2.5.29.37' => 'id-ce-extKeyUsage',
  970. '2.5.29.37.0' => 'anyExtendedKeyUsage',
  971. '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
  972. '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
  973. '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
  974. '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
  975. '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
  976. '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
  977. '2.5.29.54' => 'id-ce-inhibitAnyPolicy',
  978. '2.5.29.46' => 'id-ce-freshestCRL',
  979. '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
  980. '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
  981. '2.5.29.20' => 'id-ce-cRLNumber',
  982. '2.5.29.28' => 'id-ce-issuingDistributionPoint',
  983. '2.5.29.27' => 'id-ce-deltaCRLIndicator',
  984. '2.5.29.21' => 'id-ce-cRLReasons',
  985. '2.5.29.29' => 'id-ce-certificateIssuer',
  986. '2.5.29.23' => 'id-ce-holdInstructionCode',
  987. '2.2.840.10040.2' => 'holdInstruction',
  988. '2.2.840.10040.2.1' => 'id-holdinstruction-none',
  989. '2.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
  990. '2.2.840.10040.2.3' => 'id-holdinstruction-reject',
  991. '2.5.29.24' => 'id-ce-invalidityDate',
  992. '1.2.840.113549.2.2' => 'md2',
  993. '1.2.840.113549.2.5' => 'md5',
  994. '1.3.14.3.2.26' => 'id-sha1',
  995. '1.2.840.10040.4.1' => 'id-dsa',
  996. '1.2.840.10040.4.3' => 'id-dsa-with-sha1',
  997. '1.2.840.113549.1.1' => 'pkcs-1',
  998. '1.2.840.113549.1.1.1' => 'rsaEncryption',
  999. '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
  1000. '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
  1001. '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
  1002. '1.2.840.10046.2.1' => 'dhpublicnumber',
  1003. '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
  1004. '1.2.840.10045' => 'ansi-X9-62',
  1005. '1.2.840.10045.4' => 'id-ecSigType',
  1006. '1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
  1007. '1.2.840.10045.1' => 'id-fieldType',
  1008. '1.2.840.10045.1.1' => 'prime-field',
  1009. '1.2.840.10045.1.2' => 'characteristic-two-field',
  1010. '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
  1011. '1.2.840.10045.1.2.3.1' => 'gnBasis',
  1012. '1.2.840.10045.1.2.3.2' => 'tpBasis',
  1013. '1.2.840.10045.1.2.3.3' => 'ppBasis',
  1014. '1.2.840.10045.2' => 'id-publicKeyType',
  1015. '1.2.840.10045.2.1' => 'id-ecPublicKey',
  1016. '1.2.840.10045.3' => 'ellipticCurve',
  1017. '1.2.840.10045.3.0' => 'c-TwoCurve',
  1018. '1.2.840.10045.3.0.1' => 'c2pnb163v1',
  1019. '1.2.840.10045.3.0.2' => 'c2pnb163v2',
  1020. '1.2.840.10045.3.0.3' => 'c2pnb163v3',
  1021. '1.2.840.10045.3.0.4' => 'c2pnb176w1',
  1022. '1.2.840.10045.3.0.5' => 'c2pnb191v1',
  1023. '1.2.840.10045.3.0.6' => 'c2pnb191v2',
  1024. '1.2.840.10045.3.0.7' => 'c2pnb191v3',
  1025. '1.2.840.10045.3.0.8' => 'c2pnb191v4',
  1026. '1.2.840.10045.3.0.9' => 'c2pnb191v5',
  1027. '1.2.840.10045.3.0.10' => 'c2pnb208w1',
  1028. '1.2.840.10045.3.0.11' => 'c2pnb239v1',
  1029. '1.2.840.10045.3.0.12' => 'c2pnb239v2',
  1030. '1.2.840.10045.3.0.13' => 'c2pnb239v3',
  1031. '1.2.840.10045.3.0.14' => 'c2pnb239v4',
  1032. '1.2.840.10045.3.0.15' => 'c2pnb239v5',
  1033. '1.2.840.10045.3.0.16' => 'c2pnb272w1',
  1034. '1.2.840.10045.3.0.17' => 'c2pnb304w1',
  1035. '1.2.840.10045.3.0.18' => 'c2pnb359v1',
  1036. '1.2.840.10045.3.0.19' => 'c2pnb368w1',
  1037. '1.2.840.10045.3.0.20' => 'c2pnb431r1',
  1038. '1.2.840.10045.3.1' => 'primeCurve',
  1039. '1.2.840.10045.3.1.1' => 'prime192v1',
  1040. '1.2.840.10045.3.1.2' => 'prime192v2',
  1041. '1.2.840.10045.3.1.3' => 'prime192v3',
  1042. '1.2.840.10045.3.1.4' => 'prime239v1',
  1043. '1.2.840.10045.3.1.5' => 'prime239v2',
  1044. '1.2.840.10045.3.1.6' => 'prime239v3',
  1045. '1.2.840.10045.3.1.7' => 'prime256v1',
  1046. '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
  1047. '1.2.840.113549.1.1.9' => 'id-pSpecified',
  1048. '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
  1049. '1.2.840.113549.1.1.8' => 'id-mgf1',
  1050. '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
  1051. '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
  1052. '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
  1053. '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
  1054. '2.16.840.1.101.3.4.2.4' => 'id-sha224',
  1055. '2.16.840.1.101.3.4.2.1' => 'id-sha256',
  1056. '2.16.840.1.101.3.4.2.2' => 'id-sha384',
  1057. '2.16.840.1.101.3.4.2.3' => 'id-sha512',
  1058. '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
  1059. '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
  1060. '1.2.643.2.2.20' => 'id-GostR3410-2001',
  1061. '1.2.643.2.2.19' => 'id-GostR3410-94',
  1062. // Netscape Object Identifiers from "Netscape Certificate Extensions"
  1063. '2.16.840.1.113730' => 'netscape',
  1064. '2.16.840.1.113730.1' => 'netscape-cert-extension',
  1065. '2.16.840.1.113730.1.1' => 'netscape-cert-type',
  1066. '2.16.840.1.113730.1.13' => 'netscape-comment',
  1067. // the following are X.509 extensions not supported by phpseclib
  1068. '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
  1069. '1.2.840.113533.7.65.0' => 'entrustVersInfo',
  1070. '2.16.840.1.113733.1.6.9' => 'verisignPrivate',
  1071. // for Certificate Signing Requests
  1072. // see http://tools.ietf.org/html/rfc2985
  1073. '1.2.840.113549.1.9.2' => 'unstructuredName', // PKCS #9 unstructured name
  1074. '1.2.840.113549.1.9.7' => 'challengePassword' // Challenge password for certificate revocations
  1075. );
  1076. }
  1077. /**
  1078. * Load X.509 certificate
  1079. *
  1080. * Returns an associative array describing the X.509 cert or a false if the cert failed to load
  1081. *
  1082. * @param String $cert
  1083. * @access public
  1084. * @return Mixed
  1085. */
  1086. function loadX509($cert)
  1087. {
  1088. if (is_array($cert) && isset($cert['tbsCertificate'])) {
  1089. $this->currentCert = $cert;
  1090. unset($this->signatureSubject);
  1091. return false;
  1092. }
  1093. $asn1 = new File_ASN1();
  1094. /*
  1095. X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie.
  1096. some may have the following preceeding the -----BEGIN CERTIFICATE----- line:
  1097. subject=/O=organization/OU=org unit/CN=common name
  1098. issuer=/O=organization/CN=common name
  1099. */
  1100. $cert = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]#', '', $cert);
  1101. $cert = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $cert) ? base64_decode($cert) : false;
  1102. if ($cert === false) {
  1103. $this->currentCert = false;
  1104. return false;
  1105. }
  1106. $asn1->loadOIDs($this->oids);
  1107. $decoded = $asn1->decodeBER($cert);
  1108. $x509 = $asn1->asn1map($decoded[0], $this->Certificate);
  1109. if (!isset($x509) || $x509 === false) {
  1110. $this->currentCert = false;
  1111. return false;
  1112. }
  1113. $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
  1114. if (isset($x509['tbsCertificate']['extensions'])) {
  1115. for ($i = 0; $i < count($x509['tbsCertificate']['extensions']); $i++) {
  1116. $id = $x509['tbsCertificate']['extensions'][$i]['extnId'];
  1117. $value = &$x509['tbsCertificate']['extensions'][$i]['extnValue'];
  1118. $value = base64_decode($value);
  1119. $decoded = $asn1->decodeBER($value);
  1120. /* [extnValue] contains the DER encoding of an ASN.1 value
  1121. corresponding to the extension type identified by extnID */
  1122. $map = $this->_getMapping($id);
  1123. if (!is_bool($map)) {
  1124. $mapped = $asn1->asn1map($decoded[0], $map);
  1125. $value = $mapped === false ? $decoded[0] : $mapped;
  1126. if ($id == 'id-ce-certificatePolicies') {
  1127. for ($j = 0; $j < count($value); $j++) {
  1128. if (!isset($value[$j]['policyQualifiers'])) {
  1129. continue;
  1130. }
  1131. for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
  1132. $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
  1133. $map = $this->_getMapping($subid);
  1134. $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
  1135. if ($map !== false) {
  1136. $decoded = $asn1->decodeBER($subvalue);
  1137. $mapped = $asn1->asn1map($decoded[0], $map);
  1138. $subvalue = $mapped === false ? $decoded[0] : $mapped;
  1139. }
  1140. }
  1141. }
  1142. }
  1143. } elseif ($map) {
  1144. $value = base64_encode($value);
  1145. }
  1146. }
  1147. }
  1148. $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
  1149. $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
  1150. $this->currentCert = $x509;
  1151. $this->dn = $x509['tbsCertificate']['subject'];
  1152. return $x509;
  1153. }
  1154. /**
  1155. * Save X.509 certificate
  1156. *
  1157. * @param Array $cert
  1158. * @access public
  1159. * @return String
  1160. */
  1161. function saveX509($cert)
  1162. {
  1163. if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
  1164. return false;
  1165. }
  1166. switch ($cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm']) {
  1167. case 'rsaEncryption':
  1168. $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] =
  1169. base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
  1170. }
  1171. $asn1 = new File_ASN1();
  1172. $asn1->loadOIDs($this->oids);
  1173. $filters = array();
  1174. $filters['tbsCertificate']['signature']['parameters'] =
  1175. $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] =
  1176. $filters['tbsCertificate']['issuer']['rdnSequence']['value'] =
  1177. $filters['tbsCertificate']['subject']['rdnSequence']['value'] =
  1178. $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] =
  1179. $filters['signatureAlgorithm']['parameters'] =
  1180. $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] =
  1181. //$filters['policyQualifiers']['qualifier'] =
  1182. $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] =
  1183. $filters['directoryName']['rdnSequence']['value'] =
  1184. array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  1185. /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING.
  1186. FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
  1187. characters.
  1188. */
  1189. $filters['policyQualifiers']['qualifier'] =
  1190. array('type' => FILE_ASN1_TYPE_IA5_STRING);
  1191. $asn1->loadFilters($filters);
  1192. if (isset($cert['tbsCertificate']['extensions'])) {
  1193. $size = count($cert['tbsCertificate']['extensions']);
  1194. for ($i = 0; $i < $size; $i++) {
  1195. $id = $cert['tbsCertificate']['extensions'][$i]['extnId'];
  1196. $value = &$cert['tbsCertificate']['extensions'][$i]['extnValue'];
  1197. switch ($id) {
  1198. case 'id-ce-certificatePolicies':
  1199. for ($j = 0; $j < count($value); $j++) {
  1200. if (!isset($value[$j]['policyQualifiers'])) {
  1201. continue;
  1202. }
  1203. for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
  1204. $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
  1205. $map = $this->_getMapping($subid);
  1206. $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
  1207. if ($map !== false) {
  1208. // by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's
  1209. // actual type is FILE_ASN1_TYPE_ANY
  1210. $subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue, $map));
  1211. }
  1212. }
  1213. }
  1214. break;
  1215. case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string
  1216. if (isset($value['authorityCertSerialNumber'])) {
  1217. if ($value['authorityCertSerialNumber']->toBytes() == '') {
  1218. $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0";
  1219. $value['authorityCertSerialNumber'] = new File_ASN1_Element($temp);
  1220. }
  1221. }
  1222. }
  1223. /* [extnValue] contains the DER encoding of an ASN.1 value
  1224. corresponding to the extension type identified by extnID */
  1225. $map = $this->_getMapping($id);
  1226. if (is_bool($map)) {
  1227. if (!$map) {
  1228. user_error($id . ' is not a currently supported extension', E_USER_NOTICE);
  1229. unset($cert['tbsCertificate']['extensions'][$i]);
  1230. }
  1231. } else {
  1232. $temp = $asn1->encodeDER($value, $map);
  1233. $value = base64_encode($temp);
  1234. }
  1235. }
  1236. }
  1237. $cert = $asn1->encodeDER($cert, $this->Certificate);
  1238. return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert)) . '-----END CERTIFICATE-----';
  1239. }
  1240. /**
  1241. * Associate an extension ID to an extension mapping
  1242. *
  1243. * @param String $extnId
  1244. * @access private
  1245. * @return Mixed
  1246. */
  1247. function _getMapping($extnId)
  1248. {
  1249. switch ($extnId) {
  1250. case 'id-ce-keyUsage':
  1251. return $this->KeyUsage;
  1252. case 'id-ce-basicConstraints':
  1253. return $this->BasicConstraints;
  1254. case 'id-ce-subjectKeyIdentifier':
  1255. return $this->KeyIdentifier;
  1256. case 'id-ce-cRLDistributionPoints':
  1257. return $this->CRLDistributionPoints;
  1258. case 'id-ce-authorityKeyIdentifier':
  1259. return $this->AuthorityKeyIdentifier;
  1260. case 'id-ce-certificatePolicies':
  1261. return $this->CertificatePolicies;
  1262. case 'id-ce-extKeyUsage':
  1263. return $this->ExtKeyUsageSyntax;
  1264. case 'id-pe-authorityInfoAccess':
  1265. return $this->AuthorityInfoAccessSyntax;
  1266. case 'id-ce-subjectAltName':
  1267. return $this->SubjectAltName;
  1268. case 'id-ce-privateKeyUsagePeriod':
  1269. return $this->PrivateKeyUsagePeriod;
  1270. case 'id-ce-issuerAltName':
  1271. return $this->IssuerAltName;
  1272. case 'id-ce-policyMappings':
  1273. return $this->PolicyMappings;
  1274. case 'id-ce-nameConstraints':
  1275. return $this->NameConstraints;
  1276. case 'netscape-cert-type':
  1277. return $this->netscape_cert_type;
  1278. case 'netscape-comment':
  1279. return $this->netscape_comment;
  1280. // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
  1281. // back around to asn1map() and we don't want it decoded again.
  1282. //case 'id-qt-cps':
  1283. // return $this->CPSuri;
  1284. case 'id-qt-unotice':
  1285. return $this->UserNotice;
  1286. // the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
  1287. case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt
  1288. case 'entrustVersInfo':
  1289. // http://support.microsoft.com/kb/287547
  1290. case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION
  1291. case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION
  1292. // "SET Secure Electronic Transaction Specification"
  1293. // http://www.maithean.com/docs/set_bk3.pdf
  1294. case '2.23.42.7.0': // id-set-hashedRootKey
  1295. return true;
  1296. }
  1297. return false;
  1298. }
  1299. /**
  1300. * Load an X.509 certificate as a certificate authority
  1301. *
  1302. * @param String $cert
  1303. * @access public
  1304. * @return Boolean
  1305. */
  1306. function loadCA($cert)
  1307. {
  1308. $cert = $this->loadX509($cert);
  1309. if (!$cert) {
  1310. return false;
  1311. }
  1312. /* From RFC5280 "PKIX Certificate and CRL Profile":
  1313. If the keyUsage extension is present, then the subject public key
  1314. MUST NOT be used to verify signatures on certificates or CRLs unless
  1315. the corresponding keyCertSign or cRLSign bit is set. */
  1316. //$keyUsage = $this->getExtension('id-ce-keyUsage');
  1317. //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
  1318. // return false;
  1319. //}
  1320. /* From RFC5280 "PKIX Certificate and CRL Profile":
  1321. The cA boolean indicates whether the certified public key may be used
  1322. to verify certificate signatures. If the cA boolean is not asserted,
  1323. then the keyCertSign bit in the key usage extension MUST NOT be
  1324. asserted. If the basic constraints extension is not present in a
  1325. version 3 certificate, or the extension is present but the cA boolean
  1326. is not asserted, then the certified public key MUST NOT be used to
  1327. verify certificate signatures. */
  1328. //$basicConstraints = $this->getExtension('id-ce-basicConstraints');
  1329. //if (!$basicConstraints || !$basicConstraints['cA']) {
  1330. // return false;
  1331. //}
  1332. $this->CAs[] = $cert;
  1333. unset($this->currentCert);
  1334. unset($this->signatureSubject);
  1335. return true;
  1336. }
  1337. /**
  1338. * Validate an X.509 certificate against a URL
  1339. *
  1340. * From RFC2818 "HTTP over TLS":
  1341. *
  1342. * Matching is performed using the matching rules specified by
  1343. * [RFC2459]. If more than one identity of a given type is present in
  1344. * the certificate (e.g., more than one dNSName name, a match in any one
  1345. * of the set is considered acceptable.) Names may contain the wildcard
  1346. * character * which is considered to match any single domain name
  1347. * component or component fragment. E.g., *.a.com matches foo.a.com but
  1348. * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
  1349. *
  1350. * @param String $url
  1351. * @access public
  1352. * @return Boolean
  1353. */
  1354. function validateURL($url)
  1355. {
  1356. if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  1357. return false;
  1358. }
  1359. $components = parse_url($url);
  1360. if (!isset($components['host'])) {
  1361. return false;
  1362. }
  1363. if ($names = $this->getExtension('id-ce-subjectAltName')) {
  1364. foreach ($names as $key => $value) {
  1365. $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
  1366. switch ($key) {
  1367. case 'dNSName':
  1368. /* From RFC2818 "HTTP over TLS":
  1369. If a subjectAltName extension of type dNSName is present, that MUST
  1370. be used as the identity. Otherwise, the (most specific) Common Name
  1371. field in the Subject field of the certificate MUST be used. Although
  1372. the use of the Common Name is existing practice, it is deprecated and
  1373. Certification Authorities are encouraged to use the dNSName instead. */
  1374. if (preg_match('#^' . $value . '$#', $components['host'])) {
  1375. return true;
  1376. }
  1377. break;
  1378. case 'iPAddress':
  1379. /* From RFC2818 "HTTP over TLS":
  1380. In some cases, the URI is specified as an IP address rather than a
  1381. hostname. In this case, the iPAddress subjectAltName must be present
  1382. in the certificate and must exactly match the IP in the URI. */
  1383. if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
  1384. return true;
  1385. }
  1386. }
  1387. }
  1388. return false;
  1389. }
  1390. if ($value = $this->getDNProp('id-at-commonName')) {
  1391. $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
  1392. return preg_match('#^' . $value . '$#', $components['host']);
  1393. }
  1394. return false;
  1395. }
  1396. /**
  1397. * Validate a date
  1398. *
  1399. * If $date isn't defined it is assumed to be the current date.
  1400. *
  1401. * @param Integer $date optional
  1402. * @access public
  1403. */
  1404. function validateDate($date = NULL)
  1405. {
  1406. if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  1407. return false;
  1408. }
  1409. if (!isset($date)) {
  1410. $date = time();
  1411. }
  1412. switch (true) {
  1413. case time() < @strtotime($this->currentCert['tbsCertificate']['validity']['notBefore']):
  1414. case time() > @strtotime($this->currentCert['tbsCertificate']['validity']['notAfter']):
  1415. return false;
  1416. }
  1417. return true;
  1418. }
  1419. /**
  1420. * Validate a signature
  1421. *
  1422. * Works on both X.509 certs and CSR's.
  1423. * Returns 1 if the signature is verified, 0 if it is not correct or -1 on error
  1424. *
  1425. * To know if a signature is valid one should do validateSignature() === 1
  1426. *
  1427. * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
  1428. *
  1429. * @param Integer $options optional
  1430. * @access public
  1431. * @return Mixed
  1432. */
  1433. function validateSignature($options = 0)
  1434. {
  1435. if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
  1436. return 0;
  1437. }
  1438. /* TODO:
  1439. "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
  1440. -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
  1441. implement pathLenConstraint in the id-ce-basicConstraints extension */
  1442. switch (true) {
  1443. case isset($this->currentCert['tbsCertificate']):
  1444. // self-signed cert
  1445. if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) {
  1446. $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
  1447. $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
  1448. switch (true) {
  1449. case !is_array($authorityKey):
  1450. case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
  1451. $signingCert = $this->currentCert; // working cert
  1452. }
  1453. }
  1454. if (!empty($this->CAs)) {
  1455. for ($i = 0; $i < count($this->CAs); $i++) {
  1456. // even if the cert is a self-signed one we still want to see if it's a CA;
  1457. // if not, we'll conditionally return an error
  1458. $ca = $this->CAs[$i];
  1459. if ($this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
  1460. $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
  1461. $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
  1462. switch (true) {
  1463. case !is_array($authorityKey):
  1464. case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
  1465. $signingCert = $ca; // working cert
  1466. break 2;
  1467. }
  1468. }
  1469. }
  1470. if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) {
  1471. return 0;
  1472. }
  1473. } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) {
  1474. return 0;
  1475. }
  1476. return $this->_validateSignature(
  1477. $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
  1478. $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
  1479. $this->currentCert['signatureAlgorithm']['algorithm'],
  1480. substr(base64_decode($this->currentCert['signature']), 1),
  1481. $this->signatureSubject
  1482. );
  1483. case isset($this->currentCert['certificationRequestInfo']):
  1484. return $this->_validateSignature(
  1485. $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
  1486. $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
  1487. $this->currentCert['signatureAlgorithm']['algorithm'],
  1488. substr(base64_decode($this->currentCert['signature']), 1),
  1489. $this->signatureSubject
  1490. );
  1491. default:
  1492. return 0;
  1493. }
  1494. }
  1495. /**
  1496. * Validates a signature
  1497. *
  1498. * Returns 1 if the signature is verified, 0 if it is not correct or -1 on error
  1499. *
  1500. * @param String $publicKeyAlgorithm
  1501. * @param String $publicKey
  1502. * @param String $signatureAlgorithm
  1503. * @param String $signature
  1504. * @param String $signatureSubject
  1505. * @access private
  1506. * @return Integer
  1507. */
  1508. function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
  1509. {
  1510. switch ($publicKeyAlgorithm) {
  1511. case 'rsaEncryption':
  1512. if (!class_exists('Crypt_RSA')) {
  1513. require_once('Crypt/RSA.php');
  1514. }
  1515. $rsa = new Crypt_RSA();
  1516. $rsa->loadKey($publicKey);
  1517. switch ($signatureAlgorithm) {
  1518. case 'md2WithRSAEncryption':
  1519. case 'md5WithRSAEncryption':
  1520. case 'sha1WithRSAEncryption':
  1521. case 'sha224WithRSAEncryption':
  1522. case 'sha256WithRSAEncryption':
  1523. case 'sha384WithRSAEncryption':
  1524. case 'sha512WithRSAEncryption':
  1525. $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
  1526. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  1527. if (!@$rsa->verify($signatureSubject, $signature)) {
  1528. return 0;
  1529. }
  1530. break;
  1531. default:
  1532. return -1;
  1533. }
  1534. break;
  1535. default:
  1536. return -1;
  1537. }
  1538. return 1;
  1539. }
  1540. /**
  1541. * Reformat public keys
  1542. *
  1543. * Reformats a public key to a format supported by phpseclib (if applicable)
  1544. *
  1545. * @param String $algorithm
  1546. * @param String $key
  1547. * @access private
  1548. * @return String
  1549. */
  1550. function _reformatKey($algorithm, $key)
  1551. {
  1552. switch ($algorithm) {
  1553. case 'rsaEncryption':
  1554. return
  1555. "-----BEGIN PUBLIC KEY-----\r\n" .
  1556. // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits
  1557. // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox
  1558. // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
  1559. chunk_split(base64_encode(substr(base64_decode($key), 1))) .
  1560. '-----END PUBLIC KEY-----';
  1561. default:
  1562. return $key;
  1563. }
  1564. }
  1565. /**
  1566. * Set a Distinguished Name property
  1567. *
  1568. * @param String $propName
  1569. * @param String $propValue
  1570. * @access public
  1571. * @return Boolean
  1572. */
  1573. function setDNProp($propName, $propValue)
  1574. {
  1575. if (empty($this->dn)) {
  1576. $this->dn = array('rdnSequence' => array());
  1577. }
  1578. switch (strtolower($propName)) {
  1579. case 'id-at-countryname':
  1580. case 'countryname':
  1581. case 'c':
  1582. $type = 'id-at-countryName';
  1583. break;
  1584. case 'id-at-organizationname':
  1585. case 'organizationname':
  1586. case 'o':
  1587. $type = 'id-at-organizationName';
  1588. break;
  1589. case 'id-at-dnqualifier':
  1590. case 'dnqualifier':
  1591. case 'ou':
  1592. $type = 'id-at-dnQualifier';
  1593. break;
  1594. case 'id-at-commonname':
  1595. case 'commonname':
  1596. case 'cn':
  1597. $type = 'id-at-commonName';
  1598. break;
  1599. case 'id-at-stateorprovinceName':
  1600. case 'stateorprovincename':
  1601. case 'state':
  1602. case 'province':
  1603. case 'provincename':
  1604. case 'st':
  1605. $type = 'id-at-stateOrProvinceName';
  1606. break;
  1607. case 'id-at-localityname':
  1608. case 'localityname':
  1609. case 'l':
  1610. $type = 'id-at-localityName';
  1611. break;
  1612. case 'id-emailaddress':
  1613. case 'emailaddress':
  1614. $type = 'id-at-emailAddress';
  1615. break;
  1616. case 'id-at-serialnumber':
  1617. case 'serialnumber':
  1618. $type = 'id-at-serialNumber';
  1619. break;
  1620. case 'id-at-postalcode':
  1621. case 'postalcode':
  1622. $type = 'id-at-postalCode';
  1623. break;
  1624. case 'id-at-streetaddress':
  1625. case 'streetaddress':
  1626. $type = 'id-at-streetAddress';
  1627. break;
  1628. case 'id-at-name':
  1629. case 'name':
  1630. $type = 'id-at-name';
  1631. case 'id-at-givenname':
  1632. case 'givenname':
  1633. $type = 'id-at-givenName';
  1634. break;
  1635. case 'id-at-surname':
  1636. case 'surname':
  1637. $type = 'id-at-surname';
  1638. break;
  1639. case 'id-at-initials':
  1640. case 'initials':
  1641. $type = 'id-at-initials';
  1642. break;
  1643. case 'id-at-generationqualifier':
  1644. case 'generationqualifier':
  1645. $type = 'id-at-generationQualifier';
  1646. break;
  1647. case 'id-at-organizationalunitname':
  1648. case 'organizationalunitname':
  1649. $type = 'id-at-organizationalUnitName';
  1650. break;
  1651. case 'id-at-pseudonym':
  1652. case 'pseudonym':
  1653. $type = 'id-at-pseudonym';
  1654. break;
  1655. default:
  1656. return false;
  1657. }
  1658. $this->dn['rdnSequence'][] = array(
  1659. array(
  1660. 'type' => $type,
  1661. 'value'=> $propValue
  1662. )
  1663. );
  1664. return true;
  1665. }
  1666. /**
  1667. * Remove Distinguished Name properties
  1668. *
  1669. * @param String $propName
  1670. * @access public
  1671. */
  1672. function removeDNProp($propName)
  1673. {
  1674. if (empty($this->dn)) {
  1675. return;
  1676. }
  1677. $dn = &$this->dn['rdnSequence'];
  1678. $size = count($dn);
  1679. for ($i = 0; $i < $size; $i++) {
  1680. if ($dn[$i][0]['type'] == $propName) {
  1681. unset($dn[$i]);
  1682. }
  1683. }
  1684. $dn = array_values($dn);
  1685. }
  1686. /**
  1687. * Get Distinguished Name properties
  1688. *
  1689. * @param String $propName
  1690. * @return Mixed
  1691. * @access public
  1692. */
  1693. function getDNProp($propName)
  1694. {
  1695. if (empty($this->dn)) {
  1696. return false;
  1697. }
  1698. $dn = $this->dn['rdnSequence'];
  1699. $result = array();
  1700. for ($i = 0; $i < $size; $i++) {
  1701. if ($dn[$i][0]['type'] == $propName) {
  1702. $result[] = $propName;
  1703. }
  1704. }
  1705. return $result;
  1706. }
  1707. /**
  1708. * Set a Distinguished Name
  1709. *
  1710. * @param Mixed $dn
  1711. * @access public
  1712. * @return Boolean
  1713. */
  1714. function setDN($dn)
  1715. {
  1716. if (is_array($dn)) {
  1717. if (isset($dn['rdnSequence'])) {
  1718. $this->dn = $dn;
  1719. return true;
  1720. }
  1721. // handles stuff generated by openssl_x509_parse()
  1722. foreach ($dn as $type => $value) {
  1723. if (!$this->setDNProp($type, $value)) {
  1724. return false;
  1725. }
  1726. }
  1727. return true;
  1728. }
  1729. // handles everything else
  1730. $results = preg_split('#((?:^|, |/)(?:C=|O=|OU=|CN=|L=|ST=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
  1731. for ($i = 1; $i < count($results); $i+=2) {
  1732. $type = trim($results[$i], ', =/');
  1733. $value = $results[$i + 1];
  1734. if (!$this->setDNProp($type, $value)) {
  1735. return false;
  1736. }
  1737. }
  1738. return true;
  1739. }
  1740. /**
  1741. * Get the Distinguished Name for a certificates subject
  1742. *
  1743. * @param Boolean $string optional
  1744. * @access public
  1745. * @return Boolean
  1746. */
  1747. function getDN($string = false, $dn = NULL)
  1748. {
  1749. if (!isset($dn)) {
  1750. $dn = $this->dn;
  1751. }
  1752. if (!$string) {
  1753. return $dn;
  1754. }
  1755. $start = true;
  1756. foreach ($dn['rdnSequence'] as $field) {
  1757. $type = $field[0]['type'];
  1758. $value = $field[0]['value'];
  1759. $delim = ', ';
  1760. switch ($type) {
  1761. case 'id-at-countryName':
  1762. $desc = 'C=';
  1763. break;
  1764. case 'id-at-stateOrProvinceName':
  1765. $desc = 'ST=';
  1766. break;
  1767. case 'id-at-organizationName':
  1768. $desc = 'O=';
  1769. break;
  1770. case 'id-at-dnQualifier':
  1771. $desc = 'OU=';
  1772. break;
  1773. case 'id-at-commonName':
  1774. $desc = 'CN=';
  1775. break;
  1776. case 'id-at-localityName':
  1777. $desc = 'L=';
  1778. break;
  1779. default:
  1780. $delim = '/';
  1781. $desc = preg_replace('#.+-([^-]+)$#', '$1', $type) . '=';
  1782. }
  1783. if (!$start) {
  1784. $output.= $delim;
  1785. }
  1786. $output.= $desc . $value;
  1787. $start = false;
  1788. }
  1789. return $output;
  1790. }
  1791. /**
  1792. * Get the Distinguished Name for a certificates issuer
  1793. *
  1794. * @param Boolean $string optional
  1795. * @access public
  1796. * @return Boolean
  1797. */
  1798. function getIssuerDN($string = false)
  1799. {
  1800. if (!isset($this->currentCert) || !is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  1801. return false;
  1802. }
  1803. return $this->getDN($string, $this->currentCert['tbsCertificate']['issuer']);
  1804. }
  1805. /**
  1806. * Set public key
  1807. *
  1808. * Key needs to be a Crypt_RSA object
  1809. *
  1810. * @param Object $key
  1811. * @access public
  1812. * @return Boolean
  1813. */
  1814. function setPublicKey($key)
  1815. {
  1816. $this->publicKey = $key;
  1817. }
  1818. /**
  1819. * Set private key
  1820. *
  1821. * Key needs to be a Crypt_RSA object
  1822. *
  1823. * @param Object $key
  1824. * @access public
  1825. */
  1826. function setPrivateKey($key)
  1827. {
  1828. $this->privateKey = $key;
  1829. }
  1830. /**
  1831. * Gets the public key
  1832. *
  1833. * Returns a Crypt_RSA object or a false.
  1834. *
  1835. * @access public
  1836. * @return Mixed
  1837. */
  1838. function getPublicKey()
  1839. {
  1840. if (!isset($this->currentCert) || !is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  1841. return false;
  1842. }
  1843. $key = $this->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
  1844. switch ($this->currentCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm']) {
  1845. case 'rsaEncryption':
  1846. if (!class_exists('Crypt_RSA')) {
  1847. require_once('Crypt/RSA.php');
  1848. }
  1849. $publicKey = new Crypt_RSA();
  1850. $publicKey->loadKey($key);
  1851. $publicKey->setPublicKey();
  1852. break;
  1853. default:
  1854. return false;
  1855. }
  1856. return $publicKey;
  1857. }
  1858. /**
  1859. * Load a Certificate Signing Request
  1860. *
  1861. * @param String $csr
  1862. * @access public
  1863. * @return Mixed
  1864. */
  1865. function loadCSR($csr)
  1866. {
  1867. // see http://tools.ietf.org/html/rfc2986
  1868. $asn1 = new File_ASN1();
  1869. $csr = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]#', '', $csr);
  1870. $orig = $csr = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $csr) ? base64_decode($csr) : false;
  1871. if ($csr === false) {
  1872. $this->currentCert = false;
  1873. return false;
  1874. }
  1875. $asn1->loadOIDs($this->oids);
  1876. $decoded = $asn1->decodeBER($csr);
  1877. $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
  1878. if (!isset($csr) || $csr === false) {
  1879. $this->currentCert = false;
  1880. return false;
  1881. }
  1882. $this->dn = $csr['certificationRequestInfo']['subject'];
  1883. $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
  1884. $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
  1885. $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
  1886. $key = $this->_reformatKey($algorithm, $key);
  1887. switch ($algorithm) {
  1888. case 'rsaEncryption':
  1889. if (!class_exists('Crypt_RSA')) {
  1890. require_once('Crypt/RSA.php');
  1891. }
  1892. $this->publicKey = new Crypt_RSA();
  1893. $this->publicKey->loadKey($key);
  1894. $this->publicKey->setPublicKey();
  1895. break;
  1896. default:
  1897. $this->publicKey = NULL;
  1898. }
  1899. $this->currentCert = $csr;
  1900. return $csr;
  1901. }
  1902. /**
  1903. * Save CSR request
  1904. *
  1905. * @param Array $csr
  1906. * @access public
  1907. * @return String
  1908. */
  1909. function saveCSR($csr)
  1910. {
  1911. if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
  1912. return false;
  1913. }
  1914. switch ($csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']) {
  1915. case 'rsaEncryption':
  1916. $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
  1917. base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
  1918. }
  1919. $asn1 = new File_ASN1();
  1920. $asn1->loadOIDs($this->oids);
  1921. $filters = array();
  1922. $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] =
  1923. array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  1924. $asn1->loadFilters($filters);
  1925. $csr = $asn1->encodeDER($csr, $this->CertificationRequest);
  1926. return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr)) . '-----END CERTIFICATE REQUEST-----';
  1927. }
  1928. /**
  1929. * Sign an X.509 certificate
  1930. *
  1931. * $issuer's private key needs to be loaded.
  1932. * $subject can be either an existing X.509 cert (if you want to resign it),
  1933. * a CSR or something with the DN and public key explicitly set.
  1934. *
  1935. * @param File_X509 $issuer
  1936. * @param File_X509 $subject
  1937. * @param String $signatureAlgorithm optional
  1938. * @access public
  1939. * @return Mixed
  1940. */
  1941. function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
  1942. {
  1943. if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
  1944. return false;
  1945. }
  1946. if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) {
  1947. return false;
  1948. }
  1949. $currentCert = $this->currentCert;
  1950. $signatureSubject = $this->signatureSubject;
  1951. if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
  1952. $this->currentCert = $subject->currentCert;
  1953. $this->currentCert['tbsCertificate']['signature']['algorithm'] =
  1954. $this->currentCert['signatureAlgorithm']['algorithm'] =
  1955. $signatureAlgorithm;
  1956. if (!empty($this->startDate)) {
  1957. $this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate;
  1958. unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']);
  1959. }
  1960. if (!empty($this->endDate)) {
  1961. $this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime'] = $this->endDate;
  1962. unset($this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime']);
  1963. }
  1964. if (!empty($this->serialNumber)) {
  1965. $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
  1966. }
  1967. if (!empty($subject->dn)) {
  1968. $this->currentCert['tbsCertificate']['subject'] = $subject->dn;
  1969. }
  1970. if (!empty($subject->publicKey)) {
  1971. $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
  1972. }
  1973. $this->removeExtension('id-ce-authorityKeyIdentifier');
  1974. if (isset($subject->domains)) {
  1975. $this->removeExtension('id-ce-subjectAltName');
  1976. }
  1977. } else {
  1978. if (!isset($subject->publicKey)) {
  1979. return false;
  1980. }
  1981. $startDate = !empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T');
  1982. $endDate = !empty($this->endDate) ? $this->endDate : @date('M j H:i:s Y T', strtotime('+1 year'));
  1983. $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new Math_BigInteger();
  1984. $this->currentCert = array(
  1985. 'tbsCertificate' =>
  1986. array(
  1987. 'version' => 'v3',
  1988. 'serialNumber' => $serialNumber, // $this->setserialNumber()
  1989. 'signature' => array('algorithm' => $signatureAlgorithm),
  1990. 'issuer' => false, // this is going to be overwritten later
  1991. 'validity' => array(
  1992. 'notBefore' => array('generalTime' => $startDate), // $this->setStartDate()
  1993. 'notAfter' => array('generalTime' => $endDate) // $this->setEndDate()
  1994. ),
  1995. 'subject' => $subject->dn,
  1996. 'subjectPublicKeyInfo' => $subjectPublicKey
  1997. ),
  1998. 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
  1999. 'signature' => false // this is going to be overwritten later
  2000. );
  2001. }
  2002. $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;
  2003. if (isset($issuer->keyIdentifier)) {
  2004. $extensions = &$this->currentCert['tbsCertificate']['extensions'];
  2005. $extensions[] = array(
  2006. 'extnId' => 'id-ce-authorityKeyIdentifier',
  2007. 'critical' => false,
  2008. 'extnValue'=> array(
  2009. //'authorityCertIssuer' => array(
  2010. // array(
  2011. // 'directoryName' => $issuer->dn
  2012. // )
  2013. //),
  2014. 'keyIdentifier' => $issuer->keyIdentifier
  2015. )
  2016. );
  2017. //if (isset($issuer->serialNumber)) {
  2018. // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
  2019. //}
  2020. unset($extensions);
  2021. }
  2022. if (isset($subject->keyIdentifier)) {
  2023. $this->removeExtension('id-ce-subjectKeyIdentifier');
  2024. $this->currentCert['tbsCertificate']['extensions'][] = array(
  2025. 'extnId' => 'id-ce-subjectKeyIdentifier',
  2026. 'critical' => false,
  2027. 'extnValue'=> $subject->keyIdentifier
  2028. );
  2029. }
  2030. if (isset($subject->domains) && count($subject->domains) > 1) {
  2031. $this->currentCert['tbsCertificate']['extensions'][] = array(
  2032. 'extnId' => 'id-ce-subjectAltName',
  2033. 'critical' => false,
  2034. 'extnValue' => array()
  2035. );
  2036. $last = count($this->currentCert['tbsCertificate']['extensions']) - 1;
  2037. foreach ($subject->domains as $domain) {
  2038. $this->currentCert['tbsCertificate']['extensions'][$last]['extnValue'][] = array('dNSName' => $domain);
  2039. }
  2040. }
  2041. if ($this->caFlag) {
  2042. $keyUsage = $this->getExtension('id-ce-keyUsage');
  2043. if (!$keyUsage) {
  2044. $keyUsage = array();
  2045. }
  2046. $this->removeExtension('id-ce-keyUsage');
  2047. $this->currentCert['tbsCertificate']['extensions'][] = array(
  2048. 'extnId' => 'id-ce-keyUsage',
  2049. 'critical' => false,
  2050. 'extnValue' => array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign'))))
  2051. );
  2052. $basicConstraints = $this->getExtension('id-ce-basicConstraints');
  2053. if (!$basicConstraints) {
  2054. $basicConstraints = array();
  2055. }
  2056. $this->removeExtension('id-ce-basicConstraints');
  2057. $this->currentCert['tbsCertificate']['extensions'][] = array(
  2058. 'extnId' => 'id-ce-basicConstraints',
  2059. 'critical' => true,
  2060. 'extnValue' => array_unique(array_merge(array('cA' => true), $basicConstraints))
  2061. );
  2062. }
  2063. // resync $this->signatureSubject
  2064. // save $tbsCertificate in case there are any File_ASN1_Element objects in it
  2065. $tbsCertificate = $this->currentCert['tbsCertificate'];
  2066. $this->loadX509($this->saveX509($this->currentCert));
  2067. $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
  2068. $result['tbsCertificate'] = $tbsCertificate;
  2069. $this->currentCert = $currentCert;
  2070. $this->signatureSubject = $signatureSubject;
  2071. return $result;
  2072. }
  2073. /**
  2074. * Sign a CSR
  2075. *
  2076. * @access public
  2077. * @return Mixed
  2078. */
  2079. function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
  2080. {
  2081. if (!is_object($this->privateKey) || empty($this->dn)) {
  2082. return false;
  2083. }
  2084. $origPublicKey = $this->publicKey;
  2085. $class = get_class($this->privateKey);
  2086. $this->publicKey = new $class();
  2087. $this->publicKey->loadKey($this->privateKey->getPublicKey());
  2088. $this->publicKey->setPublicKey();
  2089. if (!($publicKey = $this->_formatSubjectPublicKey())) {
  2090. return false;
  2091. }
  2092. $this->publicKey = $origPublicKey;
  2093. $currentCert = $this->currentCert;
  2094. $signatureSubject = $this->signatureSubject;
  2095. if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
  2096. $this->currentCert['signatureAlgorithm']['algorithm'] =
  2097. $signatureAlgorithm;
  2098. if (!empty($this->dn)) {
  2099. $this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
  2100. }
  2101. $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
  2102. } else {
  2103. $this->currentCert = array(
  2104. 'certificationRequestInfo' =>
  2105. array(
  2106. 'version' => 'v1',
  2107. 'subject' => $this->dn,
  2108. 'subjectPKInfo' => $publicKey
  2109. ),
  2110. 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
  2111. 'signature' => false // this is going to be overwritten later
  2112. );
  2113. }
  2114. // resync $this->signatureSubject
  2115. // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
  2116. $certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
  2117. $this->loadCSR($this->saveCSR($this->currentCert));
  2118. $result = $this->_sign($this->privateKey, $signatureAlgorithm);
  2119. $result['certificationRequestInfo'] = $certificationRequestInfo;
  2120. $this->currentCert = $currentCert;
  2121. $this->signatureSubject = $signatureSubject;
  2122. return $result;
  2123. }
  2124. /**
  2125. * X.509 certificate signing helper function.
  2126. *
  2127. * @param Object $key
  2128. * @param File_X509 $subject
  2129. * @param String $signatureAlgorithm
  2130. * @access public
  2131. * @return Mixed
  2132. */
  2133. function _sign($key, $signatureAlgorithm)
  2134. {
  2135. switch (strtolower(get_class($key))) {
  2136. case 'crypt_rsa':
  2137. switch ($signatureAlgorithm) {
  2138. case 'md2WithRSAEncryption':
  2139. case 'md5WithRSAEncryption':
  2140. case 'sha1WithRSAEncryption':
  2141. case 'sha224WithRSAEncryption':
  2142. case 'sha256WithRSAEncryption':
  2143. case 'sha384WithRSAEncryption':
  2144. case 'sha512WithRSAEncryption':
  2145. $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
  2146. $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  2147. $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
  2148. return $this->currentCert;
  2149. }
  2150. default:
  2151. return false;
  2152. }
  2153. }
  2154. /**
  2155. * Set certificate start date
  2156. *
  2157. * @param String $date
  2158. * @access public
  2159. */
  2160. function setStartDate($date)
  2161. {
  2162. $this->startDate = @date('M j H:i:s Y T', @strtotime($date));
  2163. }
  2164. /**
  2165. * Set certificate end date
  2166. *
  2167. * @param String $date
  2168. * @access public
  2169. */
  2170. function setEndDate($date)
  2171. {
  2172. /*
  2173. To indicate that a certificate has no well-defined expiration date,
  2174. the notAfter SHOULD be assigned the GeneralizedTime value of
  2175. 99991231235959Z.
  2176. -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
  2177. */
  2178. if (strtolower($date) == 'lifetime') {
  2179. $temp = '99991231235959Z';
  2180. $asn1 = new File_ASN1();
  2181. $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
  2182. $this->endDate = new File_ASN1_Element($temp);
  2183. } else {
  2184. $this->endDate = @date('M j H:i:s Y T', @strtotime($date));
  2185. }
  2186. }
  2187. /**
  2188. * Set Serial Number
  2189. *
  2190. * @param String $serial
  2191. * @access public
  2192. */
  2193. function setSerialNumber($serial)
  2194. {
  2195. $this->serialNumber = new Math_BigInteger($serial, -256);
  2196. }
  2197. /**
  2198. * Turns the certificate into a certificate authority
  2199. *
  2200. * @access public
  2201. */
  2202. function makeCA()
  2203. {
  2204. $this->caFlag = true;
  2205. }
  2206. /**
  2207. * Remove an Extension
  2208. *
  2209. * @param String $id
  2210. * @access public
  2211. * @return Boolean
  2212. */
  2213. function removeExtension($id)
  2214. {
  2215. switch (true) {
  2216. case !is_array($this->currentCert):
  2217. case !isset($this->currentCert['tbsCertificate']['extensions']):
  2218. return false;
  2219. }
  2220. $result = false;
  2221. $extensions = &$this->currentCert['tbsCertificate']['extensions'];
  2222. foreach ($extensions as $key => $value) {
  2223. if ($value['extnId'] == $id) {
  2224. unset($extensions[$key]);
  2225. $result = true;
  2226. }
  2227. }
  2228. $extensions = array_values($extensions);
  2229. return $result;
  2230. }
  2231. /**
  2232. * Get an Extension
  2233. *
  2234. * Returns the extension if it exists and false if not
  2235. *
  2236. * @param String $id
  2237. * @access public
  2238. * @return Mixed
  2239. */
  2240. function getExtension($id, $cert = NULL)
  2241. {
  2242. if (!isset($cert)) {
  2243. $cert = $this->currentCert;
  2244. }
  2245. switch (true) {
  2246. case !is_array($cert):
  2247. case !isset($cert['tbsCertificate']['extensions']):
  2248. return false;
  2249. }
  2250. foreach ($cert['tbsCertificate']['extensions'] as $key => $value) {
  2251. if ($value['extnId'] == $id) {
  2252. return $value['extnValue'];
  2253. }
  2254. }
  2255. return false;
  2256. }
  2257. /**
  2258. * Returns a list of all extensions in use
  2259. *
  2260. * @access public
  2261. * @return Array
  2262. */
  2263. function getExtensions($cert = NULL)
  2264. {
  2265. if (!isset($cert)) {
  2266. $cert = $this->currentCert;
  2267. }
  2268. switch (true) {
  2269. case !is_array($cert):
  2270. case !isset($cert['tbsCertificate']['extensions']):
  2271. return array();
  2272. }
  2273. $extensions = array();
  2274. foreach ($cert['tbsCertificate']['extensions'] as $extension) {
  2275. $extensions[] = $extension['extnId'];
  2276. }
  2277. return $extensions;
  2278. }
  2279. /**
  2280. * Sets the authority key identifier
  2281. *
  2282. * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
  2283. *
  2284. * @param String $value
  2285. * @access public
  2286. */
  2287. function setKeyIdentifier($value)
  2288. {
  2289. if (empty($value)) {
  2290. unset($this->keyIdentifier);
  2291. } else {
  2292. $this->keyIdentifier = base64_encode($value);
  2293. }
  2294. }
  2295. /**
  2296. * Format a public key as appropriate
  2297. *
  2298. * @access private
  2299. * @return Array
  2300. */
  2301. function _formatSubjectPublicKey()
  2302. {
  2303. if (!isset($this->publicKey) || !is_object($this->publicKey)) {
  2304. return false;
  2305. }
  2306. switch (strtolower(get_class($this->publicKey))) {
  2307. case 'crypt_rsa':
  2308. return array(
  2309. 'algorithm' => array('algorithm' => 'rsaEncryption'),
  2310. 'subjectPublicKey' => $this->publicKey->getPublicKey()
  2311. );
  2312. default:
  2313. return false;
  2314. }
  2315. }
  2316. /**
  2317. * Set the domain name's which the cert is to be valid for
  2318. *
  2319. * @access public
  2320. * @return Array
  2321. */
  2322. function setDomain()
  2323. {
  2324. $this->domains = func_get_args();
  2325. $this->removeDNProp('id-at-commonName');
  2326. $this->setDNProp('id-at-commonName', $this->domains[0]);
  2327. }
  2328. }