SignApp.php 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Core\Command\Integrity;
  8. use OC\IntegrityCheck\Checker;
  9. use OC\IntegrityCheck\Helpers\FileAccessHelper;
  10. use OCP\IURLGenerator;
  11. use phpseclib\Crypt\RSA;
  12. use phpseclib\File\X509;
  13. use Symfony\Component\Console\Command\Command;
  14. use Symfony\Component\Console\Input\InputInterface;
  15. use Symfony\Component\Console\Input\InputOption;
  16. use Symfony\Component\Console\Output\OutputInterface;
  17. /**
  18. * Class SignApp
  19. *
  20. * @package OC\Core\Command\Integrity
  21. */
  22. class SignApp extends Command {
  23. public function __construct(
  24. private Checker $checker,
  25. private FileAccessHelper $fileAccessHelper,
  26. private IURLGenerator $urlGenerator,
  27. ) {
  28. parent::__construct(null);
  29. }
  30. protected function configure() {
  31. $this
  32. ->setName('integrity:sign-app')
  33. ->setDescription('Signs an app using a private key.')
  34. ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Application to sign')
  35. ->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
  36. ->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
  37. }
  38. /**
  39. * {@inheritdoc }
  40. */
  41. protected function execute(InputInterface $input, OutputInterface $output): int {
  42. $path = $input->getOption('path');
  43. $privateKeyPath = $input->getOption('privateKey');
  44. $keyBundlePath = $input->getOption('certificate');
  45. if (is_null($path) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
  46. $documentationUrl = $this->urlGenerator->linkToDocs('developer-code-integrity');
  47. $output->writeln('This command requires the --path, --privateKey and --certificate.');
  48. $output->writeln('Example: ./occ integrity:sign-app --path="/Users/lukasreschke/Programming/myapp/" --privateKey="/Users/lukasreschke/private/myapp.key" --certificate="/Users/lukasreschke/public/mycert.crt"');
  49. $output->writeln('For more information please consult the documentation: '. $documentationUrl);
  50. return 1;
  51. }
  52. $privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
  53. $keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
  54. if ($privateKey === false) {
  55. $output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
  56. return 1;
  57. }
  58. if ($keyBundle === false) {
  59. $output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
  60. return 1;
  61. }
  62. $rsa = new RSA();
  63. $rsa->loadKey($privateKey);
  64. $x509 = new X509();
  65. $x509->loadX509($keyBundle);
  66. $x509->setPrivateKey($rsa);
  67. try {
  68. $this->checker->writeAppSignature($path, $x509, $rsa);
  69. $output->writeln('Successfully signed "'.$path.'"');
  70. } catch (\Exception $e) {
  71. $output->writeln('Error: ' . $e->getMessage());
  72. return 1;
  73. }
  74. return 0;
  75. }
  76. }