Subversion Repositories oidplus

Rev

Rev 1129 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2. /**
  3.  * SBrook\JWS\JwsRsa
  4.  */
  5.  
  6. namespace SBrook\JWS;
  7.  
  8. use SBrook\JWS\Exception\JwsException;
  9.  
  10. /**
  11.  * Class JwsRsa
  12.  * @package SBrook\JWS
  13.  * @throws JwsException:
  14.  *  40. Private key is not set
  15.  *  41. Public key is not set
  16.  *  49. Forwarded openssl error(s)
  17.  */
  18. class JwsRsa extends Jws implements Asymmetric {
  19.         /**
  20.          * JWS signature private key.
  21.          * @var resource
  22.          */
  23.         protected $privateKey = false;
  24.  
  25.         /**
  26.          * JWS signature public key.
  27.          * @var resource
  28.          */
  29.         protected $publicKey = false;
  30.  
  31.         /**
  32.          * Default signature algorithm.
  33.          * @var string
  34.          */
  35.         protected $defaultAlgo = "RS256";
  36.  
  37.         /**
  38.          * Signature algorithms map JWS => openssl_sign() / openssl_verify().
  39.          *
  40.          * JWS signature algorithms (RFC 7518, Section 3.3) - "alg":
  41.          *  RS256: RSASSA-PKCS1-v1_5 using SHA-256
  42.          *  RS384: RSASSA-PKCS1-v1_5 using SHA-384
  43.          *  RS512: RSASSA-PKCS1-v1_5 using SHA-512
  44.          *
  45.          * @var array
  46.          */
  47.         protected $algos = [
  48.                 "RS256" => OPENSSL_ALGO_SHA256,
  49.                 "RS384" => OPENSSL_ALGO_SHA384,
  50.                 "RS512" => OPENSSL_ALGO_SHA512
  51.         ];
  52.  
  53.         /**
  54.          * JwsRsa destructor.
  55.          */
  56.         public function __destruct() {
  57.                 if ($this->privateKey) {
  58.                         if (version_compare(PHP_VERSION, '8.0.0') < 0) openssl_pkey_free($this->privateKey);
  59.                 }
  60.  
  61.                 if ($this->publicKey) {
  62.                         if (version_compare(PHP_VERSION, '8.0.0') < 0) openssl_pkey_free($this->publicKey);
  63.                 }
  64.  
  65.                 unset(
  66.                         $this->defaultAlgo,
  67.                         $this->algos
  68.                 );
  69.         }
  70.  
  71.         /**
  72.          * Set private key - overwrites previously set key.
  73.          * @param string $key - Private key. Same as openssl_pkey_get_private "key" parameter (http://php.net/manual/en/function.openssl-pkey-get-private.php).
  74.          * @param string $pass - (Optional) Private key password. Same as openssl_pkey_get_private "passphrase" parameter (http://php.net/manual/en/function.openssl-pkey-get-private.php).
  75.          * @throws JwsException
  76.          */
  77.         public function setPrivateKey($key, $pass = "") {
  78.                 if ($this->privateKey) {
  79.                         if (version_compare(PHP_VERSION, '8.0.0') < 0) openssl_pkey_free($this->privateKey);
  80.                 }
  81.  
  82.                 $this->privateKey = openssl_pkey_get_private($key, $pass);
  83.                 if (!$this->privateKey) {
  84.                         throw new JwsException($this->getOpensslErrors(), 49);
  85.                 }
  86.         }
  87.  
  88.         /**
  89.          * Set public key - overwrites previously set key.
  90.          * @param string $key - Public key. Same as openssl_pkey_get_public "certificate" parameter (http://php.net/manual/en/function.openssl-pkey-get-public.php).
  91.          * @throws JwsException
  92.          */
  93.         public function setPublicKey($key) {
  94.                 if ($this->publicKey) {
  95.                         if (version_compare(PHP_VERSION, '8.0.0') < 0) openssl_pkey_free($this->publicKey);
  96.                 }
  97.  
  98.                 $this->publicKey = openssl_pkey_get_public($key);
  99.                 if (!$this->publicKey) {
  100.                         throw new JwsException($this->getOpensslErrors(), 49);
  101.                 }
  102.         }
  103.  
  104.         /**
  105.          * Create JWS from payload and optional header and sign it.
  106.          * @param string $payload - Payload.
  107.          * @param array $header - (Optional) Header data.
  108.          * @return string - JWS.
  109.          * @throws JwsException
  110.          */
  111.         public function sign($payload, $header = []) {
  112.                 if ($this->privateKey) {
  113.                         $d = $this->prepareSign($this->defaultAlgo, $payload, $header);
  114.  
  115.                         $signature = null;
  116.                         $v = openssl_sign($d["h"] . "." . $d["p"], $signature, $this->privateKey, $this->algos[$d["alg"]]);
  117.                         if ($v) {
  118.                                 return $d["h"] . "." . $d["p"] . "." . base64_encode($signature);
  119.                         } else {
  120.                                 throw new JwsException($this->getOpensslErrors(), 49);
  121.                         }
  122.                 } else {
  123.                         throw new JwsException("Private key is not set", 40);
  124.                 }
  125.         }
  126.  
  127.         /**
  128.          * Verify JWS signature.
  129.          * @param string $jws - JWS.
  130.          * @return bool - TRUE on valid signature, FALSE on invalid.
  131.          * @throws JwsException
  132.          */
  133.         public function verify($jws) {
  134.                 if ($this->publicKey) {
  135.                         $d = $this->prepareVerify($jws);
  136.  
  137.                         $v = openssl_verify($d["h"] . "." . $d["p"], $d["sig"], $this->publicKey, $this->algos[$d["alg"]]);
  138.                         if ($v == 1) {
  139.                                 return true;
  140.                         } else if ($v == 0) {
  141.                                 return false;
  142.                         } else {
  143.                                 throw new JwsException($this->getOpensslErrors(), 49);
  144.                         }
  145.                 } else {
  146.                         throw new JwsException("Public key is not set", 41);
  147.                 }
  148.         }
  149.  
  150.         /**
  151.          * Check validity of signature algorithm.
  152.          * @param string $algorithm - Algorithm name.
  153.          * @return bool - TRUE on valid algorithm, FALSE on invalid.
  154.          */
  155.         protected function isValidAlgorithm(string $algorithm): bool {
  156.                 return array_key_exists(strtoupper($algorithm), $this->algos);
  157.         }
  158.  
  159.         /**
  160.          * Get openssl error queue.
  161.          * @return string - Openssl error message(s).
  162.          */
  163.         protected function getOpensslErrors() {
  164.                 $message = "OpenSSL Error(s):";
  165.  
  166.                 while ($m = openssl_error_string()) {
  167.                         $message .= " " . $m;
  168.                 }
  169.  
  170.                 return $message;
  171.         }
  172. }
  173.