From 172a9d55990d793c78d11a5108aa21719f5288fe Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 6 Dec 2022 11:50:06 +0100 Subject: [PATCH 1/5] feat(timestamp): add certigna --- app/lib/certigna/api.rb | 41 ++++++++++++++++++++++ app/lib/certigna/authorities.crt | 58 ++++++++++++++++++++++++++++++++ config/initializers/02_urls.rb | 1 + config/secrets.yml | 2 ++ 4 files changed, 102 insertions(+) create mode 100644 app/lib/certigna/api.rb create mode 100644 app/lib/certigna/authorities.crt diff --git a/app/lib/certigna/api.rb b/app/lib/certigna/api.rb new file mode 100644 index 000000000..e1c6fb7d3 --- /dev/null +++ b/app/lib/certigna/api.rb @@ -0,0 +1,41 @@ +class Certigna::API + ## Certigna Timestamp POST API + # the CAfile used to controle the timestamp token is build: + # curl http://autorite.certigna.fr/ACcertigna.crt http://autorite.certigna.fr/entityca.crt > authorities.crt + + def self.ensure_properly_configured! + if userpwd.blank? + raise StandardError, 'Certigna API is not properly configured' + end + end + + def self.timestamp(data) + ensure_properly_configured! + + response = Typhoeus.post( + CERTIGNA_API_URL, + userpwd: userpwd, + body: body(data) + ) + + if response.success? + response.body + else + raise StandardError, "Certigna timestamp query failed: #{response.status_message}" + end + end + + private + + def self.body(data) + { + 'hashAlgorithm': 'SHA256', + 'certReq': 'true', + 'hashedMessage': data + } + end + + def self.userpwd + Rails.application.secrets.certigna[:userpwd] + end +end diff --git a/app/lib/certigna/authorities.crt b/app/lib/certigna/authorities.crt new file mode 100644 index 000000000..376fbe73b --- /dev/null +++ b/app/lib/certigna/authorities.crt @@ -0,0 +1,58 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGFTCCBP2gAwIBAgIRAPlPiLTzBzmpCdNvjtZMWaYwDQYJKoZIhvcNAQELBQAw +NDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2Vy +dGlnbmEwHhcNMTUxMTI1MTAyNDA3WhcNMjUxMTIyMTAyNDA3WjB7MQswCQYDVQQG +EwJGUjESMBAGA1UECgwJREhJTVlPVElTMRwwGgYDVQQLDBMwMDAyIDQ4MTQ2MzA4 +MTAwMDM2MR0wGwYDVQRhDBROVFJGUi00ODE0NjMwODEwMDAzNjEbMBkGA1UEAwwS +Q2VydGlnbmEgRW50aXR5IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA0lTrAwYaOJpvbeSPdj8l4DVUCP2td5ZpJ6YBETkj0DEyairmzh+QdoqJusE0 +bPu7DeivGYLaIfZP53fVi+OM8/TfiQK61B53kgwI1tL1nEhdLd7G8G7SrKs0HGSo +fk6KMWqn4t2kVkkvotuH5RzxW/TrYyF9kUrBvqcAqUigpPtNv2w6JRmEN3QpYYDj +G4Bwz4J3RMRMtq5z6T1F55hX1hFP7FcavN+q4GiJ6m7LMhwiwh7nHU71lmLhUrYH +AIrygN/eQobCnV8HiSxBm0eFtnJDXWUd7KdkdJ02E67aclV625Vwzw+0OKH20XBq +O4tO8Yy0TuJnZQGfA/tSxdfp7hxNWlXFHvkaRGTfj9y6tv9WVZrgcNbaXR4c3gjE +/GuhGDCqt32ogTKu37cHMBxCRXCgMWEAyz4tBDVT8/UZzDbyFJOe63aroZdoohI4 +XkEj50fYT6+AoEl0UoctswbPzq1PdIzXb+u8ki4FZK/2sTwCKJxifplzj9eFcv0m +cEVyYozVPAPzAo1aksK1/KWSCdHB2Odo5dhLlyiG/AmVcdA7NlQEPg6fSkJcbZV5 +kNnmkI9bgQv0iVFO9y0s1AlzfFcIrFCLS/A1UsL6+59H1ifSubehmD4ms8At83/N +jyAjEJCs7UvIGkSFe54ifSnIdB03KZaz6cnFIwPy7ThPU4kCAwEAAaOCAdkwggHV +MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSl +Px4kTGz4i9IbcphGUMrohlW52DBkBgNVHSMEXTBbgBQa7f5BOZC0JFm+AfJS1UX2 +WjncEaE4pDYwNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8G +A1UEAwwIQ2VydGlnbmGCCQD+3OMBD8lI/zBJBgNVHSAEQjBAMD4GCiqBegGBMQEA +AQIwMDAuBggrBgEFBQcCARYiaHR0cHM6Ly93d3cuY2VydGlnbmEuZnIvYXV0b3Jp +dGVzLzB8BggrBgEFBQcBAQRwMG4wNAYIKwYBBQUHMAKGKGh0dHA6Ly9hdXRvcml0 +ZS5jZXJ0aWduYS5mci9jZXJ0aWduYS5kZXIwNgYIKwYBBQUHMAKGKmh0dHA6Ly9h +dXRvcml0ZS5kaGlteW90aXMuY29tL2NlcnRpZ25hLmRlcjBhBgNVHR8EWjBYMCmg +J6AlhiNodHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hLmNybDAroCmgJ4Yl +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hLmNybDANBgkqhkiG9w0B +AQsFAAOCAQEAq05OtV0WmCyLpzxxcw98xPV8OiY56TQch4KEGotY1Dfw7H0nM7la +p/6kAZkUwfI4WJt4piRedJHgasQOj+aJbpxCYX7Hv7vCnGDKZFC6c/vY3B/stbQR +GTl//7+iKB2fgB/FoQOohrZqO77C0tqp1KuTfCSq/XAyZNtXoVn+YDaILMUHp2vs +lSHh131XPGjkloeXTMLf/+RcVVimMyAUjwdfcIUQEGU4Z5suQNWiwCU89l1UY7qa +WC9As/GqWo3vqQG9ALG+HBFBr1HSPnUjW88J6CYUVxOcTDfAj3eJtvUF/3dFjPl6 +jM4rAJNeg+LqU6kQ+z/50yhrdgJ/A8cZiQ== +-----END CERTIFICATE----- + diff --git a/config/initializers/02_urls.rb b/config/initializers/02_urls.rb index 7cb26dce0..a948be7fa 100644 --- a/config/initializers/02_urls.rb +++ b/config/initializers/02_urls.rb @@ -10,6 +10,7 @@ PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1 SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2") SENDINBLUE_API_V3_URL = ENV.fetch("SENDINBLUE_API_V3_URL", "https://api.sendinblue.com/v3") UNIVERSIGN_API_URL = ENV.fetch("UNIVERSIGN_API_URL", "https://ws.universign.eu/tsa/post/") +CERTIGNA_API_URL = ENV.fetch("CERTIGNA_API_URL", "https://timestamp.dhimyotis.com/api/v1/") FEATURE_UPVOTE_URL = ENV.fetch("FEATURE_UPVOTE_URL", "https://demarches-simplifiees.featureupvote.com") # Internal URLs diff --git a/config/secrets.yml b/config/secrets.yml index 4dc60539a..1e69adc34 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -76,6 +76,8 @@ defaults: &defaults client_key: <%= ENV['CRISP_CLIENT_KEY'] %> universign: userpwd: <%= ENV['UNIVERSIGN_USERPWD'] %> + certigna: + userpwd: <%= ENV['CERTIGNA_USERPWD'] %> autocomplete: api_geo_url: <%= ENV['API_GEO_URL'] %> api_adresse_url: <%= ENV['API_ADRESSE_URL'] %> From c085c7820f646194626a90b39748a0814daca5a1 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 6 Dec 2022 11:59:09 +0100 Subject: [PATCH 2/5] fix(spec): update spec with new timestamp --- .../files/bill_signature/serialized.json | 2 +- .../files/bill_signature/signature.der | Bin 2186 -> 5366 bytes spec/lib/asn1/timestamp_spec.rb | 4 ++-- spec/models/bill_signature_spec.rb | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/fixtures/files/bill_signature/serialized.json b/spec/fixtures/files/bill_signature/serialized.json index 87852f06f..ec1c86b79 100644 --- a/spec/fixtures/files/bill_signature/serialized.json +++ b/spec/fixtures/files/bill_signature/serialized.json @@ -1 +1 @@ -{"dossier1": "hash1", "dossier2": "hash2"} +{"1":"hash1","2":"hash2"} \ No newline at end of file diff --git a/spec/fixtures/files/bill_signature/signature.der b/spec/fixtures/files/bill_signature/signature.der index 9adcc9a7e05eee7338a8c5d92edc781a19c02da8..d4b580bfb456f4faa3221b8ac756b4698ecd47e3 100644 GIT binary patch literal 5366 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 literal 2186 zcmd5-dpJ~S7@u?I7&91Te=0A|7-tzzxVgO=ly=?_x@ghk#k6t zFLJm!V^l~LF2~600Y*-RAtI^;s3b~c4i}0gA~2*v02pLNQhy7jXhTRj2I(Ofq=P3k zVF)6rS(6|Hn($MBVSs*g8KGAh3-jZ@9zIPE;0#$ggoot++~~4kiEO?`hJL}M_^=v0 zlW>0gXm{9L-fXTSljB2E8Sv#-V+J%PKplY{3!wHazy!7|rrioa#J^X*sWKgGK<5A} z5-}Jd(&)j0gtS;uYC@cVzAPa*HdP#zoRT1jQ=W_IfXPN@0V5?;okjxyFzI$IN0jM^ zvh9=+8>IsiWmb)*?adcZw_HN% zIJxUTsQw?C`S-`n29|%aV){?4c)HVZX6=EAJ{9K|1&&M>&I4GXPi5Lq3qKvhxU9^k zvxG~Kh(TIP9jd_?1QDdpgovJzz=!jbcDU;7sqR}nPwK|bjHzEZsB0JK)*PSHUzm`a zws@DvkW1(F1s45Re(L)z-Rs2loZ>F))h9*1C+%J=F;P(BGgdsUDsA4^aH%x@L8tkH z2D}C6X||E1Y3Ht1>(9r!l3mBwS6QYLBv$4U<}s&(GbfkTGhSamL&o;Y zgF9P#T~?Xvls2igWTX>XItK7omM}%!z&zkw?pW@SWB(Q$E#iNKeSb4ToEjA& z5(>o;N?qCFP@t)-qXj`z2ttHavw)s5wot|zKx!DU+RtCH%G>aG;t zj$^5HJN9!c8iO3~#BGtwy{?6#82lr^;5OU|fn@t9^N+uGwrOhv-x4!$-+gCx6e3Ff zV2c38`y7IrA#~0f!)I!HYh)!u8=|LpMM2)@WNyx(%)$HB0-IY&^j$#GFs%Trfl8`? zQyQaIiN1g66 zc9k1T&a&HFW4}l*F(Z%d*BI$@VAQA9MvxeG`wAN9-q~Ip;4yHZv2X#HU@kAZA3*kT zuuSZc=kdl*N9;9R)M-1_P*EDaHB;PY;AWP`XAL>E@AhxIV9l6s8<`|)rS7xtG1;%3 tVzE)@hIu}rO8!l1uNJvrhITX`9=2p( Date: Tue, 6 Dec 2022 12:00:40 +0100 Subject: [PATCH 3/5] feat(timestamp): use certigna to timestamp --- app/services/bill_signature_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/bill_signature_service.rb b/app/services/bill_signature_service.rb index 7088456f9..8e2d0fe69 100644 --- a/app/services/bill_signature_service.rb +++ b/app/services/bill_signature_service.rb @@ -9,7 +9,7 @@ class BillSignatureService def self.sign_operations(operations, day) bill = BillSignature.build_with_operations(operations, day) - signature = Universign::API.timestamp(bill.digest) + signature = Certigna::API.timestamp(bill.digest) bill.set_signature(signature, day) bill.save! end From 86cda6a0b9154b128b5cd94a5b1dc5b0bd800a23 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 6 Dec 2022 15:57:04 +0100 Subject: [PATCH 4/5] fix(timestamp spec): rewind io after before_save check --- app/models/bill_signature.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/bill_signature.rb b/app/models/bill_signature.rb index d2c1cfd7a..7c61d66e4 100644 --- a/app/models/bill_signature.rb +++ b/app/models/bill_signature.rb @@ -109,7 +109,9 @@ class BillSignature < ApplicationRecord io = io_for_changes(attachment_changes[attachment]) if io.present? io.rewind - io.read + result = io.read + io.rewind + result end elsif serialized.attached? serialized.download From fe8bd159394af17af6d5dc5a5bb48bd24734b511 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 6 Dec 2022 12:06:11 +0100 Subject: [PATCH 5/5] feat(timestamp): ensure signature is openssl compatible --- app/services/bill_signature_service.rb | 33 ++++++++++++++ spec/services/bill_signature_service_spec.rb | 47 ++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/app/services/bill_signature_service.rb b/app/services/bill_signature_service.rb index 8e2d0fe69..797e5bf4b 100644 --- a/app/services/bill_signature_service.rb +++ b/app/services/bill_signature_service.rb @@ -12,5 +12,38 @@ class BillSignatureService signature = Certigna::API.timestamp(bill.digest) bill.set_signature(signature, day) bill.save! + + ensure_valid_signature(bill.reload) + rescue => error + operations.each { |o| o.update(bill_signature: nil) } + bill&.destroy + raise error + end + + def self.ensure_valid_signature(bill) + Dir.mktmpdir do |dir| + operations_path = File.join(dir, 'operations') + File.write(operations_path, bill.serialized.download, mode: 'wb') + + signature_path = File.join(dir, 'signature') + File.write(signature_path, bill.signature.download, mode: 'wb') + + authorities_path = Rails.application.config.root.join('app', 'lib', 'certigna', 'authorities.crt').to_s + + verify_cmd = "openssl ts -verify -CAfile #{authorities_path.shellescape} -data #{operations_path.shellescape} -in #{signature_path.shellescape} -token_in" + + openssl_errors = nil + openssl_output = nil + + process_status = Open3.popen3(verify_cmd) do |_stdin, stdout, stderr, wait_thr| + openssl_errors = stderr.read + openssl_output = stdout.read + wait_thr.value + end + + if !process_status.success? || openssl_output&.strip != 'Verification: OK' + raise StandardError, "openssl verification failed: #{openssl_errors}" + end + end end end diff --git a/spec/services/bill_signature_service_spec.rb b/spec/services/bill_signature_service_spec.rb index 6f4b377f0..20d8e2a01 100644 --- a/spec/services/bill_signature_service_spec.rb +++ b/spec/services/bill_signature_service_spec.rb @@ -32,4 +32,51 @@ describe BillSignatureService do it { is_expected.to eq 0 } end end + + describe ".sign_operations" do + let(:date) { Date.today } + + let(:operations_hash) { [['1', 'hash1'], ['2', 'hash2']] } + let(:operations) do + operations_hash + .map { |id, digest| DossierOperationLog.new(id:, digest:, operation: 'accepter') } + end + + let(:timestamp) { File.read('spec/fixtures/files/bill_signature/signature.der') } + + subject { BillSignatureService.sign_operations(operations, date) } + + before do + DossierOperationLog.where(id: [1, 2]).destroy_all + + expect(Certigna::API).to receive(:timestamp).and_return(timestamp) + end + + context "when everything is fine" do + it do + expect { subject }.not_to raise_error + expect(BillSignature.count).to eq(1) + end + end + + context "when the digest does not match with the pre recorded timestamp token" do + let(:operations_hash) { [['1', 'hash1'], ['2', 'hash3']] } + + it do + expect { subject }.to raise_error(/La validation a échoué : signature ne correspond pas à l’empreinte/) + expect(BillSignature.count).to eq(0) + end + end + + context "when the timestamp token cannot be verified by openssl" do + let(:timestamp) do + File.read('spec/fixtures/files/bill_signature/signature.der').tap { |s| s[-1] = 'd' } + end + + it do + expect { subject }.to raise_error(/openssl verification failed/) + expect(BillSignature.count).to eq(0) + end + end + end end