diff --git a/iot/api-client/manager/manager.py b/iot/api-client/manager/manager.py index dbb81db15d5..0bcd63abd70 100644 --- a/iot/api-client/manager/manager.py +++ b/iot/api-client/manager/manager.py @@ -490,6 +490,28 @@ def set_iam_permissions( # [END iot_set_iam_policy] +def send_command( + service_account_json, project_id, cloud_region, registry_id, device_id, + command): + """Send a command to a device.""" + # [START iot_send_command] + print('Sending command to device') + client = get_client(service_account_json) + device_path = 'projects/{}/locations/{}/registries/{}/devices/{}'.format( + project_id, cloud_region, registry_id, device_id) + + config_body = { + 'binaryData': base64.urlsafe_b64encode( + command.encode('utf-8')).decode('ascii') + } + + return client.projects( + ).locations().registries( + ).devices().sendCommandToDevice( + name=device_path, body=config_body).execute() + # [END iot_send_command] + + def parse_command_line_args(): """Parse command line arguments.""" default_registry = 'cloudiot_device_manager_example_registry_{}'.format( @@ -546,6 +568,10 @@ def parse_command_line_args(): '--role', default=None, help='Role used for IAM commands.') + parser.add_argument( + '--send_command', + default='1', + help='The command sent to the device') # Command subparser command = parser.add_subparsers(dest='command') @@ -566,6 +592,7 @@ def parse_command_line_args(): command.add_parser('list-registries', help=list_registries.__doc__) command.add_parser('patch-es256', help=patch_es256_auth.__doc__) command.add_parser('patch-rs256', help=patch_rsa256_auth.__doc__) + command.add_parser('send-command', help=send_command.__doc__) command.add_parser('set-config', help=patch_rsa256_auth.__doc__) command.add_parser('set-iam-permissions', help=set_iam_permissions.__doc__) @@ -679,6 +706,12 @@ def run_command(args): args.cloud_region, args.registry_id, args.device_id, args.rsa_certificate_file) + elif args.command == 'send-command': + send_command( + args.service_account_json, args.project_id, + args.cloud_region, args.registry_id, args.device_id, + args.send_command) + elif args.command == 'set-iam-permissions': if (args.member is None): sys.exit('Error: specify --member') @@ -699,10 +732,6 @@ def run_command(args): args.version, args.config) -def main(): +if __name__ == '__main__': args = parse_command_line_args() run_command(args) - - -if __name__ == '__main__': - main() diff --git a/iot/api-client/manager/manager_test.py b/iot/api-client/manager/manager_test.py index 142e60d18df..fb115dec118 100644 --- a/iot/api-client/manager/manager_test.py +++ b/iot/api-client/manager/manager_test.py @@ -13,17 +13,23 @@ # limitations under the License. import os +import sys import time +# Add command receiver for bootstrapping device registry / device for testing +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'mqtt_example')) # noqa from google.cloud import pubsub import pytest import manager +import cloudiot_mqtt_example cloud_region = 'us-central1' device_id_template = 'test-device-{}' +ca_cert_path = '../mqtt_example/resources/roots.pem' es_cert_path = 'resources/ec_public.pem' rsa_cert_path = 'resources/rsa_cert.pem' +rsa_private_path = 'resources/rsa_private.pem' # Must match rsa_cert topic_id = 'test-device-events-{}'.format(int(time.time())) project_id = os.environ['GCLOUD_PROJECT'] @@ -270,3 +276,46 @@ def test_add_patch_delete_es256(test_topic, capsys): manager.delete_registry( service_account_json, project_id, cloud_region, registry_id) + + +def test_send_command(test_topic, capsys): + device_id = device_id_template.format('RSA256') + manager.create_registry( + service_account_json, project_id, cloud_region, pubsub_topic, + registry_id) + manager.create_rs256_device( + service_account_json, project_id, cloud_region, registry_id, + device_id, rsa_cert_path) + + # Exercize the functionality + client = cloudiot_mqtt_example.get_client( + project_id, cloud_region, registry_id, device_id, + rsa_private_path, 'RS256', ca_cert_path, + 'mqtt.googleapis.com', 443) + client.loop_start() + out, _ = capsys.readouterr() + + # Pre-process commands + for i in range(1, 5): + client.loop() + time.sleep(1) + + manager.send_command( + service_account_json, project_id, cloud_region, registry_id, + device_id, 'me want cookies') + out, _ = capsys.readouterr() + + # Process commands + for i in range(1, 5): + client.loop() + time.sleep(1) + + # Clean up + manager.delete_device( + service_account_json, project_id, cloud_region, registry_id, + device_id) + manager.delete_registry( + service_account_json, project_id, cloud_region, registry_id) + + assert 'Sending command to device' in out + assert '400' not in out diff --git a/iot/api-client/manager/requirements.txt b/iot/api-client/manager/requirements.txt index 5a3fcc029a5..44105208ad2 100644 --- a/iot/api-client/manager/requirements.txt +++ b/iot/api-client/manager/requirements.txt @@ -1,4 +1,8 @@ -google-api-python-client==1.7.4 +cryptography==2.4.2 +google-api-python-client==1.7.5 google-auth-httplib2==0.0.3 google-auth==1.6.1 -google-cloud-pubsub==0.38.0 +google-cloud-pubsub==0.39.0 +oauth2client==4.1.3 +paho-mqtt==1.4.0 +pyjwt==1.6.4 diff --git a/iot/api-client/manager/resources/rsa_cert.pem b/iot/api-client/manager/resources/rsa_cert.pem index f237f787ba0..f0c79f9c9c0 100644 --- a/iot/api-client/manager/resources/rsa_cert.pem +++ b/iot/api-client/manager/resources/rsa_cert.pem @@ -1,19 +1,18 @@ -----BEGIN CERTIFICATE----- -MIIDFzCCAf+gAwIBAgIJALsqlqk6FkVjMA0GCSqGSIb3DQEBBQUAMBExDzANBgNV -BAMTBnVudXNlZDAeFw0xNzEyMDYyMjQwNDRaFw0yNzEyMDQyMjQwNDRaMBExDzAN -BgNVBAMTBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZg -rgM97eLsTl3ul4m07pVHE/g9f0VP6/MDQcAjqj9kRmg5XMo/E6eRbNcIVwKEGHwC -SuSvGO/j7reYN4cbjvBqimc8asecg5rtXjaDBm66feB/ktOjASSfGfuN79JOJf+r -/BNcCrs8qWa1FSPEnQO7VTTUstkDKB8uvBFPOiPNVw438KM2lId2G/i63soMes0m -9RjPdXbRqJI39WdMVDYXIqLSVIX11xOXvroetwDPkHIi4Tjzus1T6KMjwKD3f3sm -DeUJeu4f1ZV4LPhhW62O9fkKeGy55LglfqOmWusySdOoNgEocN4V3iDQmJ7QOEXw -yPX+ZhhPHuptDwBTWJcCAwEAAaNyMHAwHQYDVR0OBBYEFD7D1XyWbYOiMWDgDyBs -po+JmlViMEEGA1UdIwQ6MDiAFD7D1XyWbYOiMWDgDyBspo+JmlVioRWkEzARMQ8w -DQYDVQQDEwZ1bnVzZWSCCQC7KpapOhZFYzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 -DQEBBQUAA4IBAQAlq4QHt5yqCBhhLsMNnTDmg6ev7lxXKnb+JRMm3Op0rgBDe4sQ -U3Hxrhma+55f2rx32kwpcMQr4WWp0tUKL0OpWrNqdJc4oGFftLDqzKxXyT1nN5PH -1p9HCkHHwxmGZ60fxKb64yu9PTgsZS64l21CWlNFEiC8IULa5HV/O0ZZdPuAZVv0 -hzmkalP/7uPqJ+z2tBEADQOjrU7SiB8uM+q1BLN9vnZrjo7CsXNwIiQ8N8eXpzsX -did5acwQdAt67uNx9e/YXLMvuAn+qVN+crS4IliBiikMZiUkug5yBDsLySFwXsZg -MGkUkV28QlgT4wzSQJ52yGtYCauqO2qU4zZg +MIIC+DCCAeCgAwIBAgIJALt7HnuYGgVcMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV +BAMMBnVudXNlZDAeFw0xODEyMDMwMDE2MjNaFw0yODExMzAwMDE2MjNaMBExDzAN +BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOrA +b+5XMIgwyl6+CkdJkKKp4f+2hHAOoSqQroj2MR/i41YysUbKCk8KdQSZqTBtIvrY +df7s6zDV3zmi3LBBHX6MGnz/1j5YIXlujBhsnQTRbTuUjjDx96ik2C2rR1w7okA7 +MmBsdVzRe8g5pfNQGdV0l15UaZK+qFlLg0xzasPPKmYFFUbKXeWPaKJmtw9d/l6a +8jb87fwI3LTWjZr6Bk7L4Zf1TDlTDroMvNlkW9Z9xSkcgC77EjMtC7RYxxbelaxd +qI9IdxIpmExg6pKMJEvJNA11GYhTlAxkJNh7gd0WlYvQlwDI7D6NJPbCmm4ac1P2 +AA1MFHVgxoKAFk/8V38CAwEAAaNTMFEwHQYDVR0OBBYEFC5Dlz2bTWzaJO6i1Qn8 +lTdDAigCMB8GA1UdIwQYMBaAFC5Dlz2bTWzaJO6i1Qn8lTdDAigCMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANmnnBQaWPhJne5kOjMe+SEsdLbG +OD9L8RmokOsRPXJbj2KoM07UUXMgUe57daYm72wDfKZpvG5qwybkkym+NnahJY+C +u9FX1dTBjM/TqPWKI817mDp5W31a+q6DXdggG+Yf6pz0dMXGGzRtSTEpLKsKtXqe +Y9f7266JCx9V5QFK14SmpIdBF38G0bcNPEvRJ6uKaVKBnU4+7o4YlQLuDczT29Tp +CXL0egUViNT9kv03Pj9iSPR6EGcmOjnSZe1SFVg9OeCauF1wuuFCxUuCkWSEkEm7 +laNke9PHHTe9BoBxKMsTFEPivhVaAf9fUp+NQxZNmdgbux9AAlJgiyU0sFg= -----END CERTIFICATE----- diff --git a/iot/api-client/manager/resources/rsa_private.pem b/iot/api-client/manager/resources/rsa_private.pem new file mode 100644 index 00000000000..5eaf0240ce8 --- /dev/null +++ b/iot/api-client/manager/resources/rsa_private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDqwG/uVzCIMMpe +vgpHSZCiqeH/toRwDqEqkK6I9jEf4uNWMrFGygpPCnUEmakwbSL62HX+7Osw1d85 +otywQR1+jBp8/9Y+WCF5bowYbJ0E0W07lI4w8feopNgtq0dcO6JAOzJgbHVc0XvI +OaXzUBnVdJdeVGmSvqhZS4NMc2rDzypmBRVGyl3lj2iiZrcPXf5emvI2/O38CNy0 +1o2a+gZOy+GX9Uw5Uw66DLzZZFvWfcUpHIAu+xIzLQu0WMcW3pWsXaiPSHcSKZhM +YOqSjCRLyTQNdRmIU5QMZCTYe4HdFpWL0JcAyOw+jST2wppuGnNT9gANTBR1YMaC +gBZP/Fd/AgMBAAECggEBAOevf4kColJ3nPM+qlRLJaV09yjyUOlrduLUon1oRXmL +6wUCyPXtp5j04CLnKRUzUUezZVlxKHotSr/OnfKSgXKJAgeGVEN5paf8U+YzJBFC +RIV+C4wA84WNFBKWrbo43Nx50DFcOcSet4UYaFGoJ6cFB/PAaeW7p9lhbreAXcnb +g2z4SBsGGO2ZKZkSOcXg39GKX5S0bsEhHzIOFvPBdDQpaVAx/Bq76iBzVz1bRwGp +A3SjJn0g4V+3TcTrpRzgtCRRNPM48JeLHw6/mdVSk6gIoEbg+WTB92Z7BlTbjqhI +LPoMx3wQkg7J2gRrT0rPJIMkgdK9Q8RyWaulsRAn+gECgYEA/jfisAhn7IAPBXKz +ED56NSr174qX97SO2JrB7PO614BV8lHSoV4QI+nQCBkNVhL/SuWvqB3naImo+UOX +Smo6fmh78X+yIXKKhj9qY01jxRGcrnhA25L9gk7TFSNm7XaV2HxHq+TW6tvkL0uO +jXSH3D+u8/f+5ZCt/egNq3eU4n8CgYEA7GWgBLq8jmiz/VVLYboJ0YcJVZ5XAjfJ +vgzxdzX9hIkq5Cpt1ZpO9zE3IXClVvfECXcLpEzoHbiJkEve4dDqbSe4T4JPmpRx +BKRPWFJvTieLA912UmHRBDTrQSA/nZ5zxHsXYmN52femqCwbPWYu5czoqud+GcNq +ghj4oF9UCwECgYEAzpaHx1ntakne6yR807SSB2b0GUfdm1TFyMxqz655pesK7TMF +IlGYeDbn8cy6A7rIcAsbplk21sMX6Ai/h5+wDU3He0e0cG3umI4sXKpla56WX0om +Gsnm7eA0tTbhzBPUTeshK1V6Ob2cP7r9C4MpbRjriiN8pv3eBzpu8WrqOO0CgYAB +8bgGMe75EN1iGQB8tkX8ZirqfFnk18ad/IdD3rrOCz7CD6NFnXZGzC3S5ZVGiNUg +6sy6tjM2g10GRcl4e/phmXEHnl+/OrdPPXa1mD/4GZUoG/ssJEfOzAyfRX+gcTws +goKnuX+4DjRdr7ctoxiBpVTIiwzbc2L93Oy2jPIpAQKBgGzaYhfOlB3SWo+iSKcb +Vx++0gXru1Sgeo42TIn+6adO6DYfwVtfaScVL+Jcg7MZYY+94gnPCp+/ohVTJTvQ +JZdI9Czem64VUtSvc0DCji5gPqvgsO5YgBQrGJJrbmVwu0A6RlNbDwxCdkqHuYk4 +7vsxlbXvNufb0LjnNN0lSUU2 +-----END PRIVATE KEY----- diff --git a/iot/api-client/mqtt_example/cloudiot_mqtt_example.py b/iot/api-client/mqtt_example/cloudiot_mqtt_example.py index bcc2f72b690..d4469554af6 100644 --- a/iot/api-client/mqtt_example/cloudiot_mqtt_example.py +++ b/iot/api-client/mqtt_example/cloudiot_mqtt_example.py @@ -158,6 +158,13 @@ def get_client( # Subscribe to the config topic. client.subscribe(mqtt_config_topic, qos=1) + # The topic that the device will receive commands on. + mqtt_command_topic = '/devices/{}/commands/#'.format(device_id) + + # Subscribe to the commands topic, QoS 1 enables message acknowledgement. + print('Subscribing to {}'.format(mqtt_command_topic)) + client.subscribe(mqtt_command_topic, qos=0) + return client # [END iot_mqtt_config] diff --git a/iot/api-client/mqtt_example/cloudiot_mqtt_example_test.py b/iot/api-client/mqtt_example/cloudiot_mqtt_example_test.py index 770f306e642..47fb5d3e7f3 100644 --- a/iot/api-client/mqtt_example/cloudiot_mqtt_example_test.py +++ b/iot/api-client/mqtt_example/cloudiot_mqtt_example_test.py @@ -176,3 +176,45 @@ def test_config(test_topic, capsys): out, _ = capsys.readouterr() assert "Received message" in out assert '/devices/{}/config'.format(device_id) in out + + +def test_receive_command(capsys): + device_id = device_id_template.format('RSA256') + manager.create_registry( + service_account_json, project_id, cloud_region, pubsub_topic, + registry_id) + manager.create_rs256_device( + service_account_json, project_id, cloud_region, registry_id, + device_id, rsa_cert_path) + + # Exercize the functionality + client = cloudiot_mqtt_example.get_client( + project_id, cloud_region, registry_id, device_id, + rsa_private_path, 'RS256', ca_cert_path, + 'mqtt.googleapis.com', 443) + client.loop_start() + + # Pre-process commands + for i in range(1, 3): + client.loop() + time.sleep(1) + + manager.send_command( + service_account_json, project_id, cloud_region, registry_id, + device_id, 'me want cookies') + + # Process commands + for i in range(1, 3): + client.loop() + time.sleep(1) + + # Clean up + manager.delete_device( + service_account_json, project_id, cloud_region, registry_id, + device_id) + manager.delete_registry( + service_account_json, project_id, cloud_region, registry_id) + + out, _ = capsys.readouterr() + assert 'on_connect' in out # Verify can connect + assert '\'me want cookies\'' in out # Verify can receive command diff --git a/iot/api-client/mqtt_example/requirements.txt b/iot/api-client/mqtt_example/requirements.txt index c7825436175..16beaa6e421 100644 --- a/iot/api-client/mqtt_example/requirements.txt +++ b/iot/api-client/mqtt_example/requirements.txt @@ -1,7 +1,7 @@ -google-api-python-client==1.7.4 +google-api-python-client==1.7.5 google-auth-httplib2==0.0.3 google-auth==1.6.1 -google-cloud-pubsub==0.38.0 +google-cloud-pubsub==0.39.0 cryptography==2.4.2 pyjwt==1.6.4 paho-mqtt==1.4.0 diff --git a/iot/api-client/mqtt_example/resources/rsa_cert.pem b/iot/api-client/mqtt_example/resources/rsa_cert.pem index d81ae7c4f43..f0c79f9c9c0 100644 --- a/iot/api-client/mqtt_example/resources/rsa_cert.pem +++ b/iot/api-client/mqtt_example/resources/rsa_cert.pem @@ -1,18 +1,18 @@ -----BEGIN CERTIFICATE----- -MIIC9TCCAd2gAwIBAgIJALM44e3ivEWkMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV -BAMMBnVudXNlZDAeFw0xNzEyMDcwMDQ1MjdaFw0yNzEyMDUwMDQ1MjdaMBExDzAN -BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL4w -BxHuEyYdbiwKiD8yXY7vYpcygeOQ4/ZdEg3wCB2OuYcaFRcCuqLcTLMnuzdcL+y3 -HBjWkrRW658cg3NG93Vj0iwSrga6u24CGBNYV+h8MBvwaDxk+uubnd5M/Q2OyL1J -GiMxQ1blR/71Hr5hhqaQZ2+qOF6kuf1m9rLUtMUJwOKp/PjPDmy654ZGsFWFSZmy -eRpNzmGU+KJg0o+Qf+sm75a8gQZ8AsrqveW0S/8o+zAjD0SkPcd01QBmYzQhjbi/ -LGGITrzbaB3ld9umJBIcXfnYPYisJfwSsT/jFwiXhrhpxNNaIaKlTzlQIt5l8bSs -HXzJBbuIg5Jb/SyIEpkCAwEAAaNQME4wHQYDVR0OBBYEFOfaQTUVAoNb6fc7qzzl -uKyHGrCYMB8GA1UdIwQYMBaAFOfaQTUVAoNb6fc7qzzluKyHGrCYMAwGA1UdEwQF -MAMBAf8wDQYJKoZIhvcNAQELBQADggEBALKKDtiV1YV8k0YsNdiIXRlS3jsuoGuI -VVBrvDGz5Hel0rH9YmmfPS/Yn08kk3DF8Uynr4Xo1Zt9hmhgoq3ZoWm7MIP1+a9s -WyACyEMhVQSCzQrexRvG5ElpHx/vNjbcwiBkE5urlIvMBVt+BRRNKMNWq6F9ae63 -FxRp7CtNFSbibtLJuPgCs6qoNs0nlt2FPsNvs7jpPipj69o+egVckvQjAyppirWO -+jO5hCLy7EahLz2wCn90z0Xf9lhOZni9meaV1Vy3CHHg6jwIB8/XlRaHFrOGMGXg -h8eQqsmpk9/3o8pv00yj6Hkq+swVg7Rg9FZaUiOv/HO/J7stWU7qPbI= +MIIC+DCCAeCgAwIBAgIJALt7HnuYGgVcMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV +BAMMBnVudXNlZDAeFw0xODEyMDMwMDE2MjNaFw0yODExMzAwMDE2MjNaMBExDzAN +BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOrA +b+5XMIgwyl6+CkdJkKKp4f+2hHAOoSqQroj2MR/i41YysUbKCk8KdQSZqTBtIvrY +df7s6zDV3zmi3LBBHX6MGnz/1j5YIXlujBhsnQTRbTuUjjDx96ik2C2rR1w7okA7 +MmBsdVzRe8g5pfNQGdV0l15UaZK+qFlLg0xzasPPKmYFFUbKXeWPaKJmtw9d/l6a +8jb87fwI3LTWjZr6Bk7L4Zf1TDlTDroMvNlkW9Z9xSkcgC77EjMtC7RYxxbelaxd +qI9IdxIpmExg6pKMJEvJNA11GYhTlAxkJNh7gd0WlYvQlwDI7D6NJPbCmm4ac1P2 +AA1MFHVgxoKAFk/8V38CAwEAAaNTMFEwHQYDVR0OBBYEFC5Dlz2bTWzaJO6i1Qn8 +lTdDAigCMB8GA1UdIwQYMBaAFC5Dlz2bTWzaJO6i1Qn8lTdDAigCMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANmnnBQaWPhJne5kOjMe+SEsdLbG +OD9L8RmokOsRPXJbj2KoM07UUXMgUe57daYm72wDfKZpvG5qwybkkym+NnahJY+C +u9FX1dTBjM/TqPWKI817mDp5W31a+q6DXdggG+Yf6pz0dMXGGzRtSTEpLKsKtXqe +Y9f7266JCx9V5QFK14SmpIdBF38G0bcNPEvRJ6uKaVKBnU4+7o4YlQLuDczT29Tp +CXL0egUViNT9kv03Pj9iSPR6EGcmOjnSZe1SFVg9OeCauF1wuuFCxUuCkWSEkEm7 +laNke9PHHTe9BoBxKMsTFEPivhVaAf9fUp+NQxZNmdgbux9AAlJgiyU0sFg= -----END CERTIFICATE----- diff --git a/iot/api-client/mqtt_example/resources/rsa_private.pem b/iot/api-client/mqtt_example/resources/rsa_private.pem index 06a66e3d40e..5eaf0240ce8 100644 --- a/iot/api-client/mqtt_example/resources/rsa_private.pem +++ b/iot/api-client/mqtt_example/resources/rsa_private.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+MAcR7hMmHW4s -Cog/Ml2O72KXMoHjkOP2XRIN8AgdjrmHGhUXArqi3EyzJ7s3XC/stxwY1pK0Vuuf -HINzRvd1Y9IsEq4GurtuAhgTWFfofDAb8Gg8ZPrrm53eTP0Njsi9SRojMUNW5Uf+ -9R6+YYamkGdvqjhepLn9Zvay1LTFCcDiqfz4zw5suueGRrBVhUmZsnkaTc5hlPii -YNKPkH/rJu+WvIEGfALK6r3ltEv/KPswIw9EpD3HdNUAZmM0IY24vyxhiE6822gd -5XfbpiQSHF352D2IrCX8ErE/4xcIl4a4acTTWiGipU85UCLeZfG0rB18yQW7iIOS -W/0siBKZAgMBAAECggEAfwLmBdRfl2m6JNFX0hSZpJY72kuRsN8XTnUzVHmDgfHJ -9u61POvGpnLHCjIzdjIrk0NqETBjQup1aooJQ1gWdKAYQPSsobPc7geZ+nlaI9mj -61Su1/58EBKZ6Faz/HTpnHeQbAY/OW3fmeYrBOtumBgB6/HauWH7D77Oa/lfS+Ij -4f6OVAxevsi6PUtNmNtBwk5S0lZl9SFcKeHurVindquX9vWZjBEEFtNXazJttIJS -z9KX29VYwoLfflIKaUKckn8X+wYc+3u3BvH8zJpd60yQ6MSo7Sb1XkxT9549m+JW -Cb+i1K7MC/yQo4mvDtAQIVBh8p8qpd4VjpBwMuUbgQKBgQDexuAaLO3adSYFXGwW -nom6Mz/ImYcpxYo0ouAR1talbmF5/oKl9Tcwh7l1eDHfe70gfeP+g4uwAcc1hx3a -ZtXusrJFBktFezlFQnZXaE5ppryrFWeu0he0RYLAVxnL6IlP9dYQhVsTZm+7uX5d -UP7aZtmOU9ZTEsAoqvjJQXvaCQKBgQDajPebXOxIUj8ffGTeiPZczTwXux04caDC -eFKSCbAlHWgG7mR4P3fQONfEGWNHF0CxBSrew9CHmKdPyiISaExCfUaUWDDCPQCp -UE5VAHPdjSlb4lqi+cyNVlJxBJGONtyYkbQNd6N9GHMnBS8jZi7zf8VzIXpeExA4 -n79Aml/YEQKBgDFrGId19AWD+z0xNWEHJjJB8CJFvHANvAzVHLOYXuEvzTvMs5qw -/N8tHHzsftO+lUPB6XOqJrCSlGhRYtPx//8FcPpS3Ru6rAerKKlXIB3buPqSsv9a -55s72DdmmvhayysLs8LSclOpY5vXGCsHLqGwMw6Zlm+zNyFOXAX5GspRAoGAaJMx -W68ABK8OM0OzhGQm9kriKTzIg5yjXspyQBzQo0HJ6B8kBgHgk8rPO68mOPsgYlPl -qogp/OgHjv9ahFJRwzLslckJM7g628loYfYAew+zrZrG4dsDjNG0Sw3zlAgeUAbQ -D+2iVhZf61josFiRuMP3t9paEi+vAFk4C3KSz/ECgYBpi1akpIzsYehW5uOL7Jhw -Hay5eshQ4vmHYuhDnn3gtT3h6J7TMwWs9pOygBG1I1b7GJ+tp4BZWJ2PmI7P8s45 -jdI99WODHwv03lAzjLwigoqDUDduaYqXcGghcGht5Sknkl2uYDChwLtI5JdBZ9/x -8h9dE9oAiH/KTzhPmK1E1Q== +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDqwG/uVzCIMMpe +vgpHSZCiqeH/toRwDqEqkK6I9jEf4uNWMrFGygpPCnUEmakwbSL62HX+7Osw1d85 +otywQR1+jBp8/9Y+WCF5bowYbJ0E0W07lI4w8feopNgtq0dcO6JAOzJgbHVc0XvI +OaXzUBnVdJdeVGmSvqhZS4NMc2rDzypmBRVGyl3lj2iiZrcPXf5emvI2/O38CNy0 +1o2a+gZOy+GX9Uw5Uw66DLzZZFvWfcUpHIAu+xIzLQu0WMcW3pWsXaiPSHcSKZhM +YOqSjCRLyTQNdRmIU5QMZCTYe4HdFpWL0JcAyOw+jST2wppuGnNT9gANTBR1YMaC +gBZP/Fd/AgMBAAECggEBAOevf4kColJ3nPM+qlRLJaV09yjyUOlrduLUon1oRXmL +6wUCyPXtp5j04CLnKRUzUUezZVlxKHotSr/OnfKSgXKJAgeGVEN5paf8U+YzJBFC +RIV+C4wA84WNFBKWrbo43Nx50DFcOcSet4UYaFGoJ6cFB/PAaeW7p9lhbreAXcnb +g2z4SBsGGO2ZKZkSOcXg39GKX5S0bsEhHzIOFvPBdDQpaVAx/Bq76iBzVz1bRwGp +A3SjJn0g4V+3TcTrpRzgtCRRNPM48JeLHw6/mdVSk6gIoEbg+WTB92Z7BlTbjqhI +LPoMx3wQkg7J2gRrT0rPJIMkgdK9Q8RyWaulsRAn+gECgYEA/jfisAhn7IAPBXKz +ED56NSr174qX97SO2JrB7PO614BV8lHSoV4QI+nQCBkNVhL/SuWvqB3naImo+UOX +Smo6fmh78X+yIXKKhj9qY01jxRGcrnhA25L9gk7TFSNm7XaV2HxHq+TW6tvkL0uO +jXSH3D+u8/f+5ZCt/egNq3eU4n8CgYEA7GWgBLq8jmiz/VVLYboJ0YcJVZ5XAjfJ +vgzxdzX9hIkq5Cpt1ZpO9zE3IXClVvfECXcLpEzoHbiJkEve4dDqbSe4T4JPmpRx +BKRPWFJvTieLA912UmHRBDTrQSA/nZ5zxHsXYmN52femqCwbPWYu5czoqud+GcNq +ghj4oF9UCwECgYEAzpaHx1ntakne6yR807SSB2b0GUfdm1TFyMxqz655pesK7TMF +IlGYeDbn8cy6A7rIcAsbplk21sMX6Ai/h5+wDU3He0e0cG3umI4sXKpla56WX0om +Gsnm7eA0tTbhzBPUTeshK1V6Ob2cP7r9C4MpbRjriiN8pv3eBzpu8WrqOO0CgYAB +8bgGMe75EN1iGQB8tkX8ZirqfFnk18ad/IdD3rrOCz7CD6NFnXZGzC3S5ZVGiNUg +6sy6tjM2g10GRcl4e/phmXEHnl+/OrdPPXa1mD/4GZUoG/ssJEfOzAyfRX+gcTws +goKnuX+4DjRdr7ctoxiBpVTIiwzbc2L93Oy2jPIpAQKBgGzaYhfOlB3SWo+iSKcb +Vx++0gXru1Sgeo42TIn+6adO6DYfwVtfaScVL+Jcg7MZYY+94gnPCp+/ohVTJTvQ +JZdI9Czem64VUtSvc0DCji5gPqvgsO5YgBQrGJJrbmVwu0A6RlNbDwxCdkqHuYk4 +7vsxlbXvNufb0LjnNN0lSUU2 -----END PRIVATE KEY-----