diff --git a/composer.json b/composer.json
index 1dd2ae3e898c9fc2201fb9c43b651d73bbd75f43..49b559c3eabe56769510e160144e013af0141285 100644
--- a/composer.json
+++ b/composer.json
@@ -11,12 +11,12 @@
         }
     ],
     "require": {
-        "phpseclib/phpseclib": "^2.0.29"
+        "phpseclib/phpseclib": "^3.0"
     },
     "require-dev": {
-        "drupal/key": "^1.8"
+        "drupal/key": "^1.19"
     },
     "suggest": {
-        "drupal/key": "^1.8"
+        "drupal/key": "^1.19"
     }
 }
diff --git a/sftp_client.info.yml b/sftp_client.info.yml
index 2f79739ea64e3759892f28a5f1f0fcfc9bb37c6b..dc7cab61bde36a3a39ffa5830d3cc95bd9687d41 100644
--- a/sftp_client.info.yml
+++ b/sftp_client.info.yml
@@ -2,5 +2,5 @@ name: 'SFTP Client'
 description: 'The SFTP client for programmatic use.'
 package: Developer
 type: module
-php: 7.3
-core_version_requirement: ^8 || ^9
+php: 8.1
+core_version_requirement: ^10.3 || ^11
diff --git a/src/SftpClient.php b/src/SftpClient.php
index 3bc0e5b857f347a9da80305a2022e7f9cead5fb1..811c04e455c6f421fd2d402efa3d3d10afe469a7 100644
--- a/src/SftpClient.php
+++ b/src/SftpClient.php
@@ -3,14 +3,15 @@
 namespace Drupal\sftp_client;
 
 use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\File\FileExists;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Drupal\Core\Logger\LoggerChannelInterface;
 use Drupal\Core\Site\Settings;
 use Drupal\key\KeyRepositoryInterface;
 use Drupal\sftp_client\Exception\SftpException;
 use Drupal\sftp_client\Exception\SftpLoginException;
-use phpseclib\Crypt\RSA;
-use phpseclib\Net\SFTP;
+use phpseclib3\Crypt\PublicKeyLoader;
+use phpseclib3\Net\SFTP;
 use Psr\Log\LoggerInterface;
 use Psr\Log\LoggerTrait;
 use Psr\Log\LogLevel;
@@ -91,7 +92,7 @@ class SftpClient implements SftpClientInterface {
     Settings $settings,
     FileSystemInterface $file_system,
     LoggerChannelFactoryInterface $logger_factory,
-    ?KeyRepositoryInterface $key_repository
+    ?KeyRepositoryInterface $key_repository,
   ) {
     $this->fileSystem = $file_system;
     $this->sftpSettings = $settings::get(static::SETTING, []);
@@ -225,9 +226,11 @@ class SftpClient implements SftpClientInterface {
     }
 
     if ($this->settings['key_id'] !== FALSE) {
-      $rsa = new RSA();
-      $rsa->loadKey($this->keyRepository->getKey($this->settings['key_id'])->getKeyValue());
-      $rsa->setPassword($this->settings['password']);
+      $key_password = $this->settings['password'];
+      $key_value = $this->keyRepository->getKey($this->settings['key_id'])->getKeyValue();
+
+      /** @var \phpseclib3\Crypt\RSA $rsa */
+      $rsa = PublicKeyLoader::load($key_value, $key_password);
 
       // Convert the password to RSA key.
       /* @see \phpseclib\Net\SFTP::_login_helper() */
@@ -387,9 +390,9 @@ class SftpClient implements SftpClientInterface {
   /**
    * {@inheritdoc}
    */
-  public function moveFile(string $source, string $destination, int $replace = FileSystemInterface::EXISTS_ERROR): bool {
+  public function moveFile(string $source, string $destination, /* FileExists */ object $replace = FileExists::Error): bool {
     switch ($replace) {
-      case FileSystemInterface::EXISTS_RENAME:
+      case FileExists::Rename:
         $new_destination = $destination;
         $counter = 0;
 
@@ -400,7 +403,7 @@ class SftpClient implements SftpClientInterface {
         $destination = $new_destination;
         break;
 
-      case FileSystemInterface::EXISTS_REPLACE:
+      case FileExists::Replace:
         if ($this->isFile($destination)) {
           $this->removeFile($destination);
         }
@@ -515,7 +518,7 @@ class SftpClient implements SftpClientInterface {
    *
    * @link https://www.php.net/manual/en/class.filesystemiterator.php#filesystemiterator.constants.skip-dots
    */
-  protected function listItems(string $path_remote, string $type_constant = NULL, bool $skip_dots = TRUE): \Generator {
+  protected function listItems(string $path_remote, ?string $type_constant = NULL, bool $skip_dots = TRUE): \Generator {
     $list = $this->connect()->rawlist($path_remote);
 
     if (\is_array($list)) {
diff --git a/src/SftpClientInterface.php b/src/SftpClientInterface.php
index 752163f29395f11ea90edddb750aa6deb72bdd4f..da24d352fa59dd8f62fda021ea41bf2a4b458b97 100644
--- a/src/SftpClientInterface.php
+++ b/src/SftpClientInterface.php
@@ -167,11 +167,11 @@ interface SftpClientInterface {
    *   The path to a remote file.
    * @param string $destination
    *   The path to a new location of a remote file.
-   * @param int $replace
-   *   Replace behavior when the destination file already exists:
-   *   - {@see FileSystemInterface::EXISTS_REPLACE};
-   *   - {@see FileSystemInterface::EXISTS_RENAME};
-   *   - {@see FileSystemInterface::EXISTS_ERROR}.
+   * @param object $replace
+   *   Behavior when the destination file already exists:
+   *   - {@see FileExists::Replace};
+   *   - {@see FileExists::Rename};
+   *   - {@see FileExists::Error}.
    *
    * @return bool
    *   The state of whether a file was successfully moved.
@@ -179,9 +179,9 @@ interface SftpClientInterface {
    * @throws \Drupal\sftp_client\Exception\SftpLoginException
    *   When the connection to SFTP server cannot be established.
    *
-   * @see \Drupal\Core\File\FileSystemInterface
+   * @see \Drupal\Core\File\FileExists
    */
-  public function moveFile(string $source, string $destination, int $replace): bool;
+  public function moveFile(string $source, string $destination, /* FileExists */ object $replace): bool;
 
   /**
    * Removes a remote file.
diff --git a/src/SftpResource.php b/src/SftpResource.php
index 6ccfa374b1e19b4673a0bda16d028f73d299a32e..9509861bc9fb5354e01695eebd53c79a8af3fd43 100644
--- a/src/SftpResource.php
+++ b/src/SftpResource.php
@@ -61,6 +61,11 @@ class SftpResource {
    *   The data to create a resource.
    */
   public function __construct(array $data) {
+    // Convert mode to permission.
+    if (!isset($data['permissions']) && isset($data['mode'])) {
+      $data['permissions'] = \decoct($data['mode']);
+    }
+
     foreach ($this as $key => $value) {
       if (!isset($data[$key])) {
         throw new \InvalidArgumentException(\sprintf('The "%s" must exist in a resource definition!', $key));
diff --git a/tests/src/Unit/SftpClientTest.php b/tests/src/Unit/SftpClientTest.php
index 47fae3b0b806d527256520e76e8af20ac8a3abc2..a322961233cbe567632e189c911e2e05b49f398c 100644
--- a/tests/src/Unit/SftpClientTest.php
+++ b/tests/src/Unit/SftpClientTest.php
@@ -11,7 +11,7 @@ use Drupal\key\KeyRepositoryInterface;
 use Drupal\sftp_client\Exception\SftpLoginException;
 use Drupal\sftp_client\SftpClient;
 use Drupal\Tests\UnitTestCase;
-use phpseclib\Crypt\RSA;
+use phpseclib3\Crypt\RSA;
 use PHPUnit\Framework\Error\Notice;
 
 /**
@@ -61,17 +61,11 @@ class SftpClientTest extends UnitTestCase {
     $logger_channel = new LoggerChannel('sftp.client.test');
     $logger_channel->addLogger($this->logger);
 
-    $this->fileSystem = $this
-      ->getMockBuilder(FileSystemInterface::class)
-      ->getMock();
+    $this->fileSystem = $this->createMock(FileSystemInterface::class);
 
-    $this->keyRepository = $this
-      ->getMockBuilder(KeyRepositoryInterface::class)
-      ->getMock();
+    $this->keyRepository = $this->createMock(KeyRepositoryInterface::class);
 
-    $this->loggerChannelFactory = $this
-      ->getMockBuilder(LoggerChannelFactoryInterface::class)
-      ->getMock();
+    $this->loggerChannelFactory = $this->createMock(LoggerChannelFactoryInterface::class);
 
     $this->loggerChannelFactory
       ->method('get')
@@ -92,7 +86,7 @@ class SftpClientTest extends UnitTestCase {
   protected function getSftpClient(Settings $settings, $key = NULL): SftpClient {
     $this->keyRepository
       ->method('getKey')
-      ->willReturn($key ?? $this->getMockBuilder(KeyInterface::class)->getMock());
+      ->willReturn($key ?? $this->createMock(KeyInterface::class));
 
     $sftp_client = new SftpClient(
       $settings,
@@ -447,7 +441,7 @@ class SftpClientTest extends UnitTestCase {
   /**
    * {@inheritdoc}
    */
-  public function providerRetry(): array {
+  public static function providerRetry(): array {
     return [
       [
         5,