diff --git a/.github/workflows/ci_run_custom_key_ops_cfg.json b/.github/workflows/ci_run_custom_key_ops_cfg.json index 7ad726462..2ed5b991b 100644 --- a/.github/workflows/ci_run_custom_key_ops_cfg.json +++ b/.github/workflows/ci_run_custom_key_ops_cfg.json @@ -1,8 +1,8 @@ { "language": "Java", - "sample_file": "samples/CustomKeyOpsPubSub", + "sample_file": "samples/CustomKeyOpsConnect", "sample_region": "us-east-1", - "sample_main_class": "customkeyopspubsub.CustomKeyOpsPubSub", + "sample_main_class": "customkeyopsconnect.CustomKeyOpsConnect", "arguments": [ { "name": "--endpoint", diff --git a/.github/workflows/ci_run_fleet_provisioning_cfg.json b/.github/workflows/ci_run_fleet_provisioning_cfg.json index a064037a4..77f3a575f 100644 --- a/.github/workflows/ci_run_fleet_provisioning_cfg.json +++ b/.github/workflows/ci_run_fleet_provisioning_cfg.json @@ -1,8 +1,8 @@ { "language": "Java", - "sample_file": "samples/Identity", + "sample_file": "samples/FleetProvisioning", "sample_region": "us-east-1", - "sample_main_class": "identity.FleetProvisioningSample", + "sample_main_class": "fleetprovisioning.FleetProvisioningSample", "arguments": [ { "name": "--endpoint", diff --git a/android/app/build.gradle b/android/app/build.gradle index b3d77e0aa..8585b667f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -21,7 +21,6 @@ android { main { java.srcDir '../../samples/BasicPubSub/src/main/java' java.srcDir '../../samples/Jobs/src/main/java' - java.srcDir '../../samples/PubSubStress/src/main/java' java.srcDir '../../samples/Shadow/src/main/java' java.srcDir 'src/main/java' } diff --git a/android/app/src/main/java/software/amazon/awssdk/iotsamples/MainActivity.kt b/android/app/src/main/java/software/amazon/awssdk/iotsamples/MainActivity.kt index 016199601..21a2d3623 100644 --- a/android/app/src/main/java/software/amazon/awssdk/iotsamples/MainActivity.kt +++ b/android/app/src/main/java/software/amazon/awssdk/iotsamples/MainActivity.kt @@ -17,8 +17,7 @@ import kotlin.concurrent.thread val SAMPLES = mapOf( "Publish/Subscribe Sample" to "pubsub.PubSub", "Jobs Client Sample" to "jobs.JobsSample", - "Shadow Client Sample" to "shadow.ShadowSample", - "Publish/Subscribe Load Test" to "pubsubstress.PubSubStress" + "Shadow Client Sample" to "shadow.ShadowSample" ) class MainActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener { diff --git a/codebuild/samples/custom-key-ops-linux.sh b/codebuild/samples/custom-key-ops-linux.sh index 57a887566..184d1e081 100755 --- a/codebuild/samples/custom-key-ops-linux.sh +++ b/codebuild/samples/custom-key-ops-linux.sh @@ -4,13 +4,13 @@ set -e set -o pipefail env -pushd $CODEBUILD_SRC_DIR/samples/CustomKeyOpsPubSub +pushd $CODEBUILD_SRC_DIR/samples/CustomKeyOpsConnect ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') mvn compile echo "Custom Key Ops test" -mvn exec:java -Dexec.mainClass="customkeyopspubsub.CustomKeyOpsPubSub" -Daws.crt.ci="True" -Dexec.arguments="--endpoint,$ENDPOINT,--key,/tmp/privatekey_p8.pem,--cert,/tmp/certificate.pem" +mvn exec:java -Dexec.mainClass="customkeyopsconnect.CustomKeyOpsConnect" -Daws.crt.ci="True" -Dexec.arguments="--endpoint,$ENDPOINT,--key,/tmp/privatekey_p8.pem,--cert,/tmp/certificate.pem" popd diff --git a/codebuild/samples/customkeyops-linux.sh b/codebuild/samples/customkeyops-linux.sh index aadd6a724..e9b72695c 100755 --- a/codebuild/samples/customkeyops-linux.sh +++ b/codebuild/samples/customkeyops-linux.sh @@ -4,13 +4,13 @@ set -e env -pushd $CODEBUILD_SRC_DIR/samples/CustomKeyOpsPubSub +pushd $CODEBUILD_SRC_DIR/samples/CustomKeyOpsConnect ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') mvn compile echo "Custom Key Ops test" -mvn exec:java -Dexec.mainClass="customkeyopspubsub.CustomKeyOpsPubSub" -Daws.crt.ci="True" -Dexec.arguments="--endpoint,$ENDPOINT,--key,/tmp/privatekey_p8.pem,--cert,/tmp/certificate.pem" +mvn exec:java -Dexec.mainClass="customkeyopsconnect.CustomKeyOpsConnect" -Daws.crt.ci="True" -Dexec.arguments="--endpoint,$ENDPOINT,--key,/tmp/privatekey_p8.pem,--cert,/tmp/certificate.pem" popd diff --git a/documents/MQTT5_Userguide.md b/documents/MQTT5_Userguide.md index 68a0c6786..d18b94cba 100644 --- a/documents/MQTT5_Userguide.md +++ b/documents/MQTT5_Userguide.md @@ -27,7 +27,7 @@ MQTT5 support is currently in **developer preview**. We encourage feedback at all times, but feedback during the preview window is especially valuable in shaping the final product. During the preview period we may make backwards-incompatible changes to the public API, but in general, this is something we will try our best to avoid. -The MQTT5 client cannot yet be used with the AWS IoT MQTT services (Shadow, Jobs, Identity). We plan to address this in the near future. +The MQTT5 client cannot yet be used with the AWS IoT MQTT services (Shadow, Jobs, Fleet-Provisioning/Identity). We plan to address this in the near future. # Introduction @@ -221,7 +221,7 @@ A MQTT5 direct connection can be made with a set of custom private key operation ~~~ java class MyKeyOperationHandler implements TlsKeyOperationHandler { - // Implement based on the operation. See CustomKeyOpsPubSub sample for example + // Implement based on the operation. See CustomKeyOpsConnect sample for example public void performOperation(TlsKeyOperation operation) { try { throw new RuntimeException("This is just an example!"); diff --git a/pom.xml b/pom.xml index 7bc986721..e847307be 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,6 @@ samples/BasicConnect samples/WebsocketConnect samples/X509CredentialsProviderConnect - samples/RawConnect samples/Pkcs11Connect samples/CustomAuthorizerConnect samples/JavaKeystoreConnect @@ -20,11 +19,10 @@ samples/Greengrass samples/GreengrassIPC samples/Jobs - samples/PubSubStress - samples/CustomKeyOpsPubSub + samples/CustomKeyOpsConnect samples/WindowsCertConnect samples/Shadow - samples/Identity + samples/FleetProvisioning samples/Mqtt5/PubSub samples/Mqtt5/SharedSubscription diff --git a/samples/BasicConnect/src/main/java/basicconnect/BasicConnect.java b/samples/BasicConnect/src/main/java/basicconnect/BasicConnect.java index 4fdfe301c..fe5468466 100644 --- a/samples/BasicConnect/src/main/java/basicconnect/BasicConnect.java +++ b/samples/BasicConnect/src/main/java/basicconnect/BasicConnect.java @@ -8,29 +8,26 @@ import software.amazon.awssdk.crt.CRT; import software.amazon.awssdk.crt.CrtResource; import software.amazon.awssdk.crt.CrtRuntimeException; -import software.amazon.awssdk.crt.io.ClientBootstrap; import software.amazon.awssdk.crt.mqtt.MqttClientConnection; import software.amazon.awssdk.crt.mqtt.MqttClientConnectionEvents; -import software.amazon.awssdk.iot.iotjobs.model.RejectedError; +import software.amazon.awssdk.crt.http.HttpProxyOptions; +import software.amazon.awssdk.iot.AwsIotMqttConnectionBuilder; import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletableFuture; import utils.commandlineutils.CommandLineUtils; public class BasicConnect { - // When run normally, we want to exit nicely even if something goes wrong + // When run normally, we want to exit nicely even if something goes wrong. // When run from CI, we want to let an exception escape which in turn causes the - // exec:java task to return a non-zero exit code + // exec:java task to return a non-zero exit code. static String ciPropValue = System.getProperty("aws.crt.ci"); static boolean isCI = ciPropValue != null && Boolean.valueOf(ciPropValue); static CommandLineUtils cmdUtils; - static void onRejectedError(RejectedError error) { - System.out.println("Request rejected: " + error.code.toString() + ": " + error.message); - } - /* * When called during a CI run, throw an exception that will escape and fail the exec:java task * When called otherwise, print what went wrong (if anything) and just continue (return from main) @@ -45,15 +42,12 @@ static void onApplicationFailure(Throwable cause) { public static void main(String[] args) { - cmdUtils = new CommandLineUtils(); - cmdUtils.registerProgramName("BasicConnect"); - cmdUtils.addCommonMQTTCommands(); - cmdUtils.addCommonProxyCommands(); - cmdUtils.registerCommand("key", "", "Path to your key in PEM format."); - cmdUtils.registerCommand("cert", "", "Path to your client certificate in PEM format."); - cmdUtils.registerCommand("client_id", "", "Client id to use (optional, default='test-*')."); - cmdUtils.registerCommand("port", "", "Port to connect to on the endpoint (optional, default='8883')."); - cmdUtils.sendArguments(args); + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + CommandLineUtils.SampleCommandLineData cmdData = CommandLineUtils.getInputForIoTSample("BasicConnect", args); MqttClientConnectionEvents callbacks = new MqttClientConnectionEvents() { @Override @@ -71,20 +65,54 @@ public void onConnectionResumed(boolean sessionPresent) { try { - // Create a connection using a certificate and key - // Note: The data for the connection is gotten from cmdUtils. - // (see buildDirectMQTTConnection for implementation) - MqttClientConnection connection = cmdUtils.buildDirectMQTTConnection(callbacks); + /** + * Create the MQTT connection from the builder + */ + AwsIotMqttConnectionBuilder builder = AwsIotMqttConnectionBuilder.newMtlsBuilderFromPath(cmdData.input_cert, cmdData.input_key); + if (cmdData.input_ca != "") { + builder.withCertificateAuthorityFromPath(null, cmdData.input_ca); + } + builder.withConnectionEventCallbacks(callbacks) + .withClientId(cmdData.input_clientId) + .withEndpoint(cmdData.input_endpoint) + .withPort((short)cmdData.input_port) + .withCleanSession(true) + .withProtocolOperationTimeoutMs(60000); + if (cmdData.input_proxyHost != "" && cmdData.input_proxyPort > 0) { + HttpProxyOptions proxyOptions = new HttpProxyOptions(); + proxyOptions.setHost(cmdData.input_proxyHost); + proxyOptions.setPort(cmdData.input_proxyPort); + builder.withHttpProxyOptions(proxyOptions); + } + MqttClientConnection connection = builder.build(); + builder.close(); + + /** + * Verify the connection was created + */ if (connection == null) { onApplicationFailure(new RuntimeException("MQTT connection creation failed!")); } - // Connect and disconnect using the connection we created - // (see sampleConnectAndDisconnect for implementation) - cmdUtils.sampleConnectAndDisconnect(connection); - - // Close the connection now that we are completely done with it. + /** + * Connect and disconnect + */ + CompletableFuture connected = connection.connect(); + try { + boolean sessionPresent = connected.get(); + System.out.println("Connected to " + (!sessionPresent ? "new" : "existing") + " session!"); + } catch (Exception ex) { + throw new RuntimeException("Exception occurred during connect", ex); + } + System.out.println("Disconnecting..."); + CompletableFuture disconnected = connection.disconnect(); + disconnected.get(); + System.out.println("Disconnected."); + + /** + * Close the connection now that it is complete + */ connection.close(); } catch (CrtRuntimeException | InterruptedException | ExecutionException ex) { diff --git a/samples/BasicPubSub/src/main/java/pubsub/PubSub.java b/samples/BasicPubSub/src/main/java/pubsub/PubSub.java index cbc7ce367..a3e2b64c5 100644 --- a/samples/BasicPubSub/src/main/java/pubsub/PubSub.java +++ b/samples/BasicPubSub/src/main/java/pubsub/PubSub.java @@ -8,21 +8,14 @@ import software.amazon.awssdk.crt.CRT; import software.amazon.awssdk.crt.CrtResource; import software.amazon.awssdk.crt.CrtRuntimeException; -import software.amazon.awssdk.crt.Log; -import software.amazon.awssdk.crt.auth.credentials.X509CredentialsProvider; import software.amazon.awssdk.crt.http.HttpProxyOptions; -import software.amazon.awssdk.crt.io.ClientBootstrap; -import software.amazon.awssdk.crt.io.ClientTlsContext; -import software.amazon.awssdk.crt.io.TlsContextOptions; import software.amazon.awssdk.crt.mqtt.MqttClientConnection; import software.amazon.awssdk.crt.mqtt.MqttClientConnectionEvents; import software.amazon.awssdk.crt.mqtt.MqttMessage; import software.amazon.awssdk.crt.mqtt.QualityOfService; import software.amazon.awssdk.iot.AwsIotMqttConnectionBuilder; -import software.amazon.awssdk.iot.iotjobs.model.RejectedError; import java.nio.charset.StandardCharsets; -import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -37,16 +30,8 @@ public class PubSub { static String ciPropValue = System.getProperty("aws.crt.ci"); static boolean isCI = ciPropValue != null && Boolean.valueOf(ciPropValue); - static String topic = "test/topic"; - static String message = "Hello World!"; - static int messagesToPublish = 10; - static CommandLineUtils cmdUtils; - static void onRejectedError(RejectedError error) { - System.out.println("Request rejected: " + error.code.toString() + ": " + error.message); - } - /* * When called during a CI run, throw an exception that will escape and fail the exec:java task * When called otherwise, print what went wrong (if anything) and just continue (return from main) @@ -61,20 +46,12 @@ static void onApplicationFailure(Throwable cause) { public static void main(String[] args) { - cmdUtils = new CommandLineUtils(); - cmdUtils.registerProgramName("PubSub"); - cmdUtils.addCommonMQTTCommands(); - cmdUtils.addCommonTopicMessageCommands(); - cmdUtils.registerCommand("key", "", "Path to your key in PEM format."); - cmdUtils.registerCommand("cert", "", "Path to your client certificate in PEM format."); - cmdUtils.registerCommand("client_id", "", "Client id to use (optional, default='test-*')."); - cmdUtils.registerCommand("port", "", "Port to connect to on the endpoint (optional, default='8883')."); - cmdUtils.registerCommand("count", "", "Number of messages to publish (optional, default='10')."); - cmdUtils.sendArguments(args); - - topic = cmdUtils.getCommandOrDefault("topic", topic); - message = cmdUtils.getCommandOrDefault("message", message); - messagesToPublish = Integer.parseInt(cmdUtils.getCommandOrDefault("count", String.valueOf(messagesToPublish))); + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + CommandLineUtils.SampleCommandLineData cmdData = CommandLineUtils.getInputForIoTSample("PubSub", args); MqttClientConnectionEvents callbacks = new MqttClientConnectionEvents() { @Override @@ -92,12 +69,29 @@ public void onConnectionResumed(boolean sessionPresent) { try { - MqttClientConnection connection = cmdUtils.buildMQTTConnection(callbacks); - if (connection == null) - { - onApplicationFailure(new RuntimeException("MQTT connection creation failed!")); + /** + * Create the MQTT connection from the builder + */ + AwsIotMqttConnectionBuilder builder = AwsIotMqttConnectionBuilder.newMtlsBuilderFromPath(cmdData.input_cert, cmdData.input_key); + if (cmdData.input_ca != "") { + builder.withCertificateAuthorityFromPath(null, cmdData.input_ca); + } + builder.withConnectionEventCallbacks(callbacks) + .withClientId(cmdData.input_clientId) + .withEndpoint(cmdData.input_endpoint) + .withPort((short)cmdData.input_port) + .withCleanSession(true) + .withProtocolOperationTimeoutMs(60000); + if (cmdData.input_proxyHost != "" && cmdData.input_proxyPort > 0) { + HttpProxyOptions proxyOptions = new HttpProxyOptions(); + proxyOptions.setHost(cmdData.input_proxyHost); + proxyOptions.setPort(cmdData.input_proxyPort); + builder.withHttpProxyOptions(proxyOptions); } + MqttClientConnection connection = builder.build(); + builder.close(); + // Connect the MQTT client CompletableFuture connected = connection.connect(); try { boolean sessionPresent = connected.get(); @@ -106,25 +100,25 @@ public void onConnectionResumed(boolean sessionPresent) { throw new RuntimeException("Exception occurred during connect", ex); } - CountDownLatch countDownLatch = new CountDownLatch(messagesToPublish); - - CompletableFuture subscribed = connection.subscribe(topic, QualityOfService.AT_LEAST_ONCE, (message) -> { + // Subscribe to the topic + CountDownLatch countDownLatch = new CountDownLatch(cmdData.input_count); + CompletableFuture subscribed = connection.subscribe(cmdData.input_topic, QualityOfService.AT_LEAST_ONCE, (message) -> { String payload = new String(message.getPayload(), StandardCharsets.UTF_8); System.out.println("MESSAGE: " + payload); countDownLatch.countDown(); }); - subscribed.get(); + // Publish to the topic int count = 0; - while (count++ < messagesToPublish) { - CompletableFuture published = connection.publish(new MqttMessage(topic, message.getBytes(), QualityOfService.AT_LEAST_ONCE, false)); + while (count++ < cmdData.input_count) { + CompletableFuture published = connection.publish(new MqttMessage(cmdData.input_topic, cmdData.input_message.getBytes(), QualityOfService.AT_LEAST_ONCE, false)); published.get(); Thread.sleep(1000); } - countDownLatch.await(); + // Disconnect CompletableFuture disconnected = connection.disconnect(); disconnected.get(); diff --git a/samples/CognitoConnect/src/main/java/cognitoconnect/CognitoConnect.java b/samples/CognitoConnect/src/main/java/cognitoconnect/CognitoConnect.java index 5874543a2..2e8a7b9bd 100644 --- a/samples/CognitoConnect/src/main/java/cognitoconnect/CognitoConnect.java +++ b/samples/CognitoConnect/src/main/java/cognitoconnect/CognitoConnect.java @@ -10,8 +10,14 @@ import software.amazon.awssdk.crt.CrtRuntimeException; import software.amazon.awssdk.crt.mqtt.MqttClientConnection; import software.amazon.awssdk.crt.mqtt.MqttClientConnectionEvents; -import software.amazon.awssdk.iot.iotjobs.model.RejectedError; - +import software.amazon.awssdk.crt.http.HttpProxyOptions; +import software.amazon.awssdk.crt.auth.credentials.CognitoCredentialsProvider; +import software.amazon.awssdk.crt.io.ClientBootstrap; +import software.amazon.awssdk.crt.io.TlsContextOptions; +import software.amazon.awssdk.crt.io.ClientTlsContext; +import software.amazon.awssdk.iot.AwsIotMqttConnectionBuilder; + +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import utils.commandlineutils.CommandLineUtils; @@ -39,14 +45,12 @@ static void onApplicationFailure(Throwable cause) { public static void main(String[] args) { - cmdUtils = new CommandLineUtils(); - cmdUtils.registerProgramName("CognitoConnect"); - cmdUtils.addCommonMQTTCommands(); - cmdUtils.addCommonProxyCommands(); - cmdUtils.registerCommand("signing_region", "", "AWS IoT service region."); - cmdUtils.registerCommand("client_id", "", "Client id to use (optional, default='test-*')."); - cmdUtils.registerCommand("cognito_identity", "", "The Cognito identity ID to use to connect via Cognito"); - cmdUtils.sendArguments(args); + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + CommandLineUtils.SampleCommandLineData cmdData = CommandLineUtils.getInputForIoTSample("CognitoConnect", args); MqttClientConnectionEvents callbacks = new MqttClientConnectionEvents() { @Override @@ -63,10 +67,9 @@ public void onConnectionResumed(boolean sessionPresent) { }; try { + /** * Creates a connection using Cognito credentials. - * Note: The data for the connection is gotten from cmdUtils. - * (see buildCognitoMQTTConnection for implementation) * * Note: This sample and code assumes that you are using a Cognito identity * in the same region as you pass to "--signing_region". @@ -74,17 +77,59 @@ public void onConnectionResumed(boolean sessionPresent) { * See https://docs.aws.amazon.com/general/latest/gr/cognito_identity.html * for all Cognito endpoints. */ - MqttClientConnection connection = cmdUtils.buildCognitoMQTTConnection(callbacks); + // ================================= + AwsIotMqttConnectionBuilder builder = AwsIotMqttConnectionBuilder.newMtlsBuilderFromPath(null, null); + builder.withConnectionEventCallbacks(callbacks) + .withClientId(cmdData.input_clientId) + .withEndpoint(cmdData.input_endpoint) + .withCleanSession(true) + .withProtocolOperationTimeoutMs(60000); + + builder.withWebsockets(true); + builder.withWebsocketSigningRegion(cmdData.input_signingRegion); + + CognitoCredentialsProvider.CognitoCredentialsProviderBuilder cognitoBuilder = new CognitoCredentialsProvider.CognitoCredentialsProviderBuilder(); + String cognitoEndpoint = "cognito-identity." + cmdData.input_signingRegion + ".amazonaws.com"; + cognitoBuilder.withEndpoint(cognitoEndpoint).withIdentity(cmdData.input_cognitoIdentity); + cognitoBuilder.withClientBootstrap(ClientBootstrap.getOrCreateStaticDefault()); + + TlsContextOptions cognitoTlsContextOptions = TlsContextOptions.createDefaultClient(); + ClientTlsContext cognitoTlsContext = new ClientTlsContext(cognitoTlsContextOptions); + cognitoTlsContextOptions.close(); + cognitoBuilder.withTlsContext(cognitoTlsContext); + + CognitoCredentialsProvider cognitoCredentials = cognitoBuilder.build(); + builder.withWebsocketCredentialsProvider(cognitoCredentials); + + MqttClientConnection connection = builder.build(); + builder.close(); + cognitoCredentials.close(); + cognitoTlsContext.close(); + if (connection == null) { onApplicationFailure(new RuntimeException("MQTT connection creation failed!")); } + // ================================= - // Connect and disconnect using the connection we created - // (see sampleConnectAndDisconnect for implementation) - cmdUtils.sampleConnectAndDisconnect(connection); + /** + * Connect and disconnect + */ + CompletableFuture connected = connection.connect(); + try { + boolean sessionPresent = connected.get(); + System.out.println("Connected to " + (!sessionPresent ? "new" : "existing") + " session!"); + } catch (Exception ex) { + throw new RuntimeException("Exception occurred during connect", ex); + } + System.out.println("Disconnecting..."); + CompletableFuture disconnected = connection.disconnect(); + disconnected.get(); + System.out.println("Disconnected."); - // Close the connection now that we are completely done with it. + /** + * Close the connection now that it is complete + */ connection.close(); } catch (CrtRuntimeException | InterruptedException | ExecutionException ex) { diff --git a/samples/CustomAuthorizerConnect/src/main/java/customauthorizerconnect/CustomAuthorizerConnect.java b/samples/CustomAuthorizerConnect/src/main/java/customauthorizerconnect/CustomAuthorizerConnect.java index f1880067e..8f92fe079 100644 --- a/samples/CustomAuthorizerConnect/src/main/java/customauthorizerconnect/CustomAuthorizerConnect.java +++ b/samples/CustomAuthorizerConnect/src/main/java/customauthorizerconnect/CustomAuthorizerConnect.java @@ -8,12 +8,13 @@ import software.amazon.awssdk.crt.CRT; import software.amazon.awssdk.crt.CrtResource; import software.amazon.awssdk.crt.CrtRuntimeException; -import software.amazon.awssdk.crt.io.ClientBootstrap; import software.amazon.awssdk.crt.mqtt.MqttClientConnection; import software.amazon.awssdk.crt.mqtt.MqttClientConnectionEvents; -import software.amazon.awssdk.iot.iotjobs.model.RejectedError; +import software.amazon.awssdk.iot.AwsIotMqttConnectionBuilder; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.io.UnsupportedEncodingException; import utils.commandlineutils.CommandLineUtils; @@ -39,15 +40,13 @@ static void onApplicationFailure(Throwable cause) { } public static void main(String[] args) { - cmdUtils = new CommandLineUtils(); - cmdUtils.registerProgramName("CustomAuthorizerConnect"); - cmdUtils.addCommonMQTTCommands(); - cmdUtils.registerCommand("client_id", "", "Client id to use (optional, default='test-*')."); - cmdUtils.registerCommand("custom_auth_username", "", "Username for connecting to custom authorizer (optional, default=null)."); - cmdUtils.registerCommand("custom_auth_authorizer_name", "", "Name of custom authorizer (optional, default=null)."); - cmdUtils.registerCommand("custom_auth_authorizer_signature", "", "Signature passed when connecting to custom authorizer (optional, default=null)."); - cmdUtils.registerCommand("custom_auth_password", "", "Password for connecting to custom authorizer (optional, default=null)."); - cmdUtils.sendArguments(args); + + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + CommandLineUtils.SampleCommandLineData cmdData = CommandLineUtils.getInputForIoTSample("CustomAuthorizerConnect", args); MqttClientConnectionEvents callbacks = new MqttClientConnectionEvents() { @Override @@ -65,23 +64,50 @@ public void onConnectionResumed(boolean sessionPresent) { try { - // Create a connection authenticated via a custom authorizer. - // Note: The data for the connection is gotten from cmdUtils. - // (see buildDirectMQTTConnectionWithCustomAuthorizer for implementation) - MqttClientConnection connection = cmdUtils.buildDirectMQTTConnectionWithCustomAuthorizer(callbacks); + /** + * Create the MQTT connection from the builder + */ + AwsIotMqttConnectionBuilder builder = AwsIotMqttConnectionBuilder.newDefaultBuilder(); + builder.withConnectionEventCallbacks(callbacks) + .withClientId(cmdData.input_clientId) + .withEndpoint(cmdData.input_endpoint) + .withCleanSession(true) + .withProtocolOperationTimeoutMs(60000); + builder.withCustomAuthorizer( + cmdData.input_customAuthUsername, + cmdData.input_customAuthorizerName, + cmdData.input_customAuthorizerSignature, + cmdData.input_customAuthPassword); + MqttClientConnection connection = builder.build(); + builder.close(); + + /** + * Verify the connection was created + */ if (connection == null) { onApplicationFailure(new RuntimeException("MQTT connection creation (through custom authorizer) failed!")); } - // Connect and disconnect using the connection we created - // (see sampleConnectAndDisconnect for implementation) - cmdUtils.sampleConnectAndDisconnect(connection); + /** + * Connect and disconnect + */ + CompletableFuture connected = connection.connect(); + try { + boolean sessionPresent = connected.get(); + System.out.println("Connected to " + (!sessionPresent ? "new" : "existing") + " session!"); + } catch (Exception ex) { + throw new RuntimeException("Exception occurred during connect", ex); + } + System.out.println("Disconnecting..."); + CompletableFuture disconnected = connection.disconnect(); + disconnected.get(); + System.out.println("Disconnected."); // Close the connection now that we are completely done with it. connection.close(); - } catch (CrtRuntimeException | InterruptedException | ExecutionException ex) { + } catch (CrtRuntimeException | UnsupportedEncodingException | InterruptedException | ExecutionException ex) { onApplicationFailure(ex); } diff --git a/samples/CustomKeyOpsPubSub/README.md b/samples/CustomKeyOpsConnect/README.md similarity index 72% rename from samples/CustomKeyOpsPubSub/README.md rename to samples/CustomKeyOpsConnect/README.md index 6cd4dafb2..6a591d6eb 100644 --- a/samples/CustomKeyOpsPubSub/README.md +++ b/samples/CustomKeyOpsConnect/README.md @@ -1,8 +1,8 @@ -# Custom Key Operations PubSub +# Custom Key Operations Connect [**Return to main sample list**](../README.md) -This sample is similar to the [Basic PubSub](../BasicPubSub/README.md) sample but shows how to perform custom private key operations during the Mutual TLS (mTLS) handshake. This is necessary if you require an external library to handle private key operations such as signing and decrypting. By utilizing custom private key operations, you can use any external library for the Mutual TLS private key operations when connecting to AWS IoT Core. +This sample is similar to the [Basic Connect](../BasicConnect/README.md) sample but shows how to perform custom private key operations during the Mutual TLS (mTLS) handshake. This is necessary if you require an external library to handle private key operations such as signing and decrypting. By utilizing custom private key operations, you can use any external library for the Mutual TLS private key operations when connecting to AWS IoT Core. **WARNING: Unix (Linux) only** @@ -56,16 +56,16 @@ Note that in a real application, you may want to avoid the use of wildcards in y ## How to run -To run the Custom Key Ops PubSub sample use the following command: +To run the Custom Key Ops Connect sample use the following command: ``` sh -> mvn exec:java -pl samples/CustomKeyOpsPubSub -Dexec.mainClass=customkeyopspubsub.CustomKeyOpsPubSub -Dexec.args="--endpoint --cert --key " +> mvn exec:java -pl samples/CustomKeyOpsConnect -Dexec.mainClass=customkeyopsconnect.CustomKeyOpsConnect -Dexec.args="--endpoint --cert --key " ``` You can also pass a Certificate Authority file (CA) if your certificate and key combination requires it: ``` sh -> mvn exec:java -pl samples/CustomKeyOpsPubSub -Dexec.mainClass=customkeyopspubsub.CustomKeyOpsPubSub -Dexec.args="--endpoint --cert --key --ca_file " +> mvn exec:java -pl samples/CustomKeyOpsConnect -Dexec.mainClass=customkeyopsconnect.CustomKeyOpsConnect -Dexec.args="--endpoint --cert --key --ca_file " ``` ### How to convert AWS IoT Core key to PCKS#8 key diff --git a/samples/CustomKeyOpsPubSub/pom.xml b/samples/CustomKeyOpsConnect/pom.xml similarity index 95% rename from samples/CustomKeyOpsPubSub/pom.xml rename to samples/CustomKeyOpsConnect/pom.xml index 13b68db19..59dea22dc 100644 --- a/samples/CustomKeyOpsPubSub/pom.xml +++ b/samples/CustomKeyOpsConnect/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.awssdk.iotdevicesdk - CustomKeyOpsPubSub + CustomKeyOpsConnect jar 1.0-SNAPSHOT ${project.groupId}:${project.artifactId} @@ -45,7 +45,7 @@ exec-maven-plugin 1.4.0 - customkeyopspubsub.CustomKeyOpsPubSub + customkeyopsconnect.CustomKeyOpsConnect diff --git a/samples/CustomKeyOpsPubSub/src/main/java/customkeyopspubsub/CustomKeyOpsPubSub.java b/samples/CustomKeyOpsConnect/src/main/java/customkeyopsconnect/CustomKeyOpsConnect.java similarity index 75% rename from samples/CustomKeyOpsPubSub/src/main/java/customkeyopspubsub/CustomKeyOpsPubSub.java rename to samples/CustomKeyOpsConnect/src/main/java/customkeyopsconnect/CustomKeyOpsConnect.java index c15fd5874..d7c6fe697 100644 --- a/samples/CustomKeyOpsPubSub/src/main/java/customkeyopspubsub/CustomKeyOpsPubSub.java +++ b/samples/CustomKeyOpsConnect/src/main/java/customkeyopsconnect/CustomKeyOpsConnect.java @@ -3,36 +3,31 @@ * SPDX-License-Identifier: Apache-2.0. */ -package customkeyopspubsub; +package customkeyopsconnect; import software.amazon.awssdk.crt.CRT; import software.amazon.awssdk.crt.CrtResource; import software.amazon.awssdk.crt.CrtRuntimeException; import software.amazon.awssdk.crt.io.*; import software.amazon.awssdk.crt.mqtt.*; +import software.amazon.awssdk.crt.http.HttpProxyOptions; import software.amazon.awssdk.iot.AwsIotMqttConnectionBuilder; -import software.amazon.awssdk.crt.Log; -import software.amazon.awssdk.crt.Log.LogLevel; - import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.FileReader; -import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; -import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import utils.commandlineutils.CommandLineUtils; -public class CustomKeyOpsPubSub { +public class CustomKeyOpsConnect { // When run normally, we want to exit nicely even if something goes wrong // When run from CI, we want to let an exception escape which in turn causes the @@ -42,12 +37,6 @@ public class CustomKeyOpsPubSub { static CommandLineUtils cmdUtils; - static String topic = "test/topic"; - static String message = "Hello World!"; - static int messagesToPublish = 10; - static String certPath; - static String keyPath; - /* * When called during a CI run, throw an exception that will escape and fail the exec:java task * When called otherwise, print what went wrong (if anything) and just continue (return from main) @@ -70,7 +59,7 @@ static class MyKeyOperationHandler implements TlsKeyOperationHandler { public void performOperation(TlsKeyOperation operation) { try { System.out.println("MyKeyOperationHandler.performOperation" + operation.getType().name()); - + if (operation.getType() != TlsKeyOperation.Type.SIGN) { throw new RuntimeException("Simple sample only handles SIGN operations"); } @@ -155,23 +144,12 @@ RSAPrivateKey loadPrivateKey(String filepath) { public static void main(String[] args) { - cmdUtils = new CommandLineUtils(); - cmdUtils.registerProgramName("CustomKeyOpsPubSub"); - cmdUtils.addCommonMQTTCommands(); - cmdUtils.addCommonTopicMessageCommands(); - cmdUtils.registerCommand("key", "", "Path to your PKCS#8 key in PEM format."); - cmdUtils.registerCommand("cert", "", "Path to your client certificate in PEM format."); - cmdUtils.registerCommand("client_id", "", "Client id to use (optional, default='test-*')."); - cmdUtils.registerCommand("port", "", "Port to connect to on the endpoint (optional, default='8883')."); - cmdUtils.registerCommand("count", "", "Number of messages to publish (optional, default='10')."); - cmdUtils.sendArguments(args); - - keyPath = cmdUtils.getCommandRequired("key", ""); - certPath = cmdUtils.getCommandRequired("cert", ""); - - topic = cmdUtils.getCommandOrDefault("topic", topic); - message = cmdUtils.getCommandOrDefault("message", message); - messagesToPublish = Integer.parseInt(cmdUtils.getCommandOrDefault("count", String.valueOf(messagesToPublish))); + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + CommandLineUtils.SampleCommandLineData cmdData = CommandLineUtils.getInputForIoTSample("CustomKeyOpsConnect", args); MqttClientConnectionEvents callbacks = new MqttClientConnectionEvents() { @Override @@ -187,17 +165,45 @@ public void onConnectionResumed(boolean sessionPresent) { } }; - MyKeyOperationHandler myKeyOperationHandler = new MyKeyOperationHandler(keyPath); + MyKeyOperationHandler myKeyOperationHandler = new MyKeyOperationHandler(cmdData.input_key); TlsContextCustomKeyOperationOptions keyOperationOptions = new TlsContextCustomKeyOperationOptions(myKeyOperationHandler) - .withCertificateFilePath(certPath); + .withCertificateFilePath(cmdData.input_cert); try { - MqttClientConnection connection = cmdUtils.buildCustomKeyOperationConnection(callbacks, keyOperationOptions); + + /** + * Create the MQTT connection from the builder + */ + AwsIotMqttConnectionBuilder builder = AwsIotMqttConnectionBuilder.newMtlsCustomKeyOperationsBuilder(keyOperationOptions); + if (cmdData.input_ca != "") { + builder.withCertificateAuthorityFromPath(null, cmdData.input_ca); + } + builder.withConnectionEventCallbacks(callbacks) + .withClientId(cmdData.input_clientId) + .withEndpoint(cmdData.input_endpoint) + .withPort((short)cmdData.input_port) + .withCleanSession(true) + .withProtocolOperationTimeoutMs(60000); + if (cmdData.input_proxyHost != "" && cmdData.input_proxyPort > 0) { + HttpProxyOptions proxyOptions = new HttpProxyOptions(); + proxyOptions.setHost(cmdData.input_proxyHost); + proxyOptions.setPort(cmdData.input_proxyPort); + builder.withHttpProxyOptions(proxyOptions); + } + MqttClientConnection connection = builder.build(); + builder.close(); + + /** + * Verify the connection was created + */ if (connection == null) { onApplicationFailure(new RuntimeException("MQTT connection creation failed!")); } + /** + * Connect and disconnect + */ CompletableFuture connected = connection.connect(); try { boolean sessionPresent = connected.get(); @@ -205,29 +211,14 @@ public void onConnectionResumed(boolean sessionPresent) { } catch (Exception ex) { throw new RuntimeException("Exception occurred during connect", ex); } - - CountDownLatch countDownLatch = new CountDownLatch(messagesToPublish); - - CompletableFuture subscribed = connection.subscribe(topic, QualityOfService.AT_LEAST_ONCE, (message) -> { - String payload = new String(message.getPayload(), StandardCharsets.UTF_8); - System.out.println("MESSAGE: " + payload); - countDownLatch.countDown(); - }); - - subscribed.get(); - - int count = 0; - while (count++ < messagesToPublish) { - CompletableFuture published = connection.publish(new MqttMessage(topic, message.getBytes(), QualityOfService.AT_LEAST_ONCE, false)); - published.get(); - Thread.sleep(1000); - } - - countDownLatch.await(); - + System.out.println("Disconnecting..."); CompletableFuture disconnected = connection.disconnect(); disconnected.get(); + System.out.println("Disconnected."); + /** + * Close the connection now that it is complete + */ connection.close(); } catch (CrtRuntimeException | InterruptedException | ExecutionException ex) { diff --git a/samples/Identity/README.md b/samples/FleetProvisioning/README.md similarity index 93% rename from samples/Identity/README.md rename to samples/FleetProvisioning/README.md index 20e754936..55fb5eb71 100644 --- a/samples/Identity/README.md +++ b/samples/FleetProvisioning/README.md @@ -2,7 +2,7 @@ [**Return to main sample list**](../README.md) -This sample uses the AWS IoT [Fleet provisioning](https://docs.aws.amazon.com/iot/latest/developerguide/provision-wo-cert.html) to provision devices using either a CSR or Keys-And-Certificate and subsequently calls RegisterThing. This allows you to create new AWS IoT Core things using a Fleet Provisioning Template. +This sample uses the AWS IoT [Fleet-Provisioning/Identity](https://docs.aws.amazon.com/iot/latest/developerguide/provision-wo-cert.html) service to provision devices using either a CSR or Keys-And-Certificate and subsequently calls RegisterThing. This allows you to create new AWS IoT Core things using a Fleet Provisioning Template. On startup, the script subscribes to topics based on the request type of either CSR or Keys topics, publishes the request to corresponding topic and calls RegisterThing. @@ -74,14 +74,14 @@ Note that in a real application, you may want to avoid the use of wildcards in y There are many different ways to run the Fleet Provisioning sample because of how many different ways there are to setup a Fleet Provisioning template in AWS IoT Core. **The easiest and most common way is to run the sample with the following**: ``` sh -mvn compile exec:java -pl samples/Identity -Dexec.mainClass="identity.FleetProvisioningSample" -Dexec.args="--endpoint +mvn compile exec:java -pl samples/FleetProvisioning -Dexec.mainClass="fleetprovisioning.FleetProvisioningSample" -Dexec.args="--endpoint --cert --key --template_name