Encryptor.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace App\Libs\TikTok\Kernel;
  3. use App\Libs\TikTok\Kernel\Exceptions\RuntimeException;
  4. use App\Libs\TikTok\Kernel\Support\AES;
  5. /**
  6. * Class Encryptor.
  7. *
  8. * @author overtrue <i@overtrue.me>
  9. */
  10. class Encryptor {
  11. public const ERROR_INVALID_SIGNATURE = -40001; // Signature verification failed
  12. public const ERROR_PARSE_XML = -40002; // Parse XML failed
  13. public const ERROR_CALC_SIGNATURE = -40003; // Calculating the signature failed
  14. public const ERROR_INVALID_AES_KEY = -40004; // Invalid AESKey
  15. public const ERROR_INVALID_APP_ID = -40005; // Check AppID failed
  16. public const ERROR_ENCRYPT_AES = -40006; // AES EncryptionInterface failed
  17. public const ERROR_DECRYPT_AES = -40007; // AES decryption failed
  18. public const ERROR_INVALID_XML = -40008; // Invalid XML
  19. public const ERROR_BASE64_ENCODE = -40009; // Base64 encoding failed
  20. public const ERROR_BASE64_DECODE = -40010; // Base64 decoding failed
  21. public const ERROR_XML_BUILD = -40011; // XML build failed
  22. public const ILLEGAL_BUFFER = -41003; // Illegal buffer
  23. /**
  24. * App id.
  25. *
  26. * @var string
  27. */
  28. protected $appId;
  29. /**
  30. * App token.
  31. *
  32. * @var string
  33. */
  34. protected $token;
  35. /**
  36. * @var string
  37. */
  38. protected $aesKey;
  39. /**
  40. * Block size.
  41. *
  42. * @var int
  43. */
  44. protected $blockSize = 32;
  45. /**
  46. * Constructor.
  47. *
  48. * @param string $appId
  49. * @param string|null $token
  50. * @param string|null $aesKey
  51. */
  52. public function __construct(string $appId, string $token = null, string $aesKey = null) {
  53. $this->appId = $appId;
  54. $this->token = $token;
  55. $this->aesKey = base64_decode($aesKey . '=', true);
  56. }
  57. /**
  58. * Get the app token.
  59. *
  60. * @return string
  61. */
  62. public function getToken(): string {
  63. return $this->token;
  64. }
  65. /**
  66. * Decrypt message.
  67. *
  68. * @param string $content
  69. * @param string $msgSignature
  70. * @param string $nonce
  71. * @param string $timestamp
  72. *
  73. * @return string
  74. *
  75. * @throws RuntimeException|Exceptions\InvalidArgumentException
  76. */
  77. public function decrypt(string $content, string $msgSignature, string $nonce, string $timestamp): string {
  78. $signature = $this->signature($this->token, $timestamp, $nonce, $content);
  79. if ($signature !== $msgSignature) {
  80. throw new RuntimeException('Invalid Signature.', self::ERROR_INVALID_SIGNATURE);
  81. }
  82. $decrypted = AES::decrypt(
  83. base64_decode($content, true),
  84. $this->aesKey,
  85. substr($this->aesKey, 0, 16),
  86. OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
  87. );
  88. $result = $this->pkcs7Unpad($decrypted);
  89. $content = substr($result, 16, strlen($result));
  90. $contentLen = unpack('N', substr($content, 0, 4))[1];
  91. if (trim(substr($content, $contentLen + 4)) !== $this->appId) {
  92. throw new RuntimeException('Invalid appId.', self::ERROR_INVALID_APP_ID);
  93. }
  94. return substr($content, 4, $contentLen);
  95. }
  96. /**
  97. * Get SHA1.
  98. *
  99. * @return string
  100. */
  101. public function signature(): string {
  102. $array = func_get_args();
  103. sort($array, SORT_STRING);
  104. return sha1(implode($array));
  105. }
  106. /**
  107. * PKCS#7 pad.
  108. *
  109. * @param string $text
  110. * @param int $blockSize
  111. *
  112. * @return string
  113. *
  114. * @throws RuntimeException
  115. */
  116. public function pkcs7Pad(string $text, int $blockSize): string {
  117. if ($blockSize > 256) {
  118. throw new RuntimeException('$blockSize may not be more than 256');
  119. }
  120. $padding = $blockSize - (strlen($text) % $blockSize);
  121. $pattern = chr($padding);
  122. return $text . str_repeat($pattern, $padding);
  123. }
  124. /**
  125. * PKCS#7 unpad.
  126. *
  127. * @param string $text
  128. *
  129. * @return string
  130. */
  131. public function pkcs7Unpad(string $text): string {
  132. $pad = ord(substr($text, -1));
  133. if ($pad < 1 || $pad > $this->blockSize) {
  134. $pad = 0;
  135. }
  136. return substr($text, 0, (strlen($text) - $pad));
  137. }
  138. }