Signer.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php
  2. namespace Aws\CloudFront;
  3. /**
  4. * @internal
  5. */
  6. class Signer
  7. {
  8. private $keyPairId;
  9. private $pkHandle;
  10. /**
  11. * A signer for creating the signature values used in CloudFront signed URLs
  12. * and signed cookies.
  13. *
  14. * @param $keyPairId string ID of the key pair
  15. * @param $privateKey string Path to the private key used for signing
  16. * @param $passphrase string Passphrase to private key file, if one exists
  17. *
  18. * @throws \RuntimeException if the openssl extension is missing
  19. * @throws \InvalidArgumentException if the private key cannot be found.
  20. */
  21. public function __construct($keyPairId, $privateKey, $passphrase = "")
  22. {
  23. if (!extension_loaded('openssl')) {
  24. //@codeCoverageIgnoreStart
  25. throw new \RuntimeException('The openssl extension is required to '
  26. . 'sign CloudFront urls.');
  27. //@codeCoverageIgnoreEnd
  28. }
  29. $this->keyPairId = $keyPairId;
  30. if (!$this->pkHandle = openssl_pkey_get_private($privateKey, $passphrase)) {
  31. if (!file_exists($privateKey)) {
  32. throw new \InvalidArgumentException("PK file not found: $privateKey");
  33. }
  34. $this->pkHandle = openssl_pkey_get_private("file://$privateKey", $passphrase);
  35. if (!$this->pkHandle) {
  36. $errorMessages = [];
  37. while(($newMessage = openssl_error_string()) !== false){
  38. $errorMessages[] = $newMessage;
  39. }
  40. throw new \InvalidArgumentException(implode("\n",$errorMessages));
  41. }
  42. }
  43. }
  44. public function __destruct()
  45. {
  46. if (PHP_MAJOR_VERSION < 8) {
  47. $this->pkHandle && openssl_pkey_free($this->pkHandle);
  48. }
  49. }
  50. /**
  51. * Create the values used to construct signed URLs and cookies.
  52. *
  53. * @param string $resource The CloudFront resource to which
  54. * this signature will grant access.
  55. * Not used when a custom policy is
  56. * provided.
  57. * @param string|integer|null $expires UTC Unix timestamp used when
  58. * signing with a canned policy.
  59. * Not required when passing a
  60. * custom $policy.
  61. * @param string $policy JSON policy. Use this option when
  62. * creating a signature for a custom
  63. * policy.
  64. *
  65. * @return array The values needed to construct a signed URL or cookie
  66. * @throws \InvalidArgumentException when not provided either a policy or a
  67. * resource and a expires
  68. * @throws \RuntimeException when generated signature is empty
  69. *
  70. * @link http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html
  71. */
  72. public function getSignature($resource = null, $expires = null, $policy = null)
  73. {
  74. $signatureHash = [];
  75. if ($policy) {
  76. $policy = preg_replace('/\s/s', '', $policy);
  77. $signatureHash['Policy'] = $this->encode($policy);
  78. } elseif ($resource && $expires) {
  79. $expires = (int) $expires; // Handle epoch passed as string
  80. $policy = $this->createCannedPolicy($resource, $expires);
  81. $signatureHash['Expires'] = $expires;
  82. } else {
  83. throw new \InvalidArgumentException('Either a policy or a resource'
  84. . ' and an expiration time must be provided.');
  85. }
  86. $signatureHash['Signature'] = $this->encode($this->sign($policy));
  87. $signatureHash['Key-Pair-Id'] = $this->keyPairId;
  88. return $signatureHash;
  89. }
  90. private function createCannedPolicy($resource, $expiration)
  91. {
  92. return json_encode([
  93. 'Statement' => [
  94. [
  95. 'Resource' => $resource,
  96. 'Condition' => [
  97. 'DateLessThan' => ['AWS:EpochTime' => $expiration],
  98. ],
  99. ],
  100. ],
  101. ], JSON_UNESCAPED_SLASHES);
  102. }
  103. private function sign($policy)
  104. {
  105. $signature = '';
  106. if(!openssl_sign($policy, $signature, $this->pkHandle)) {
  107. $errorMessages = [];
  108. while(($newMessage = openssl_error_string()) !== false) {
  109. $errorMessages[] = $newMessage;
  110. }
  111. $exceptionMessage = "An error has occurred when signing the policy";
  112. if (count($errorMessages) > 0) {
  113. $exceptionMessage = implode("\n", $errorMessages);
  114. }
  115. throw new \RuntimeException($exceptionMessage);
  116. }
  117. return $signature;
  118. }
  119. private function encode($policy)
  120. {
  121. return strtr(base64_encode($policy), '+=/', '-_~');
  122. }
  123. }