1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
/*
* Copyright (c) 2023 Vaughn Nugent
*
* Package: PkiAuthenticator
* File: Program.cs
*
* PkiAuthenticator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License,
* or (at your option) any later version.
*
* PkiAuthenticator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PkiAuthenticator. If not, see http://www.gnu.org/licenses/.
*/
using System;
using VNLib.Utils.Logging;
using static PkiAuthenticator.Statics;
namespace PkiAuthenticator
{
internal sealed class Program
{
public const string JWK_EXPORT_TEMPLATE = "You may copy your JWK public key\n\n{pk}\n";
public const string TOKEN_PRINT_TEMPLATE = "You may copy your authentication token \n\n{tk}\n";
public const string YUBIKEY_PIN_ENV_VAR_NAME = "YUBIKEY_PIN";
public const string SOFTWARE_PASSWORD_VAR_NAME = "CERT_PASSWORD";
public const string PEM_EXPORT_TEMPLATE = "You may copy your public key\n\n{cert}\n";
const string HELP_MESSAGE = @$"
vauth Copyright © Vaughn Nugent <vnpublic@proton.me> https://www.vaughnnugent.com/resources/software
This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to
redistribute it under certain conditions. See the license.txt file for more details.
Usage: vauth <flags>
A cross-platform hardware (YubiKey) or software backed authenticator for generating short lived
OTPs for VNLib.Plugins.Essentials.Accounts enabled servers. This tool generates a signed Json Web
Token (JWT) that can be used as a single factor authentication method for accounts that have a stored
public key. Currently the plugin requires JSON Web Keys (JWK) format for public keys. It requires
serial numbers, key-ids, and the public key itself, x509 is not used. You may use the --export
flag to export this public key in the required JWK format. This tool currently supports YubiKey
as a hardware authenticator, and PEM encoded x509 certificates as a software authenticator. You
may use this tool to list your connected YubiKey devices, and their serial numbers.
Command flags:
none/default Genereates a signed OTP (one time password) Json Web Token
for authentication.
-e, --export <pem> Writes the public key to the screen as a JWK, or optionally
PEM encoded by using the 'pem' keyword following -e.
--list-devices Lists the device information of all connected YubiKey devices.
(Ignores the --key flag)
-h, --help Prints this help message.
Global flags:
-u, --user <uid> The user-id (or email address) to specify during for
authenticating. If not specified, uses the certificates CN
subject value.
--software <cert file> Runs the process using a software authenticator instead of
a YubiKey hardware authenticator. The cert file must be a
a valid x509 certificate with the public key. You must also
set the private key file path.
--private-key <file> The path to the private key file, may be password protected.
This flag is only required in software mode.
--password <password?> Set this flag if your private key is password protected.
The password string (utf8 decoded) used to decrypt the PEM
private key file. WARNING! You should avoid setting your password
after this flag unless you have cli history disabled, otherwise
your password may be recovered from your shell history file. This
allows you to automate the authentication process. NOTE: consider
setting the {SOFTWARE_PASSWORD_VAR_NAME} environment variable before
starting the process instead of supplying the password as a flag.
--key <serial> Allows you to specify the serial number (int32) of the exact
YubiKey to connect to if multiple keys are connected. (PIV must
be enabled on the device)
--pin <device pin> Allows you to specify your device's pin as an argument.
WARNING! You should avoid using this flag unless you have cli
history disabled, otherwise your pin may be recovered from your
history file. This allows you to automate the authentication
process. NOTE: consider setting the {YUBIKEY_PIN_ENV_VAR_NAME} environment
variable before starting the process instead.
--piv-slot <slot> The hexadecimal YubiKey PIV slot number override to use, defaults
to authentication (9a) slot.
-s, --silent Silences logs, only operation output is written to STDOUT. For pin-
required operations, a --pin flag must be set, or set the {YUBIKEY_PIN_ENV_VAR_NAME}
env variable. If an op error occurs, an exit code is returned.
-v, --verbose Enables verbose logging to be written to STDOUT, is overridden
by silent mode, and will override -d debug mode.
-d, --debug Enables debug logging to be written to STDOUT, is overridden by
silent mode.
Environment Variables
{SOFTWARE_PASSWORD_VAR_NAME} The password used to decrypt the PEM encoded private key file in software mode
{YUBIKEY_PIN_ENV_VAR_NAME} Your secret pin used for protected yubikey operations
This tool was created to quickly generate short lived One-Time-Passwords (OTP) or signed
authentication tokens (JWT) for authenticating aginst PKI endpoints, using your YubiKey's
authentication slot (0x9a). You may use this tool to automate a login process by using the
-s flag and specifying a pin with --pin (not recommended!), or setting the {YUBIKEY_PIN_ENV_VAR_NAME}
environment variable.
A software, x509 certificate file backed, mode is also supported by using the --software flag.
The certificate file must be a PEM encoded certificate. You must also specify a PEM encoded private
key file using the --private-key flag. This file may be encrypted, and you must specify a --password
flag. You may wait for a prompt, set the {SOFTWARE_PASSWORD_VAR_NAME} environment variable, or write it after
the --password argument: '--password my_unsecure_password'.
Examples:
OTP:
vauth.exe # default cert CN usename
vauth.exe -u 'name@example.com' # specify username
vauth.exe --key 1111111 # specify hardware key serial numer
vauth.exe -s > token.txt # write token to a text file w/ silent mode
vauth.exe --piv-slot 9C # specify a differnt PIV slot on the yubikey (in hex)
#software mode
vauth.exe --software 'cert.pem' --private-key 'priv.pem'
vauth.exe --software 'cert.pem' --private-key 'priv.pem' --password 'mypassword'
Export public key:
vauth.exe --export # for JWK output
vauth.exe --export pem # for pem encoding
#software
vauth.exe --software cert.pem --export pem
List devices:
vauth.exe --list-devices # only supported in hardware mode
";
static int Main(string[] args)
{
if (CliArgs.HasArg("-h") || CliArgs.HasArg("--help"))
{
Console.WriteLine(HELP_MESSAGE);
return 0;
}
Log.Information("vauth © 2023 Vaughn Nugent");
int exitCode = 1;
try
{
//Get software or hardware authenticator
using IAuthenticator authenticator = CliArgs.HasArg("--software") ? new SoftwareAuthenticator() : new HardwareAuthenticator();
//initialze the authenticator
if (authenticator.Initialize())
{
//Only continue if authenticator successfully initialized
if (CliArgs.HasArg("--list-devices"))
{
Log.Verbose("Gathering device information");
//List devices flag
exitCode = authenticator.ListDevices();
}
else if (CliArgs.HasArg("-e") || CliArgs.HasArg("--export"))
{
Log.Verbose("Exporting public key");
//Check for pem encoding flag
if (CliArgs.HasArg("pem"))
{
string pem = authenticator.ExportPem();
Log.Information(PEM_EXPORT_TEMPLATE, pem);
exitCode = 0;
}
else
{
//Print jwk
string? pupKey = authenticator.ExportJwk();
//May be null if the alg is not supported
if (pupKey == null)
{
Log.Error("The certificate does not use a supported algorithm");
}
else
{
//Print
Log.Information(JWK_EXPORT_TEMPLATE, pupKey);
exitCode = 0;
}
}
}
else
{
//Authenticate
exitCode = authenticator.GenerateOtp();
}
}
}
catch(Exception ex)
{
if (Log.IsEnabled(LogLevel.Debug))
{
Log.Error(ex);
}
else
{
Log.Error("Operation failed. Reason: {ex}", ex.Message);
}
}
Log.Verbose("Exiting...");
return exitCode;
}
}
}
|