Skip to content

Commit f00cbd9

Browse files
committed
feat: finalize CAdES-BMS signing logic
1 parent 2879342 commit f00cbd9

File tree

6 files changed

+378
-8
lines changed

6 files changed

+378
-8
lines changed

src/eps2003cspif/cmssign.cpp

+304
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
/*-----------------------------------------------------------------*\
2+
*
3+
* cmssign.cpp
4+
* eps2003cspif
5+
* eps2003csp11-interface
6+
*
7+
* MIT - see LICENSE at root directory
8+
*
9+
* CREATED: 2022-12-3 12:24 PM
10+
* AUTHORS: Mohammed Elghamry <elghamry.connect[at]outlook[dot]com>
11+
*
12+
\*-----------------------------------------------------------------*/
13+
14+
#include "pch.h"
15+
16+
#include "eps2003cspif.h"
17+
#include "cmssign.h"
18+
19+
// REF: https://www.codeproject.com/Articles/1256991/The-AdES-Collection-CAdES-XAdES-PAdES-and-ASiC
20+
21+
// ==========================
22+
// ====== Declarations ======
23+
// ==========================
24+
25+
// ============================
26+
// ====== Public Methods ======
27+
// ============================
28+
29+
// ------------------------------------------------------
30+
//
31+
// Create CAdES-BES message for content with one signer.
32+
//
33+
// ------------------------------------------------------
34+
HRESULT CreateCadesBesSignedMessage(BYTE *pbContent, DWORD cbContent, PCCERT_CONTEXT pCert, BYTE **ppbEncodedBlob, DWORD *pcbEncodedBlob)
35+
{
36+
HRESULT hr{ S_OK };
37+
38+
asn_enc_rval_t asnEncodingResult{};
39+
40+
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey{ 0 };
41+
DWORD dwPrivateKeySpec{ 0 };
42+
BOOL bShouldCallerFreePrivateKeyHandle{ FALSE };
43+
44+
FILETIME signingTime{ 0 };
45+
BYTE *pbEncodedSigningTime{ nullptr };
46+
DWORD cbEncodedSigningTime{ 0 };
47+
CRYPT_ATTR_BLOB attrBlobSigningTime{ 0 };
48+
49+
BYTE *pbCertHash{ nullptr };
50+
DWORD cbCertHash{ 0 };
51+
52+
ESSCertIDv2 essCert{};
53+
ESSCertIDv2 *essCerts[]{ &essCert };
54+
SigningCertificateV2 signingCert{};
55+
std::vector<BYTE> vecEncodedSigningCert{};
56+
CRYPT_ATTR_BLOB attrBlobSigningCert{};
57+
58+
// Holding attributes for the message
59+
CRYPT_ATTRIBUTE messageAttributes[2]{};
60+
61+
// Encoded OID of sha256. "2.16.840.1.101.3.4.2.1"
62+
BYTE oid_sha256_bytes[]{ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 };
63+
OBJECT_IDENTIFIER_t oid_sha256{};
64+
AlgorithmIdentifier algid_sha256{};
65+
oid_sha256.buf = oid_sha256_bytes;
66+
oid_sha256.size = sizeof(oid_sha256_bytes);
67+
algid_sha256.algorithm = oid_sha256;
68+
69+
// Holding information about the certificate
70+
CERT_BLOB certsEncoded[1]{};
71+
certsEncoded[0].cbData = pCert->cbCertEncoded;
72+
certsEncoded[0].pbData = pCert->pbCertEncoded;
73+
74+
// Holding the information about signers -which is one in our case-
75+
CMSG_SIGNER_ENCODE_INFO msgSigners[1]{};
76+
77+
// Holding the information about message signing CMSG_SIGNED
78+
CMSG_SIGNED_ENCODE_INFO msgInfo{};
79+
80+
// Here is where we store our encoded message
81+
BYTE *pbEncodedBlob{ nullptr };
82+
DWORD cbEncodedBlob{ 0 };
83+
84+
// Handle for the opened message
85+
HCRYPTMSG hMsg{ nullptr };
86+
87+
// ======
88+
// ====== Get Certificate Private Key
89+
// ======
90+
91+
if (!CryptAcquireCertificatePrivateKey(
92+
pCert,
93+
0,
94+
nullptr,
95+
&hPrivateKey,
96+
&dwPrivateKeySpec,
97+
&bShouldCallerFreePrivateKeyHandle))
98+
{
99+
hr = HRESULT_FROM_WIN32(GetLastError());
100+
_RPT1(_CRT_WARN, "CryptAcquireCertificatePrivateKey() failed with HRESULT 0x%x\n", hr);
101+
goto done;
102+
}
103+
104+
// ======
105+
// ====== Create PKCS#9 Signing Time Attribute
106+
// ======
107+
108+
GetSystemTimeAsFileTime(&signingTime);
109+
110+
if (!CryptEncodeObjectEx(
111+
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
112+
szOID_RSA_signingTime,
113+
&signingTime,
114+
CRYPT_ENCODE_ALLOC_FLAG,
115+
nullptr,
116+
&pbEncodedSigningTime,
117+
&cbEncodedSigningTime))
118+
{
119+
hr = HRESULT_FROM_WIN32(GetLastError());
120+
_RPT1(_CRT_WARN, "CryptEncodeObjectEx() failed with HRESULT 0x%x\n", hr);
121+
goto done;
122+
}
123+
124+
attrBlobSigningTime.cbData = cbEncodedSigningTime;
125+
attrBlobSigningTime.pbData = pbEncodedSigningTime;
126+
127+
messageAttributes[0].pszObjId = const_cast<LPSTR>(szOID_RSA_signingTime);
128+
messageAttributes[0].cValue = 1;
129+
messageAttributes[0].rgValue = &attrBlobSigningTime;
130+
131+
// ======
132+
// ====== Create SigningCertificateV2
133+
// ======
134+
135+
// === Hash the certificate
136+
hr = SHA256(pCert->pbCertEncoded, pCert->cbCertEncoded, &pbCertHash, &cbCertHash);
137+
if (FAILED(hr)) { goto done; }
138+
139+
// === Set the fields on ESSCertIDv2
140+
essCert.hashAlgorithm = &algid_sha256;
141+
essCert.certHash.buf = pbCertHash;
142+
essCert.certHash.size = cbCertHash;
143+
144+
// === Set the fields on SigningCertificateV2
145+
signingCert.certs.list.size = 1;
146+
signingCert.certs.list.count = 1;
147+
signingCert.certs.list.array = essCerts;
148+
149+
// === Encode the object
150+
asnEncodingResult = der_encode(
151+
&asn_DEF_SigningCertificateV2,
152+
&signingCert,
153+
[](const void *buffer, size_t size, void *app_key)
154+
{
155+
std::vector<BYTE> *dest = static_cast<std::vector<BYTE> *>(app_key);
156+
dest->resize(size);
157+
std::memcpy(dest->data(), buffer, size);
158+
return 0;
159+
},
160+
&vecEncodedSigningCert);
161+
if (asnEncodingResult.encoded == -1)
162+
{
163+
// Error occurred during encoding
164+
hr = EPSIF_E_ENCODING_FAILED;
165+
_RPT0(_CRT_WARN, "der_encode() failed.\n");
166+
goto done;
167+
}
168+
169+
// === Set the attribute
170+
attrBlobSigningCert.cbData = static_cast<DWORD>(vecEncodedSigningCert.size());
171+
attrBlobSigningCert.pbData = vecEncodedSigningCert.data();
172+
173+
messageAttributes[1].pszObjId = const_cast<LPSTR>("1.2.840.113549.1.9.16.2.47"); // PKCS#9, S/MIME, SigningCertificateV2
174+
messageAttributes[1].cValue = 1;
175+
messageAttributes[1].rgValue = &attrBlobSigningCert;
176+
177+
// ======
178+
// ====== Prepare signer info
179+
// ======
180+
181+
msgSigners[0].cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
182+
msgSigners[0].pCertInfo = pCert->pCertInfo;
183+
msgSigners[0].hCryptProv = hPrivateKey;
184+
msgSigners[0].dwKeySpec = dwPrivateKeySpec;
185+
msgSigners[0].HashAlgorithm.pszObjId = const_cast<LPSTR>(szOID_NIST_sha256);
186+
msgSigners[0].cAuthAttr = sizeof(messageAttributes);
187+
msgSigners[0].rgAuthAttr = messageAttributes;
188+
189+
// ======
190+
// ====== Prepare message info
191+
// ======
192+
193+
msgInfo.cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO);
194+
msgInfo.cSigners = sizeof(msgSigners);
195+
msgInfo.rgSigners = msgSigners;
196+
msgInfo.cCertEncoded = sizeof(certsEncoded);
197+
msgInfo.rgCertEncoded = certsEncoded;
198+
199+
// ======
200+
// ====== Calculate message size
201+
// ======
202+
203+
cbEncodedBlob = CryptMsgCalculateEncodedLength(
204+
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
205+
CMSG_DETACHED_FLAG,
206+
CMSG_SIGNED,
207+
&msgInfo,
208+
const_cast<LPSTR>(szOID_RSA_digestedData),
209+
cbContent);
210+
if (!cbEncodedBlob)
211+
{
212+
hr = HRESULT_FROM_WIN32(GetLastError());
213+
_RPT1(_CRT_WARN, "CryptMsgCalculateEncodedLength() failed with HRESULT 0x%x\n", hr);
214+
goto done;
215+
}
216+
217+
// ======
218+
// ====== Allocate memory for encoded message
219+
// ======
220+
221+
pbEncodedBlob = new(std::nothrow) BYTE[cbEncodedBlob];
222+
if (!pbEncodedBlob)
223+
{
224+
hr = E_OUTOFMEMORY;
225+
goto done;
226+
}
227+
228+
// ======
229+
// ====== Open the message for encoding
230+
// ======
231+
232+
hMsg = CryptMsgOpenToEncode(
233+
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
234+
CMSG_DETACHED_FLAG,
235+
CMSG_SIGNED,
236+
&msgInfo,
237+
const_cast<LPSTR>(szOID_RSA_digestedData),
238+
nullptr);
239+
if (!hMsg)
240+
{
241+
hr = HRESULT_FROM_WIN32(GetLastError());
242+
_RPT1(_CRT_WARN, "CryptMsgOpenToEncode() failed with HRESULT 0x%x\n", hr);
243+
goto done;
244+
}
245+
246+
// ======
247+
// ====== Update the message with our content
248+
// ======
249+
250+
if (!CryptMsgUpdate(hMsg, pbContent, cbContent, TRUE))
251+
{
252+
hr = HRESULT_FROM_WIN32(GetLastError());
253+
_RPT1(_CRT_WARN, "CryptMsgUpdate() failed with HRESULT 0x%x\n", hr);
254+
goto done;
255+
}
256+
257+
// ======
258+
// ====== Get the encoded message
259+
// ======
260+
261+
if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pbEncodedBlob, &cbEncodedBlob))
262+
{
263+
hr = HRESULT_FROM_WIN32(GetLastError());
264+
_RPT1(_CRT_WARN, "CryptMsgGetParam() failed with HRESULT 0x%x\n", hr);
265+
goto done;
266+
}
267+
268+
// ======
269+
// ====== Finally, return the data to the caller
270+
// ======
271+
272+
(*ppbEncodedBlob) = pbEncodedBlob;
273+
(*pcbEncodedBlob) = cbEncodedBlob;
274+
275+
done:
276+
if (bShouldCallerFreePrivateKeyHandle)
277+
{
278+
if (dwPrivateKeySpec == CERT_NCRYPT_KEY_SPEC)
279+
{
280+
NCryptFreeObject(hPrivateKey);
281+
}
282+
else
283+
{
284+
CryptReleaseContext(hPrivateKey, 0);
285+
}
286+
}
287+
288+
if (pbEncodedSigningTime)
289+
{
290+
LocalFree(pbEncodedSigningTime);
291+
}
292+
293+
if (pbCertHash)
294+
{
295+
delete[] pbCertHash;
296+
}
297+
298+
if (hMsg)
299+
{
300+
CryptMsgClose(hMsg);
301+
}
302+
303+
return hr;
304+
}

src/eps2003cspif/cmssign.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*-----------------------------------------------------------------*\
2+
*
3+
* cmssign.h
4+
* eps2003cspif
5+
* eps2003csp11-interface
6+
*
7+
* MIT - see LICENSE at root directory
8+
*
9+
* CREATED: 2022-12-3 12:26 PM
10+
* AUTHORS: Mohammed Elghamry <elghamry.connect[at]outlook[dot]com>
11+
*
12+
\*-----------------------------------------------------------------*/
13+
14+
#ifndef EPS2003CSPIF_CMSSIGN_H
15+
#define EPS2003CSPIF_CMSSIGN_H
16+
17+
// ------------------------------------------------------
18+
// Create CAdES-BES message for content with one signer.
19+
// ------------------------------------------------------
20+
HRESULT CreateCadesBesSignedMessage(
21+
BYTE *pbContent,
22+
DWORD cbContent,
23+
PCCERT_CONTEXT pCert,
24+
BYTE **ppbEncodedBlob,
25+
DWORD *pcbEncodedBlob);
26+
27+
#endif //EPS2003CSPIF_CMSSIGN_H

0 commit comments

Comments
 (0)