add ability to use timestamp server

This commit is contained in:
Pete Matsyburka
2023-12-02 18:14:33 +02:00
parent c5d71505ef
commit ced88476cc
9 changed files with 168 additions and 10 deletions
+8
View File
@@ -71,6 +71,14 @@ module Accounts
end
end
def load_timeserver_url(account)
if Docuseal.multitenant?
Docuseal::TIMESERVER_URL
else
EncryptedConfig.find_by(account:, key: EncryptedConfig::TIMESTAMP_SERVER_URL_KEY)&.value
end.presence
end
def can_send_emails?(_account)
return true if Docuseal.multitenant?
return true if ENV['SMTP_ADDRESS'].present?
+1
View File
@@ -28,6 +28,7 @@ module Docuseal
end
CERTS = JSON.parse(ENV.fetch('CERTS', '{}'))
TIMESERVER_URL = ENV.fetch('TIMESERVER_URL', nil)
DEFAULT_URL_OPTIONS = {
host: HOST,
+11 -4
View File
@@ -32,6 +32,7 @@ module Submissions
def call(submission)
account = submission.template.account
pkcs = Accounts.load_signing_pkcs(account)
tsa_url = Accounts.load_timeserver_url(account)
verify_url = Rails.application.routes.url_helpers.settings_esign_url(**Docuseal.default_url_options)
composer = HexaPDF::Composer.new(skip_page_creation: true)
@@ -254,10 +255,16 @@ module Submissions
composer.document.trailer.info[:Creator] = INFO_CREATOR
composer.document.sign(io, reason: SIGN_REASON,
certificate: pkcs.certificate,
key: pkcs.key,
certificate_chain: pkcs.ca_certs || [])
sign_params = {
reason: SIGN_REASON,
certificate: pkcs.certificate,
key: pkcs.key,
certificate_chain: pkcs.ca_certs || []
}
sign_params[:timestamp_handler] = Submissions::TimestampHandler.new(tsa_url:) if tsa_url
composer.document.sign(io, **sign_params)
ActiveStorage::Attachment.create!(
blob: ActiveStorage::Blob.create_and_upload!(
+15 -6
View File
@@ -29,6 +29,7 @@ module Submissions
account = submitter.submission.template.account
pkcs = Accounts.load_signing_pkcs(account)
tsa_url = Accounts.load_timeserver_url(account)
pdfs_index = build_pdfs_index(submitter)
@@ -190,7 +191,9 @@ module Submissions
submitter.submission.template_schema.map do |item|
pdf = pdfs_index[item['attachment_uuid']]
attachment = save_signed_pdf(pdf:, submitter:, pkcs:, uuid: item['attachment_uuid'], name: item['name'])
attachment = save_signed_pdf(pdf:, submitter:, pkcs:, tsa_url:,
uuid: item['attachment_uuid'],
name: item['name'])
image_pdfs << pdf if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
@@ -217,15 +220,21 @@ module Submissions
end
# rubocop:enable Metrics
def save_signed_pdf(pdf:, submitter:, pkcs:, uuid:, name:)
def save_signed_pdf(pdf:, submitter:, pkcs:, tsa_url:, uuid:, name:)
io = StringIO.new
pdf.trailer.info[:Creator] = info_creator
pdf.sign(io, reason: sign_reason(submitter.email),
certificate: pkcs.certificate,
key: pkcs.key,
certificate_chain: pkcs.ca_certs || [])
sign_params = {
reason: sign_reason(submitter.email),
certificate: pkcs.certificate,
key: pkcs.key,
certificate_chain: pkcs.ca_certs || []
}
sign_params[:timestamp_handler] = Submissions::TimestampHandler.new(tsa_url:) if tsa_url
pdf.sign(io, **sign_params)
ActiveStorage::Attachment.create!(
uuid:,
+53
View File
@@ -0,0 +1,53 @@
# frozen_string_literal: true
module Submissions
class TimestampHandler
HASH_ALGORITHM = 'SHA512'
TimestampError = Class.new(StandardError)
attr_reader :tsa_url
def initialize(tsa_url:)
@tsa_url = tsa_url
end
def finalize_objects(_signature_field, signature)
signature.document.version = '2.0'
signature[:Type] = :DocTimeStamp
signature[:Filter] = :'Adobe.PPKLite'
signature[:SubFilter] = :'ETSI.RFC3161'
end
def sign(io, byte_range)
digest = OpenSSL::Digest.new(HASH_ALGORITHM)
io.pos = byte_range[0]
digest << io.read(byte_range[1])
io.pos = byte_range[2]
digest << io.read(byte_range[3])
uri = Addressable::URI.parse(tsa_url)
conn = Faraday.new(uri.origin) do |c|
c.basic_auth(uri.user, uri.password) if uri.password.present?
end
response = conn.post(uri.path, build_payload(digest.digest),
'content-type' => 'application/timestamp-query')
raise TimestampError if response.status != 200 || response.body.blank?
OpenSSL::Timestamp::Response.new(response.body).token.to_der
end
def build_payload(digest)
req = OpenSSL::Timestamp::Request.new
req.algorithm = HASH_ALGORITHM
req.message_imprint = digest
req.to_der
end
end
end