Skip to content

Commit d9f1a7d

Browse files
committed
Extend XAdES LTA signatures
IB-7994, IB-7995 Signed-off-by: Raul Metsma <[email protected]>
1 parent efb30bc commit d9f1a7d

File tree

12 files changed

+201
-81
lines changed

12 files changed

+201
-81
lines changed

examples/DigiDocCSharp/Program.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ private static void Verify(string file)
209209
Console.WriteLine("Time: " + s.trustedSigningTime());
210210
Console.WriteLine("Cert: " + s.signingCertificate().Subject);
211211
Console.WriteLine("TimeStamp: " + s.TimeStampCertificate().Subject);
212+
foreach (TSAInfo tsaInfo in s.ArchiveTimeStamps())
213+
{
214+
Console.WriteLine("Archive Time: " + tsaInfo.time);
215+
Console.WriteLine("Archive Cert: " + tsaInfo.cert.Subject);
216+
}
212217

213218
s.validate();
214219
Console.WriteLine("Signature is valid");

examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ static void verify(String file) {
152152
System.out.println("Time: " + signature.trustedSigningTime());
153153
System.out.println("Cert: " + signature.signingCertificate().getSubjectDN().toString());
154154
System.out.println("TimeStamp Cert: " + signature.TimeStampCertificate().getSubjectDN().toString());
155+
for(TSAInfo tsaInfo : signature.ArchiveTimeStamps()) {
156+
System.out.println("Archive Time: " + tsaInfo.getTime());
157+
System.out.println("Archive Cert: " + tsaInfo.getCert().getSubjectDN().toString());
158+
}
155159

156160
try
157161
{

libdigidocpp.i

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,17 @@ static std::vector<unsigned char>* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je
102102
%{ $1 = SWIG_JavaArrayToVectorUnsignedChar(jenv, $input); %}
103103
%typemap(out, fragment="SWIG_VectorUnsignedCharToJavaArray") std::vector<unsigned char>, digidoc::X509Cert
104104
%{ $result = SWIG_VectorUnsignedCharToJavaArray(jenv, $1); %}
105-
%typemap(jtype) std::vector<unsigned char>, digidoc::X509Cert "byte[]"
105+
%typemap(out, fragment="SWIG_VectorUnsignedCharToJavaArray") digidoc::X509Cert *
106+
%{ $result = SWIG_VectorUnsignedCharToJavaArray(jenv, *$1); %}
107+
%typemap(jtype) std::vector<unsigned char>, digidoc::X509Cert, digidoc::X509Cert * "byte[]"
106108
%typemap(jstype) std::vector<unsigned char> "byte[]"
107-
%typemap(jstype) digidoc::X509Cert "java.security.cert.X509Certificate"
108-
%typemap(jni) std::vector<unsigned char>, digidoc::X509Cert "jbyteArray"
109+
%typemap(jstype) digidoc::X509Cert, digidoc::X509Cert* "java.security.cert.X509Certificate"
110+
%typemap(jni) std::vector<unsigned char>, digidoc::X509Cert, digidoc::X509Cert * "jbyteArray"
109111
%typemap(javain) std::vector<unsigned char>, digidoc::X509Cert "$javainput"
110112
%typemap(javaout) std::vector<unsigned char> {
111113
return $jnicall;
112114
}
113-
%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert {
115+
%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert, digidoc::X509Cert * {
114116
byte[] der = $jnicall;
115117
java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X509");
116118
try (java.io.ByteArrayInputStream is = new java.io.ByteArrayInputStream(der)) {
@@ -120,7 +122,7 @@ static std::vector<unsigned char>* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je
120122

121123
#elif defined(SWIGCSHARP)
122124
%typemap(cstype) std::vector<unsigned char> "byte[]"
123-
%typemap(cstype) digidoc::X509Cert "System.Security.Cryptography.X509Certificates.X509Certificate2"
125+
%typemap(cstype) digidoc::X509Cert, digidoc::X509Cert* "System.Security.Cryptography.X509Certificates.X509Certificate2"
124126
%typemap(csin, pre= " global::System.IntPtr cPtr$csinput = digidocPINVOKE.ByteVector_to($csinput, $csinput.Length);
125127
var handleRef$csinput = new global::System.Runtime.InteropServices.HandleRef($csinput, cPtr$csinput);"
126128
) std::vector<unsigned char> "handleRef$csinput"
@@ -132,6 +134,14 @@ static std::vector<unsigned char>* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je
132134
global::System.IntPtr cPtr = $imcall;$excode
133135
return new System.Security.Cryptography.X509Certificates.X509Certificate2($modulePINVOKE.To_ByteArray(cPtr));
134136
}
137+
%typemap(csvarout, excode=SWIGEXCODE2) digidoc::X509Cert * %{
138+
get {
139+
global::System.IntPtr cPtr = $imcall;$excode
140+
byte[] der = new byte[$modulePINVOKE.ByteVector_size(cPtr)];
141+
global::System.Runtime.InteropServices.Marshal.Copy($modulePINVOKE.ByteVector_data(cPtr), der, 0, der.Length);
142+
$modulePINVOKE.ByteVector_free(cPtr);
143+
return new System.Security.Cryptography.X509Certificates.X509Certificate2(der);
144+
} %}
135145
%typemap(out) std::vector<unsigned char> %{ $result = new std::vector<unsigned char>(std::move($1)); %}
136146
%typemap(out) digidoc::X509Cert %{ $result = new std::vector<unsigned char>($1); %}
137147

@@ -154,6 +164,10 @@ static std::vector<unsigned char>* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je
154164
std::vector<unsigned char> temp = $1;
155165
$result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size());
156166
}
167+
%typemap(out) digidoc::X509Cert * {
168+
std::vector<unsigned char> temp = *$1;
169+
$result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size());
170+
}
157171
#endif
158172
%typemap(freearg) std::vector<unsigned char>
159173
%{ delete $1; %}
@@ -205,6 +219,9 @@ static std::vector<unsigned char>* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je
205219
%newobject digidoc::Container::open;
206220
%newobject digidoc::Container::create;
207221

222+
%immutable digidoc::TSAInfo::cert;
223+
%immutable digidoc::TSAInfo::time;
224+
208225
%feature("director") digidoc::ContainerOpenCB;
209226

210227
%typemap(javacode) digidoc::Conf %{
@@ -262,6 +279,7 @@ def transfer(self):
262279
%template(StringMap) std::map<std::string,std::string>;
263280
%template(DataFiles) std::vector<digidoc::DataFile*>;
264281
%template(Signatures) std::vector<digidoc::Signature*>;
282+
%template(TSAInfos) std::vector<digidoc::TSAInfo>;
265283

266284
%extend digidoc::Container {
267285
static digidoc::Container* open(const std::string &path, digidoc::ContainerOpenCB *cb)

src/Signature.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,27 @@ string Signature::TimeStampTime() const { return {}; }
223223
/**
224224
* Returns signature Archive TimeStampToken certificate.
225225
*/
226-
X509Cert Signature::ArchiveTimeStampCertificate() const { return X509Cert(); }
226+
X509Cert Signature::ArchiveTimeStampCertificate() const
227+
{
228+
if(auto list = ArchiveTimeStamps(); !list.empty())
229+
return list.back().cert;
230+
return X509Cert();
231+
}
227232

228233
/**
229234
* Returns signature Archive TimeStampToken time.
230235
*/
231-
string Signature::ArchiveTimeStampTime() const { return {}; }
236+
string Signature::ArchiveTimeStampTime() const
237+
{
238+
if(auto list = ArchiveTimeStamps(); !list.empty())
239+
return list.back().time;
240+
return {};
241+
}
242+
243+
/**
244+
* Returns signature Archive TimeStampTokens.
245+
*/
246+
vector<TSAInfo> Signature::ArchiveTimeStamps() const { return {}; }
232247

233248
struct Signature::Validator::Private
234249
{

src/Signature.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@
2121

2222
#include "Exception.h"
2323

24-
#include <string>
25-
#include <vector>
24+
#include "crypto/X509Cert.h"
2625

2726
namespace digidoc
2827
{
2928
class Signer;
3029
class X509Cert;
30+
31+
struct TSAInfo {
32+
X509Cert cert;
33+
std::string time;
34+
};
35+
3136
class DIGIDOCPP_EXPORT Signature
3237
{
3338
public:
@@ -86,18 +91,18 @@ namespace digidoc
8691
virtual std::string countryName() const;
8792
virtual std::vector<std::string> signerRoles() const;
8893

89-
//TM profile properties
94+
// TM profile properties
9095
virtual std::string OCSPProducedAt() const;
9196
virtual X509Cert OCSPCertificate() const;
9297
DIGIDOCPP_DEPRECATED virtual std::vector<unsigned char> OCSPNonce() const;
9398

94-
//TS profile properties
99+
// TS profile properties
95100
virtual X509Cert TimeStampCertificate() const;
96101
virtual std::string TimeStampTime() const;
97102

98-
//TSA profile properties
99-
virtual X509Cert ArchiveTimeStampCertificate() const;
100-
virtual std::string ArchiveTimeStampTime() const;
103+
// TSA profile properties
104+
DIGIDOCPP_DEPRECATED virtual X509Cert ArchiveTimeStampCertificate() const;
105+
DIGIDOCPP_DEPRECATED virtual std::string ArchiveTimeStampTime() const;
101106

102107
// Xades properties
103108
virtual std::string streetAddress() const;
@@ -114,6 +119,9 @@ namespace digidoc
114119
// DSig properties
115120
virtual void extendSignatureProfile(Signer *signer);
116121

122+
//TSA profile properties
123+
virtual std::vector<TSAInfo> ArchiveTimeStamps() const;
124+
117125
protected:
118126
Signature();
119127

src/SignatureXAdES_B.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,9 @@ SignatureXAdES_B::SignatureXAdES_B(const shared_ptr<Signatures> &signatures, XML
335335
"AttrAuthoritiesCertValues", "AttributeRevocationValues", "ArchiveTimeStamp"})
336336
if(usp/elem)
337337
THROW("%s is not supported", elem);
338+
for(const char *elem: {"CompleteCertificateRefsV2", "AttributeCertificateRefsV2", "SigAndRefsTimeStampV2", "RefsOnlyTimeStampV2"})
339+
if(usp/XMLName{elem, XADESv141_NS})
340+
THROW("%s is not supported", elem);
338341
for(const char *elem: {"CompleteCertificateRefs", "CompleteRevocationRefs", "SigAndRefsTimeStamp", "TimeStampValidationData"})
339342
if(usp/elem)
340343
WARN("%s are not supported", elem);

src/SignatureXAdES_LTA.cpp

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace digidoc
3939
constexpr XMLName ArchiveTimeStamp {"ArchiveTimeStamp", XADESv141_NS};
4040
}
4141

42-
void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod) const
42+
void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod, XMLNode ts) const
4343
{
4444
for(auto ref = signature/"SignedInfo"/"Reference"; ref; ref++)
4545
{
@@ -64,7 +64,7 @@ void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view can
6464
if(file == files.cend())
6565
THROW("Filed to find reference URI in container");
6666

67-
static_cast<const DataFilePrivate*>(*file)->digest(digest);
67+
dynamic_cast<const DataFilePrivate*>(*file)->digest(digest);
6868
}
6969

7070
for(const auto *name: {"SignedInfo", "SignatureValue", "KeyInfo"})
@@ -75,65 +75,46 @@ void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view can
7575
DEBUG("Element %s not found", name);
7676
}
7777

78-
auto usp = unsignedSignatureProperties();
79-
for(const auto *name: {
80-
"SignatureTimeStamp",
81-
"CounterSignature",
82-
"CompleteCertificateRefs",
83-
"CompleteRevocationRefs",
84-
"AttributeCertificateRefs",
85-
"AttributeRevocationRefs",
86-
"CertificateValues",
87-
"RevocationValues",
88-
"SigAndRefsTimeStamp",
89-
"RefsOnlyTimeStamp" })
78+
for(auto elem: unsignedSignatureProperties())
9079
{
91-
if(auto elem = usp/name)
92-
signatures->c14n(digest, canonicalizationMethod, elem);
93-
else
94-
DEBUG("Element %s not found", name);
95-
}
96-
97-
if(auto elem = usp/XMLName{"TimeStampValidationData", XADESv141_NS})
80+
if(elem == ts)
81+
break;
9882
signatures->c14n(digest, canonicalizationMethod, elem);
99-
else
100-
DEBUG("Element TimeStampValidationData not found");
83+
}
10184
//ds:Object
10285
}
10386

10487
void SignatureXAdES_LTA::extendSignatureProfile(Signer *signer)
10588
{
106-
SignatureXAdES_LT::extendSignatureProfile(signer);
89+
if(SignatureXAdES_LTA::profile().find(ASiC_E::ASIC_TS_PROFILE) == string::npos)
90+
SignatureXAdES_LT::extendSignatureProfile(signer);
10791
if(signer->profile() != ASiC_E::ASIC_TSA_PROFILE)
10892
return;
93+
94+
int i = 0;
95+
for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; ts; ts++, ++i);
96+
10997
Digest calc;
11098
auto method = canonicalizationMethod();
111-
calcArchiveDigest(calc, method);
99+
calcArchiveDigest(calc, method, {});
112100

113101
TS tsa(calc, signer->userAgent());
114102
auto ts = unsignedSignatureProperties() + ArchiveTimeStamp;
115103
ts.setNS(ts.addNS(XADESv141_NS, "xades141"));
116-
ts.setProperty("Id", id() + "-A0");
104+
ts.setProperty("Id", id() + "-A" + to_string(i));
117105
(ts + CanonicalizationMethod).setProperty("Algorithm", method);
118106
ts + EncapsulatedTimeStamp = tsa;
119107
}
120108

121-
TS SignatureXAdES_LTA::tsaFromBase64() const
122-
{
123-
try {
124-
return {unsignedSignatureProperties()/ArchiveTimeStamp/EncapsulatedTimeStamp};
125-
} catch(const Exception &) {}
126-
return {};
127-
}
128-
129-
X509Cert SignatureXAdES_LTA::ArchiveTimeStampCertificate() const
130-
{
131-
return tsaFromBase64().cert();
132-
}
133-
134-
string SignatureXAdES_LTA::ArchiveTimeStampTime() const
109+
vector<TSAInfo> SignatureXAdES_LTA::ArchiveTimeStamps() const
135110
{
136-
return date::to_string(tsaFromBase64().time());
111+
vector<TSAInfo> result;
112+
for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; ts; ts++)
113+
{
114+
TS t(ts/EncapsulatedTimeStamp);
115+
result.push_back({t.cert(), util::date::to_string(t.time())});
116+
}
117+
return result;
137118
}
138119

139120
void SignatureXAdES_LTA::validate(const string &policy) const
@@ -157,9 +138,12 @@ void SignatureXAdES_LTA::validate(const string &policy) const
157138
auto ts = unsignedSignatureProperties()/ArchiveTimeStamp;
158139
if(!ts)
159140
THROW("Missing ArchiveTimeStamp element");
160-
verifyTS(ts, exception, [this](const Digest &digest, string_view canonicalizationMethod) {
161-
calcArchiveDigest(digest, canonicalizationMethod);
162-
});
141+
for(; ts; ts++)
142+
{
143+
verifyTS(ts, exception, [this, ts](const Digest &digest, string_view canonicalizationMethod) {
144+
calcArchiveDigest(digest, canonicalizationMethod, ts);
145+
});
146+
}
163147
} catch(const Exception &e) {
164148
exception.addCause(e);
165149
}

src/SignatureXAdES_LTA.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,14 @@ class SignatureXAdES_LTA final: public SignatureXAdES_LT
2929
public:
3030
using SignatureXAdES_LT::SignatureXAdES_LT;
3131

32-
X509Cert ArchiveTimeStampCertificate() const final;
33-
std::string ArchiveTimeStampTime() const final;
32+
std::vector<TSAInfo> ArchiveTimeStamps() const final;
3433
void validate(const std::string &policy) const final;
3534
void extendSignatureProfile(Signer *signer) final;
3635

3736
private:
3837
DISABLE_COPY(SignatureXAdES_LTA);
3938

40-
void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod) const;
41-
TS tsaFromBase64() const;
39+
void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod, XMLNode node) const;
4240
};
4341

4442
}

src/XMLDocument.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ struct XMLElem
141141
return bool(d);
142142
}
143143

144+
constexpr bool operator==(XMLElem other) const noexcept
145+
{
146+
return d == other.d;
147+
}
148+
144149
constexpr auto& operator++() noexcept
145150
{
146151
d = d ? find(d->next, d->type) : nullptr;

src/digidoc-tool.1.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ Command sign:
6969
--dontValidate - Don't validate container on signature creation
7070
--userAgent - Additional info info that is sent to TSA or OCSP service
7171

72+
Command extend:
73+
Example: " << executable << " extend --signature=0 demo-container.asice
74+
Available options:
75+
--profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive
76+
--signature= - signature to extend
77+
--dontValidate - Don't validate container on signature creation
78+
7279
All commands:
7380
--nocolor - Disable terminal colors
7481
--loglevel=[0,1,2,3,4] - Log level 0 - none, 1 - error, 2 - warning, 3 - info, 4 - debug

0 commit comments

Comments
 (0)