Skip to content

Commit

Permalink
feat: validate and get signatures from signed pdf
Browse files Browse the repository at this point in the history
Signed-off-by: Vitor Mattos <[email protected]>
  • Loading branch information
vitormattos committed Dec 31, 2024
1 parent a03e18f commit 4a63c0d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/Controller/FileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ public function validate(?string $type = null, $identifier = null): DataResponse
->showSigners()
->showSettings()
->showMessages()
->showValidateFile()
->toArray()
);

Expand Down
33 changes: 33 additions & 0 deletions lib/Handler/Pkcs12Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use OCP\AppFramework\Services\IAppConfig;
use OCP\Files\File;
use OCP\IL10N;
use OCP\ITempManager;
use TypeError;

class Pkcs12Handler extends SignEngineHandler {
Expand All @@ -30,6 +31,7 @@ public function __construct(
private IL10N $l10n,
private JSignPdfHandler $jSignPdfHandler,
private FooterHandler $footerHandler,
private ITempManager $tempManager,
) {
}

Expand Down Expand Up @@ -79,6 +81,37 @@ public function readCertificate(string $uid, string $privateKey): array {
);
}

/**
* @param resource $resource
* @return array
*/
public function validatePdfContent($resource): array {
$content = stream_get_contents($resource);
preg_match_all('/ByteRange\s*\[(\d+) (?<start>\d+) (?<end>\d+) (\d+)?/', $content, $bytes);
if (empty($bytes['start']) || empty($bytes['end'])) {
throw new LibresignException($this->l10n->t('Unsigned file.'));
}

$parsed = [];
for ($i = 0; $i < count($bytes['start']); $i++) {
rewind($resource);
$signature = stream_get_contents(
$resource,
$bytes['end'][$i] - $bytes['start'][$i] - 2,
$bytes['start'][$i] + 1
);
$pfxCertificate = hex2bin($signature);
if (empty($tempFile)) {
$tempFile = $this->tempManager->getTemporaryFile('cert.pfx');
}
file_put_contents($tempFile, $pfxCertificate);
$output = shell_exec("openssl pkcs7 -in {$tempFile} -inform DER -print_certs");
$parsed[] = openssl_x509_parse($output);
}
$this->tempManager->clean();
return $parsed;
}

public function setPfxContent(string $content): void {
$this->pfxContent = $content;
}
Expand Down
58 changes: 58 additions & 0 deletions lib/Service/FileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
use OCA\Libresign\Db\SignRequest;
use OCA\Libresign\Db\SignRequestMapper;
use OCA\Libresign\Exception\LibresignException;
use OCA\Libresign\Handler\Pkcs12Handler;
use OCA\Libresign\Helper\ValidateHelper;
use OCA\Libresign\ResponseDefinitions;
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
use OCP\Accounts\IAccountManager;
use OCP\AppFramework\Services\IAppConfig;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Http\Client\IClientService;
use OCP\IDateTimeFormatter;
Expand All @@ -42,10 +45,12 @@ class FileService {
private bool $showSettings = false;
private bool $showVisibleElements = false;
private bool $showMessages = false;
private bool $validateFile = false;
private ?File $file = null;
private ?SignRequest $signRequest = null;
private ?IUser $me = null;
private ?int $identifyMethodId = null;
private array $certData = [];
private array $signers = [];
private array $settings = [
'canSign' => false,
Expand Down Expand Up @@ -74,6 +79,9 @@ public function __construct(
private IAppConfig $appConfig,
private IURLGenerator $urlGenerator,
protected IMimeTypeDetector $mimeTypeDetector,
protected Pkcs12Handler $pkcs12Handler,
private IUserMountCache $userMountCache,
private IRootFolder $root,
protected LoggerInterface $logger,
protected IL10N $l10n,
) {
Expand Down Expand Up @@ -137,6 +145,11 @@ public function setSignRequest(SignRequest $signRequest): self {
return $this;
}

public function showValidateFile(bool $validateFile = true): self {
$this->validateFile = $validateFile;
return $this;
}

/**
* @return static
*/
Expand All @@ -162,6 +175,7 @@ private function getSigners(): array {
return $this->signers;
}
$signers = $this->signRequestMapper->getByFileId($this->file->getId());
$certData = $this->getCertData();
foreach ($signers as $signer) {
$signatureToShow = [
'signed' => $signer->getSigned() ?
Expand Down Expand Up @@ -191,6 +205,29 @@ private function getSigners(): array {
$data['sign_date'] = (new \DateTime())
->setTimestamp($signer->getSigned())
->format('Y-m-d H:i:s');
$mySignature = array_filter($certData, function ($data) use ($signatureToShow) {
foreach ($signatureToShow['identifyMethods'] as $methods) {
foreach ($methods as $identifyMethod) {
$entity = $identifyMethod->getEntity();
if ($data['subject']['uid'] === $entity->getIdentifierKey() . ':' . $entity->getIdentifierValue()) {
return true;
}
}
}
});
if ($mySignature) {
$mySignature = current($mySignature);
$signatureToShow['subject'] = implode(
', ',
array_map(
fn (string $key, string $value) => "$key: $value",
array_keys($mySignature['subject']),
$mySignature['subject']
)
);
$signatureToShow['valid_from'] = $mySignature['validFrom_time_t'];
$signatureToShow['valid_to'] = $mySignature['validTo_time_t'];
}
}
// @todo refactor this code
if ($this->me || $this->identifyMethodId) {
Expand Down Expand Up @@ -396,6 +433,27 @@ private function getFile(): array {
return $return;
}

private function getCertData(): array {
if (!empty($this->certData) || !$this->validateFile || !$this->file->getSignedNodeId()) {
return $this->certData;
}
$mountsContainingFile = $this->userMountCache->getMountsForFileId($this->file->getSignedNodeId());
foreach ($mountsContainingFile as $fileInfo) {
$this->root->getByIdInPath($this->file->getSignedNodeId(), $fileInfo->getMountPoint());
}
$fileToValidate = $this->root->getById($this->file->getSignedNodeId());
if (!count($fileToValidate)) {
throw new LibresignException($this->l10n->t('Invalid data to validate file'), 404);
}
/** @var \OCP\Files\File */
$file = current($fileToValidate);

$resource = $file->fopen('rb');
$this->certData = $this->pkcs12Handler->validatePdfContent($resource);
fclose($resource);
return $this->certData;
}

/**
* @return string[][]
*
Expand Down
27 changes: 27 additions & 0 deletions src/views/Validation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,33 @@
</ul>
</template>
</NcListItem>
<NcListItem v-if="signer.opened && signer.valid_from"
class="extra"
compact
:name="t('libresign', 'Certificate valid from:')">
<template #name>
<strong>{{ t('libresign', 'Certificate valid from:')}}</strong>

Check warning on line 136 in src/views/Validation.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Expected 1 space before '}}', but not found
{{ dateFromUnixTimestamp(signer.valid_from) }}
</template>
</NcListItem>
<NcListItem v-if="signer.opened && signer.valid_to"
class="extra"
compact
:name="t('libresign', 'Certificate valid to:')">
<template #name>
<strong>{{ t('libresign', 'Certificate valid to:')}}</strong>

Check warning on line 145 in src/views/Validation.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Expected 1 space before '}}', but not found
{{ dateFromUnixTimestamp(signer.valid_to) }}
</template>
</NcListItem>
<NcListItem v-if="signer.opened && signer.subject"
class="extra"
compact
:name="t('libresign', 'Subject:')">
<template #name>
<strong>{{ t('libresign', 'Subject:')}}</strong>

Check warning on line 154 in src/views/Validation.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Expected 1 space before '}}', but not found
{{ signer.subject }}
</template>
</NcListItem>
</span>
</ul>
</div>
Expand Down

0 comments on commit 4a63c0d

Please sign in to comment.