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