Skip to content

Commit 355f496

Browse files
committed
feat: add sign tool implementation
1 parent bbbeab1 commit 355f496

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed

eTokenSign2.cpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
Copyright 2018, panagenda
3+
4+
Except where otherwise noted, this work is licensed under https://creativecommons.org/licenses/by-sa/4.0
5+
It is a derivative work based on code from "draketb" at https://stackoverflow.com/a/47894907
6+
_________________
7+
8+
Blog post with some background can be found at https://www.panagenda.com/blog/ev-code-signing-with-ci-cd/
9+
*/
10+
11+
12+
#include <windows.h>
13+
#include <cryptuiapi.h>
14+
#include <iostream>
15+
#include <string>
16+
#include <vector>
17+
#include <ctime>
18+
#include <functional>
19+
#pragma comment (lib, "cryptui.lib")
20+
#pragma comment (lib, "crypt32.lib")
21+
22+
template<typename T>
23+
class CustomAutoHandle
24+
{
25+
private:
26+
T m_handle;
27+
std::function<void(T&)> m_deleter;
28+
public:
29+
operator bool(void) const
30+
{
31+
return (m_handle != NULL) && (m_handle != INVALID_HANDLE_VALUE);
32+
}
33+
operator T(void) const
34+
{
35+
return m_handle;
36+
}
37+
public:
38+
CustomAutoHandle(T handle, std::function<void(T&)> f_deleter)
39+
: m_handle(handle), m_deleter(f_deleter)
40+
{
41+
}
42+
~CustomAutoHandle(void)
43+
{
44+
if (operator bool())
45+
{
46+
T Handle = m_handle;
47+
m_handle = NULL;
48+
m_deleter(Handle);
49+
}//if
50+
}
51+
};//template CustomAutoHandle
52+
53+
const std::wstring ETOKEN_BASE_CRYPT_PROV_NAME = L"eToken Base Cryptographic Provider";
54+
55+
std::string utf16_to_utf8(const std::wstring& str)
56+
{
57+
if (str.empty())
58+
{
59+
return "";
60+
}
61+
62+
auto utf8len = ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), NULL, 0, NULL, NULL);
63+
if (utf8len == 0)
64+
{
65+
return "";
66+
}
67+
68+
std::string utf8Str;
69+
utf8Str.resize(utf8len);
70+
::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &utf8Str[0], utf8Str.size(), NULL, NULL);
71+
72+
return utf8Str;
73+
}
74+
75+
struct CryptProvHandle
76+
{
77+
HCRYPTPROV Handle = NULL;
78+
CryptProvHandle(HCRYPTPROV handle = NULL) : Handle(handle) {}
79+
~CryptProvHandle() { if (Handle) ::CryptReleaseContext(Handle, 0); }
80+
};
81+
82+
bool token_logon(const std::wstring& tokenNum, const std::string& tokenPin)
83+
{
84+
CryptProvHandle cryptProv;
85+
std::wstring tokenName = L"\\\\.\\AKS ifdh " + tokenNum;
86+
if (!::CryptAcquireContext(&cryptProv.Handle, tokenName.c_str(), ETOKEN_BASE_CRYPT_PROV_NAME.c_str(), PROV_RSA_FULL, CRYPT_SILENT))
87+
{
88+
std::wcerr << L"CryptAcquireContext failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
89+
return NULL;
90+
}
91+
92+
if (!::CryptSetProvParam(cryptProv.Handle, PP_SIGNATURE_PIN, reinterpret_cast<const BYTE*>(tokenPin.c_str()), 0))
93+
{
94+
std::wcerr << L"CryptSetProvParam failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
95+
return NULL;
96+
}
97+
98+
bool result = cryptProv.Handle != NULL;
99+
cryptProv.Handle = NULL;
100+
return result;
101+
}
102+
103+
int wmain(int argc, wchar_t** argv)
104+
{
105+
if (argc < 5)
106+
{
107+
std::wcerr << L"usage: etokensign.exe <certificate name> <token PIN> <timestamp URL> <path to file to sign>\n";
108+
std::wcerr << L"(C) 2018 panagenda GmbH\n";
109+
return 1;
110+
}
111+
112+
const std::wstring certName = argv[1];
113+
const std::wstring tokenPin = argv[2];
114+
const std::wstring timestampUrl = argv[3];
115+
const std::wstring fileToSign = argv[4];
116+
const std::wstring tokenNumber = L"0";
117+
118+
if (!token_logon(tokenNumber, utf16_to_utf8(tokenPin)))
119+
{
120+
return 1;
121+
}
122+
123+
//-------------------------------------------------------------------
124+
// Declare and initialize variables.
125+
PCCERT_CONTEXT pDesiredCert = NULL; // Set to NULL for the first call to CertFindCertificateInStore.
126+
127+
//-------------------------------------------------------------------
128+
// Open the certificate store to be searched.
129+
130+
CustomAutoHandle<HCERTSTORE> hSystemStore(
131+
CertOpenStore(
132+
CERT_STORE_PROV_SYSTEM,
133+
0, // Encoding type not needed with this PROV.
134+
NULL, // Accept the default HCRYPTPROV.
135+
CERT_SYSTEM_STORE_CURRENT_USER, // Set the system store location in the registry.
136+
L"MY" // Could have used other predefined system stores including Trust, CA, or Root.
137+
),
138+
[] (HCERTSTORE& h_cs) {CertCloseStore(h_cs, CERT_CLOSE_STORE_CHECK_FLAG);}
139+
);
140+
if (!hSystemStore)
141+
{
142+
std::wcerr << L"Could not open the MY system store.\n";
143+
return 1;
144+
}
145+
146+
bool bFound = false;
147+
DWORD cbSize = 0;
148+
while (!bFound && (pDesiredCert = CertEnumCertificatesInStore(hSystemStore, pDesiredCert)))
149+
{
150+
if (!(cbSize = CertGetNameString(pDesiredCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0)))
151+
{
152+
std::wcerr << L"Error on getting name size. Continue with next certificate.\n";
153+
continue;
154+
}
155+
std::vector<TCHAR> pszName(cbSize);
156+
if (!CertGetNameString(pDesiredCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, &pszName[0], cbSize))
157+
{
158+
std::wcerr << L"Error on getting name. Continue with next certificate.\n";
159+
continue;
160+
}
161+
if (certName.compare(&pszName[0]) == 0)
162+
{
163+
bFound = true;
164+
break;
165+
}
166+
}
167+
if (!bFound)
168+
{
169+
std::wcerr << L"No matching certificate to sign found\n";
170+
return 1;
171+
}
172+
173+
CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO extInfo = {};
174+
extInfo.dwSize = sizeof(extInfo);
175+
extInfo.pszHashAlg = szOID_NIST_sha256; // Use SHA256 instead of default SHA1
176+
177+
CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo = {};
178+
signInfo.dwSize = sizeof(signInfo);
179+
signInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
180+
signInfo.pwszFileName = fileToSign.c_str();
181+
signInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT;
182+
signInfo.pSigningCertContext = pDesiredCert;
183+
signInfo.pwszTimestampURL = timestampUrl.c_str();
184+
signInfo.pSignExtInfo = &extInfo;
185+
186+
int rv = 0;
187+
if (!::CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL))
188+
{
189+
std::wcerr << L"CryptUIWizDigitalSign failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
190+
rv = 1;
191+
}
192+
else
193+
{
194+
std::wcout << L"Successfully signed " << fileToSign << L"\n";
195+
}
196+
197+
//-------------------------------------------------------------------
198+
// Clean up.
199+
if (pDesiredCert)
200+
{
201+
CertFreeCertificateContext(pDesiredCert);
202+
}
203+
204+
return rv;
205+
}

eTokenSign2.vcxproj

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup Label="ProjectConfigurations">
4+
<ProjectConfiguration Include="Release|Win32">
5+
<Configuration>Release</Configuration>
6+
<Platform>Win32</Platform>
7+
</ProjectConfiguration>
8+
</ItemGroup>
9+
<PropertyGroup Label="Globals">
10+
<VCProjectVersion>15.0</VCProjectVersion>
11+
<ProjectGuid>{37DB9351-7658-49F7-83F4-29A4EF161C13}</ProjectGuid>
12+
<Keyword>Win32Proj</Keyword>
13+
<RootNamespace>eTokenSign2</RootNamespace>
14+
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
15+
</PropertyGroup>
16+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
17+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
18+
<ConfigurationType>Application</ConfigurationType>
19+
<UseDebugLibraries>false</UseDebugLibraries>
20+
<PlatformToolset>v141</PlatformToolset>
21+
<WholeProgramOptimization>true</WholeProgramOptimization>
22+
<CharacterSet>Unicode</CharacterSet>
23+
</PropertyGroup>
24+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
25+
<ImportGroup Label="ExtensionSettings">
26+
</ImportGroup>
27+
<ImportGroup Label="Shared">
28+
</ImportGroup>
29+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
30+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
31+
</ImportGroup>
32+
<PropertyGroup Label="UserMacros" />
33+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
34+
<LinkIncremental>false</LinkIncremental>
35+
</PropertyGroup>
36+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
37+
<ClCompile>
38+
<WarningLevel>Level3</WarningLevel>
39+
<Optimization>MaxSpeed</Optimization>
40+
<FunctionLevelLinking>true</FunctionLevelLinking>
41+
<IntrinsicFunctions>true</IntrinsicFunctions>
42+
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
43+
<ConformanceMode>true</ConformanceMode>
44+
</ClCompile>
45+
<Link>
46+
<EnableCOMDATFolding>true</EnableCOMDATFolding>
47+
<OptimizeReferences>true</OptimizeReferences>
48+
<GenerateDebugInformation>true</GenerateDebugInformation>
49+
<SubSystem>Console</SubSystem>
50+
</Link>
51+
</ItemDefinitionGroup>
52+
<ItemGroup>
53+
<ClCompile Include="eTokenSign2.cpp" />
54+
</ItemGroup>
55+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
56+
<ImportGroup Label="ExtensionTargets">
57+
</ImportGroup>
58+
</Project>

0 commit comments

Comments
 (0)