Skip to content

Commit fb229ce

Browse files
authored
Add x509 support to basic pub sub (#132)
* Add x509 support to basic pub sub * Submodule update
1 parent e25eebf commit fb229ce

File tree

2 files changed

+177
-6
lines changed

2 files changed

+177
-6
lines changed

samples/mqtt/basic_pub_sub/main.cpp

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
#include <aws/crt/Api.h>
66
#include <aws/crt/StlAllocator.h>
7+
#include <aws/crt/auth/Credentials.h>
8+
#include <aws/crt/io/TlsOptions.h>
79

810
#include <aws/iot/MqttClient.h>
911

@@ -22,7 +24,9 @@ static void s_printHelp()
2224
stdout,
2325
"basic-pub-sub --endpoint <endpoint> --cert <path to cert>"
2426
" --key <path to key> --topic --ca_file <optional: path to custom ca>"
25-
" --use_websocket --signing_region <region> --proxy_host <host> --proxy_port <port>\n\n");
27+
" --use_websocket --signing_region <region> --proxy_host <host> --proxy_port <port>"
28+
" --x509 --x509_role_alias <role_alias> --x509_endpoint <endpoint> --x509_thing <thing_name>"
29+
" --x509_cert <path to cert> --x509_key <path to key> --x509_rootca <path to root ca>\n\n");
2630
fprintf(stdout, "endpoint: the endpoint of the mqtt server not including a port\n");
2731
fprintf(
2832
stdout,
@@ -42,7 +46,21 @@ static void s_printHelp()
4246
"websockets)\n");
4347
fprintf(stdout, "proxy_host: if you want to use a proxy with websockets, specify the host here (optional).\n");
4448
fprintf(
45-
stdout, "proxy_port: defaults to 8080 is proxy_host is set. Set this to any value you'd like (optional).\n\n");
49+
stdout, "proxy_port: defaults to 8080 is proxy_host is set. Set this to any value you'd like (optional).\n");
50+
51+
fprintf(stdout, " x509: Use the x509 credentials provider while using websockets (optional)\n");
52+
fprintf(stdout, " x509_role_alias: Role alias to use with the x509 credentials provider (required for x509)\n");
53+
fprintf(stdout, " x509_endpoint: Endpoint to fetch x509 credentials from (required for x509)\n");
54+
fprintf(stdout, " x509_thing: Thing name to fetch x509 credentials on behalf of (required for x509)\n");
55+
fprintf(
56+
stdout,
57+
" x509_cert: Path to the IoT thing certificate used in fetching x509 credentials (required for x509)\n");
58+
fprintf(
59+
stdout,
60+
" x509_key: Path to the IoT thing private key used in fetching x509 credentials (required for x509)\n");
61+
fprintf(
62+
stdout,
63+
" x509_rootca: Path to the root certificate used in fetching x509 credentials (required for x509)\n\n");
4664
}
4765

4866
bool s_cmdOptionExists(char **begin, char **end, const String &option)
@@ -79,13 +97,21 @@ int main(int argc, char *argv[])
7997
String proxyHost;
8098
uint16_t proxyPort(8080);
8199

100+
String x509Endpoint;
101+
String x509ThingName;
102+
String x509RoleAlias;
103+
String x509CertificatePath;
104+
String x509KeyPath;
105+
String x509RootCAFile;
106+
82107
bool useWebSocket = false;
108+
bool useX509 = false;
83109

84110
/*********************** Parse Arguments ***************************/
85111
if (!(s_cmdOptionExists(argv, argv + argc, "--endpoint") && s_cmdOptionExists(argv, argv + argc, "--topic")))
86112
{
87113
s_printHelp();
88-
return 0;
114+
return 1;
89115
}
90116

91117
endpoint = s_getCmdOption(argv, argv + argc, "--endpoint");
@@ -100,6 +126,13 @@ int main(int argc, char *argv[])
100126
certificatePath = s_getCmdOption(argv, argv + argc, "--cert");
101127
}
102128

129+
if (keyPath.empty() != certificatePath.empty())
130+
{
131+
fprintf(stdout, "Using mtls (cert and key) requires both the certificate and the private key\n");
132+
s_printHelp();
133+
return 1;
134+
}
135+
103136
topic = s_getCmdOption(argv, argv + argc, "--topic");
104137
if (s_cmdOptionExists(argv, argv + argc, "--ca_file"))
105138
{
@@ -109,11 +142,14 @@ int main(int argc, char *argv[])
109142
{
110143
clientId = s_getCmdOption(argv, argv + argc, "--client_id");
111144
}
145+
112146
if (s_cmdOptionExists(argv, argv + argc, "--use_websocket"))
113147
{
114148
if (!s_cmdOptionExists(argv, argv + argc, "--signing_region"))
115149
{
150+
fprintf(stdout, "Websockets require a signing region to be specified.\n");
116151
s_printHelp();
152+
return 1;
117153
}
118154
useWebSocket = true;
119155
signingRegion = s_getCmdOption(argv, argv + argc, "--signing_region");
@@ -129,6 +165,82 @@ int main(int argc, char *argv[])
129165
}
130166
}
131167

168+
bool usingMtls = !certificatePath.empty() && !keyPath.empty();
169+
170+
/* one or the other, but not both nor neither */
171+
if (useWebSocket == usingMtls)
172+
{
173+
if (useWebSocket && usingMtls)
174+
{
175+
fprintf(stdout, "You must use either websockets or mtls for authentication, but not both.\n");
176+
}
177+
else
178+
{
179+
fprintf(stdout, "You must use either websockets or mtls for authentication.\n");
180+
}
181+
182+
s_printHelp();
183+
return 1;
184+
}
185+
186+
// x509 credentials provider configuration
187+
if (s_cmdOptionExists(argv, argv + argc, "--x509"))
188+
{
189+
if (!useWebSocket)
190+
{
191+
fprintf(stdout, "X509 credentials sourcing requires websockets to be enabled and configured.\n");
192+
s_printHelp();
193+
return 1;
194+
}
195+
196+
if (!s_cmdOptionExists(argv, argv + argc, "--x509_role_alias"))
197+
{
198+
fprintf(stdout, "X509 credentials sourcing requires an x509 role alias to be specified.\n");
199+
s_printHelp();
200+
return 1;
201+
}
202+
x509RoleAlias = s_getCmdOption(argv, argv + argc, "--x509_role_alias");
203+
204+
if (!s_cmdOptionExists(argv, argv + argc, "--x509_endpoint"))
205+
{
206+
fprintf(stdout, "X509 credentials sourcing requires an x509 endpoint to be specified.\n");
207+
s_printHelp();
208+
return 1;
209+
}
210+
x509Endpoint = s_getCmdOption(argv, argv + argc, "--x509_endpoint");
211+
212+
if (!s_cmdOptionExists(argv, argv + argc, "--x509_thing"))
213+
{
214+
fprintf(stdout, "X509 credentials sourcing requires an x509 thing name to be specified.\n");
215+
s_printHelp();
216+
return 1;
217+
}
218+
x509ThingName = s_getCmdOption(argv, argv + argc, "--x509_thing");
219+
220+
if (!s_cmdOptionExists(argv, argv + argc, "--x509_cert"))
221+
{
222+
fprintf(stdout, "X509 credentials sourcing requires an Iot thing certificate to be specified.\n");
223+
s_printHelp();
224+
return 1;
225+
}
226+
x509CertificatePath = s_getCmdOption(argv, argv + argc, "--x509_cert");
227+
228+
if (!s_cmdOptionExists(argv, argv + argc, "--x509_key"))
229+
{
230+
fprintf(stdout, "X509 credentials sourcing requires an Iot thing private key to be specified.\n");
231+
s_printHelp();
232+
return 1;
233+
}
234+
x509KeyPath = s_getCmdOption(argv, argv + argc, "--x509_key");
235+
236+
if (s_cmdOptionExists(argv, argv + argc, "--x509_rootca"))
237+
{
238+
x509RootCAFile = s_getCmdOption(argv, argv + argc, "--x509_rootca");
239+
}
240+
241+
useX509 = true;
242+
}
243+
132244
/********************** Now Setup an Mqtt Client ******************/
133245
/*
134246
* You need an event loop group to process IO events.
@@ -151,6 +263,7 @@ int main(int argc, char *argv[])
151263
exit(-1);
152264
}
153265

266+
Aws::Crt::Io::TlsContext x509TlsCtx;
154267
Aws::Iot::MqttClientConnectionConfigBuilder builder;
155268

156269
if (!certificatePath.empty() && !keyPath.empty())
@@ -161,13 +274,71 @@ int main(int argc, char *argv[])
161274
{
162275
Aws::Iot::WebsocketConfig config(signingRegion, &bootstrap);
163276

277+
Aws::Crt::Http::HttpClientConnectionProxyOptions proxyOptions;
164278
if (!proxyHost.empty())
165279
{
166-
Aws::Crt::Http::HttpClientConnectionProxyOptions proxyOptions;
167280
proxyOptions.HostName = proxyHost;
168281
proxyOptions.Port = proxyPort;
169282
proxyOptions.AuthType = Aws::Crt::Http::AwsHttpProxyAuthenticationType::None;
170-
config.ProxyOptions = std::move(proxyOptions);
283+
config.ProxyOptions = proxyOptions;
284+
}
285+
286+
if (useX509)
287+
{
288+
Aws::Crt::Io::TlsContextOptions tlsCtxOptions =
289+
Aws::Crt::Io::TlsContextOptions::InitClientWithMtls(x509CertificatePath.c_str(), x509KeyPath.c_str());
290+
if (!tlsCtxOptions)
291+
{
292+
fprintf(
293+
stderr,
294+
"Unable to initialize tls context options, error: %s!\n",
295+
ErrorDebugString(tlsCtxOptions.LastError()));
296+
return -1;
297+
}
298+
299+
if (!x509RootCAFile.empty())
300+
{
301+
tlsCtxOptions.OverrideDefaultTrustStore(nullptr, x509RootCAFile.c_str());
302+
}
303+
304+
x509TlsCtx = Aws::Crt::Io::TlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT);
305+
if (!x509TlsCtx)
306+
{
307+
fprintf(
308+
stderr,
309+
"Unable to create tls context, error: %s!\n",
310+
ErrorDebugString(x509TlsCtx.GetInitializationError()));
311+
return -1;
312+
}
313+
314+
Aws::Crt::Auth::CredentialsProviderX509Config x509Config;
315+
316+
x509Config.TlsOptions = x509TlsCtx.NewConnectionOptions();
317+
if (!x509Config.TlsOptions)
318+
{
319+
fprintf(
320+
stderr,
321+
"Unable to create tls options from tls context, error: %s!\n",
322+
ErrorDebugString(x509Config.TlsOptions.LastError()));
323+
return -1;
324+
}
325+
326+
x509Config.Bootstrap = &bootstrap;
327+
x509Config.Endpoint = x509Endpoint;
328+
x509Config.RoleAlias = x509RoleAlias;
329+
x509Config.ThingName = x509ThingName;
330+
331+
if (!proxyHost.empty())
332+
{
333+
x509Config.ProxyOptions = proxyOptions;
334+
}
335+
336+
config.CredentialsProvider = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderX509(x509Config);
337+
if (!config.CredentialsProvider)
338+
{
339+
fprintf(stderr, "Failure to create X509 credentials provider!\n");
340+
return -1;
341+
}
171342
}
172343

173344
builder = Aws::Iot::MqttClientConnectionConfigBuilder(config);

0 commit comments

Comments
 (0)