MQTTデバイスインテグレーション

概要

Things CloudのMQTT実装には、次の利点があります。

MQTTセクションの構造は次の通りです。

SmartREST ドキュメント もご覧ください。

このセクションでは、MQTT通信の基本については説明しません。MQTTに慣れていない場合は、インターネットにある数多くの入門書等を参照することをお勧めします。参考文献のいくつかはMQTTのWebサイトにございます。

備考
プラットフォームへのすべてのMQTT接続は、最大許容メッセージサイズは16384バイト(約16キロバイト)です。メッセージヘッダーと本文の両方が含まれます。ヘッダーサイズはさまざまですが、最小サイズは2バイトです。本文のサイズはトピックとペイロードを合わせたサイズです。

MQTT実装

このセクションでは、MQTTプロトコルの実装の詳細を一覧表示します。Things Cloudの実装は MQTTバージョン3.1.1をサポートしています。

MQTT標準接続

Things CloudはTCPとWebソケットの両方でMQTTをサポートしています。URLとして、利用しているテナントのドメイン(例: mytenant.je1.thingscloud.ntt.com/mqtt )、もしくは次の形式のインスタンスのドメイン mqtt.<instance_domain> を利用できます(例: mqtt.je1.thingscloud.ntt.com)。

使用可能なポート:

  TCP Webソケット
一方向SSL 8883 443
備考
クラウドシステムではポート 80 が無効になります。

ポート 8883 は、クライアント認証に証明書を使用する双方向 SSL と、クライアント認証にユーザー名とパスワードを使用する一方向 SSL の 2 種類の SSL をサポートします。 双方向 SSL サポートはデフォルトで有効になっています。 無効にするには、製品サポート にお問い合わせください。

備考
WebSocket を使用するには、パス /mqtt に接続し、WebSocket 通信の MQTT 標準 に従う必要があります。

SmartRESTペイロード

Things Cloud MQTT実装は、SmartRESTをペイロードとして使用します。

SmartRESTはCSVに似たメッセージプロトコルで、サーバーサイドのテンプレートを使用してThings Cloudでデータを作成します。 REST APIの高い表現力を取り入れていますが、組み込みデバイスのJSON解析の複雑さを回避するために、JSONをカンマ区切り値(CSV)に置き換えています。 さらに、CSVのシンプルでコンパクトな構文により、モバイルネットワークを介したIoT通信でも非常に効率的です。 他のHTTP APIと比較して、モバイルトラフィックを最大80%節約できます。

備考
プラットフォームへのすべてのMQTT接続は、最大許容メッセージサイズは16,184バイト(約16キロバイト)です。メッセージヘッダーと本文の両方が含まれます。ヘッダーサイズはさまざまですが、最小サイズは2バイトです。本文のサイズはトピックとペイロードを合わせたサイズです。

SmartRESTの基本

SmartRESTメッセージは、各パラメータがカンマで区切られた1行です。最初のパラメータは、メッセージを定義するIDです。メッセージ間に改行を使用すると、一度のパブリッシュで複数のメッセージを送信できます。

SmartRESTエスケープ

SmartRESTエンドポイントとの通信には、CSV(カンマ区切り値)形式を使用します。正常に通信するためには以下の規則に従う必要があります。

サーバーからクライアントに送信されるメッセージにも同様のエスケープ規則が適用されます。

パブリッシュの例:

100,"This value, needs escaping",This value does not need escaping

サブスクライブの例:

511,myDeviceSerial,"execute this\nand this\nand ""this"""
備考
\n は出力(例: コンソールやUI)中では新規の行を作成しません。新規の行を作成するには、改行文字(ASCII 0A)を使用する必要があります。

デバイス階層

MQTTセッションは1つのデバイスにリンクされますが、このデバイスの下には自由に設定可能なデバイス階層を持つことができます。

すべての子デバイスには、デバイスの作成時に定義された一意のIDが必要です。ルートデバイスの一意のIDと階層内の一意のIDを組み合わせて使用することをお勧めします。

ルートデバイスの代わりに子デバイスのデータを作成するには、子デバイスの一意のIDをトピックの別のセクションとして追加します(例: s/us/myChildDeviceIdentifier)。

クライアントは、それぞれのトピックをサブスクライブすることにより、階層内のすべての子デバイスに対するオペレーションを自動的に受信します。子デバイスごとにサブスクライブする必要はありません。

受信されたすべてのオペレーションには、テンプレートIDの後に、そのオペレーションが作成されたデバイス/子デバイスのIDが含まれます(その後に他のオペレーションパラメータが続きます)。

MQTT機能

MQTT認証

MQTT を使用した Things Cloud との通信は、次の 2 つの方法で認証をサポートしています。

トラブルシューティング

デバイスから正しいユーザー名とパスワードが送信されるが、同時に不正な証明書も送信される

プラットフォームが双方向 SSL をサポートするように構成されており、デバイスに無効な証明書を含む構成されたキーストアがあり、基本認証を使用したい場合は、接続中に証明書の送信をオフにすることをお勧めします。 証明書の有効期限が切れているか、ルート証明書がプラットフォームにアップロードされていないため、証明書が無効である可能性があります。 デバイスのソフトウェアでの証明書の送信をオフにしてください。 それが不可能な場合、接続を機能させるには次の点を確認してください。

MQTT ClientId

MQTT ClientIdは、接続されている各クライアントを一意に識別するフィールドです。Things Cloud実装もClientIdを使用して、クライアントを直接デバイスにリンクします。したがって、ClientIdには次の形式を使用する必要があります。

connectionType:deviceIdentifier:defaultTemplateIdentifier

フィールド 必須 説明
connectionType いいえ 接続タイプのデフォルト表示: d (デバイス)
deviceIdentifier はい デバイスの一意の識別子 (IMEI, シリアル番号など)
defaultTemplateIdentifier いいえ テンプレート識別子の詳細については、SmartREST 2.0 > MQTT 静的テンプレートをご覧ください

クライアントの最も単純なバージョンでは、MQTT ClientIdは単に deviceIdentfier にすることができます。デバイス接続として自動的に解釈されます。

重要
コロン文字はThings Cloudでは特別な意味を持つため、deviceIdentfier では使用しないでください。

ClientIdの例:

mySerialNumber
d:mySerialNumber
d:mySerialNumber:myDefaultTemplate

MQTT ClientIdの一意性は、deviceIdentfier によってのみ決定されます。したがって、上記の例では、同時に接続できるクライアントは1つだけです。 証明書によるSSL接続では、deviceIdentfier は使用されている証明書(デバイスにより提供される証明書チェーンのうち最初のもの)の「コモンネーム」と一致している必要があります。

MQTTサービス品質 (QoS)

Things Cloud実装は、MQTT QoSの3つのレベルすべてをサポートします。

オペレーショントピックまたはエラートピックをサブスクライブする場合は、トピックのサブスクライブ時にクライアントが定義したQoS内のすべてのメッセージを配信します。

MQTTクリーンセッション

MQTTクライアントは、一般にクリーンセッションを「0」(false)に設定できます。これにより、クライアントが切断された場合でもサブスクリプションが動作し、クライアントに再接続したときに失われたメッセージが受信されます。

備考
Things Cloudはクリーンセッションを「1」(true)に設定する必要があります。現在のところ、クリーンセッションを無効にしても完全に動作することは保証できませんので、常にクリーンセッションを有効にすることを推奨します。

MQTT保持フラグ

現在のThings Cloud実装では、デバイスがデータをパブリッシュするトピックへのサブスクリプションは許可されていません。このトピックに保持フラグを付けてデータをパブリッシュすることはできますが、フラグを付けずにデータを送信することと実質的な違いはありません。 Things Cloudがパブリッシュするオペレーションやエラーなどのメッセージには、保持フラグは含まれません。

MQTT last will

MQTTでは、「last will」は接続時に指定し、クライアントが接続を失ったときに実行されるメッセージです。例えば、last willメッセージとして 400,c8y_ConnectionEvent,"Device connection was lost." 、last willトピックとしてs/usを指定すると、デバイスが接続を失うたびにイベントが発生します。

備考
「last will」が実行されると、デバイスの可用性が更新されます。

MQTT リターンコード

MQTT エラーが発生した場合、プラットフォームはゼロ以外のリターンコードを含むCONNACKメッセージで応答します。 このメッセージは、問題があることを示す最初の手がかりです。 このようなリターンコードは、401 などの REST API HTTP コードと同様に扱うことができます。 これらは、予期しないエラーや権限の不足などの理由で返される可能性があります。

CONNACKは、CONNECT メッセージに対する応答であるだけでなく、プラットフォームで発生したエラーを通知する方法でもあります。 したがって、通常の接続中に直接アクションを行わなくても、このメッセージを 2 回目に受信する可能性があります。 ほとんどの MQTT クライアントは、接続を閉じる必要があるかのように0以外のコードで CONNACK を扱うため、これは接続の終了を知らせる方法でもあります。 詳細は以下をご覧ください。

以下の表は、Things Cloud によって返されるエラーのリストを示しています。

コード 正規メッセージ トラブルシューティング
0 Connection accepted 問題ありません。接続は機能しています。
1 Connection refused, unacceptable protocol version MQTT プロトコルのサポートされていないバージョン。 現在、Things Cloud では 3.1 と 3.1.1 のみが許可されています。
2 Connection refused, identifier rejected ClientId がプラットフォームに受け入れられません。
3 Connection refused, Server unavailable 一般的なプラットフォーム側のエラー。内部エラーや不明な認証の問題で受信されることもあります。
ネットワークの問題で受信できます。
このエラーは一時的なものでありデバイスの状態とは無関係であるため、通常の解決策は後で再試行することです。
4 Connection refused, bad username or password 認証情報が正しくありません (パスワードが空ではありませんが、ユーザー名やパスワードが間違っています)。 証明書を使用して認証する場合、このエラーが返されることはありません。
5          Connection refused, not authorized                    ほとんどがデバイス側に関連した問題で、デバイスにアクセス許可がない、または禁止されている操作を行っている場合に使用されます。例えば、クライアントが不正なメッセージを送信したり、メッセージの公開など、最初に認証を行わずに操作を実行しようとした場合などです。
証明書の認証に関する問題 (コモンネームが間違っている、自動登録が失敗したなど) が発生した場合にスローされます。
また、デバイス データの受信に関する一般的な問題や、プラットフォーム上のデバイスの状態に関連するその他の認証の問題でもスローされます。 例えば、デバイスのマネージドオブジェクト問題や、アクセス権限が突然削除された場合などです。この状況では、プラットフォーム上で調査と修正の適用を行う必要がある場合があります。
MQTT バージョン 3.1 を使用している場合、clientId が長すぎると、このエラーが発生する可能性があります。 これは、clientId が 24 文字以上の場合に発生する可能性があります。
最後に、特に接続中のパフォーマンスの問題など、予期しない例外が発生した場合にもスローされる可能性があります。 したがって、一時的なパフォーマンスの問題を解決するには、接続を数回繰り返すことが良い方法です。

公式の MQTT 接続リターン コードの詳細については、MQTT バージョン 3.1.1 > 3.2 CONNACK - Acknowledge connection request をご覧ください。

デバッグ

開発中に開発者をサポートするために、トピックs/eをサブスクライブできます。このトピックでは、デバイスからパブリッシュ中に発生するデバッグメッセージおよびエラーメッセージを取得できます。

備考
このトピックは、純粋にクライアントの開発をサポートするために設計されています。メッセージが冗長であり、データ使用量を大幅に増加させる可能性があるため、常にこのチャネルをサブスクライブすることはお勧めしません。また、このトピックを使用して、そのトピックで受信した内容に基づいてデバイスのアクションをトリガーしないでください。レスポンスチャネルではありません。

MQTT ブローカー証明書

MQTT ブローカーは、メイン環境ドメインに割り当てられた証明書を使用します。 MQTT ブローカーは、常にTLS ハンドシェイク中に、これらの証明書をデバイスに送信します。

デバイス証明書

概要

デバイスは、X.509 クライアント証明書を使用して Things Cloud プラットフォームに対して認証を行えます。

デバイスはプラットフォームの MQTT インターフェースを使用して通信できますが、WebSocket 上のMQTT はサポートされていません。Things Cloudプラットフォームは、デバイスがポート8883でSSLを使用して接続することを想定しています。

各テナントは、ベースCA証明書をアップロードすることで、誰を信頼するかを個別に定義しています。

証明書を使ってプラットフォームに接続するデバイスは、テナントID、ユーザー名、パスワードを提供する必要はありません。認証情報は、証明書から取得されます。

証明書を使用してデバイスを接続するための一般的な要件

証明書を使用したデバイス登録

Things Cloudでは、証明書を使って接続するデバイスを登録する方法として、以下の2つの方法をサポートしています。

自動登録

デバイス証明書が、true の値を持つ autoRegistrationEnabled フラグで Things Cloud プラットフォームにアップロードされた信頼できる証明書から派生する場合、デバイスのユーザーは最初の MQTT コール中に作成されます。 アップロードされた証明書に対して自動登録を有効にする必要があります。 自動登録が有効になっていない場合は、一括登録を使用する必要があります (下記参照)。 UIでアップロードされた証明書の自動登録フィールドを管理するには、デバイス管理 > デバイスデータの管理 > 信頼できる証明書の管理をご覧ください。

一括登録

デバイスのユーザーは、デバイス管理の標準的な一括登録からも作成することができます。

一括登録で使用するCSVファイルは、Things Cloud OpenAPI仕様の一括デバイス認証情報リクエストを作成するに記載されている要件を満たしている必要があります。また、CSVファイルには、CERTIFICATES の値を持つAUTH_TYPE列が追加されており、CREDENTIALS列が存在しないか、値が空であることが必要です。

単一登録

単一登録は、認証に証明書を使用するデバイスではサポートされていません。

備考
デバイスの登録中に、デバイスとプラットフォームの通信に必要なデバイス ユーザーが作成されます。

JWTトークンの取得

証明書によって認証され、Things Cloud プラットフォームに接続されているデバイスは、後で HTTP リクエストの認証に使用できるトークンを受け取ることができます。

71,<<Base64 encoded JWT token>>

デバイストークンの有効期限は、テナントオプションで設定することができ、カテゴリは oauth.internal 、キーは device-token.lifespan.seconds です。 デフォルト値は1時間で、最小許容値は5分です。 詳細は、Things Cloud OpenAPI仕様のTenant APIを参照してください。

デバイスは、トークンの有効期限の半分が経過した後に JWT トークンをリクエストした場合、古いデバイストークンの有効期限が切れる前に新しいデバイストークンを取得できます。

X.509 証明書の概要

X.509 は公開鍵証明書を定義する標準であり、安全な接続とデータ転送を提供するために SSL プロトコルで一般的に使用されます。 バージョン 3 が 1995 年以降の最新です。

X.509 証明書の一般的な目的は、ID を鍵のペアにバインドすることです。公開鍵は証明書の一部として公開され、秘密鍵は証明書の所有者のみが知ることができます。 このような鍵のペアは、現在では安全であると考えられている非対称鍵アルゴリズムで作成する必要があります。 このようなアルゴリズムの例として、少なくとも2,048ビットのキーサイズを持つRSAがあります。 1,024ビット以下は、もはや安全とは見なされません。 秘密鍵は、2つの方法で使用することができます。

すべての証明書は自己署名することも、別の証明書で署名することもできます。 証明書が自己署名であるかどうかを確認するには、証明書の「発行者名」フィールドと「サブジェクト名」フィールドを確認します。 この2つが同じであれば自己署名証明書であることを意味し、そうでなければ、証明書は発行者によって署名されていると主張します。 発行者が本当に証明書に署名したかどうかを確認するには、証明書の「署名」フィールドを確認する必要があります。 発行者の公開鍵で復号化した後、署名は署名された証明書のデータと一致するはずです。 別の人が証明書に署名するということは、発行者の証明書が信頼できる場合は、署名された証明書も信頼できることを意味します。

例えば、プラットフォームがお客さまの証明書を信頼しており、お客さまが個別の証明書を持つ 20 台のデバイスを持っている場合、それらを 1 つずつアップロードする必要はありません。

これらのデバイス証明書がお客さまの証明書によって署名されている場合、プラットフォームはそれらも信頼する必要があります。 この場合、すべてのデバイスは、SSL ハンドシェイク中に独自の証明書だけでなく、証明書のチェーン全体 (いわゆる信頼のチェーン) を送信する必要があります。

証明書のチェーンは、デバイスに属する証明書から始まり、プラットフォームによって信頼される CA 証明書に到達するまで、使用されるすべての中間証明書を経由します。 通常、証明書のチェーンには信頼できる CA 証明書が含まれている必要はないため、CA によって直接署名された証明書で終わることができます。 しかし、Things Cloud プラットフォームでは、証明書チェーンに信頼できる CA 証明書を提供することも必要です。

証明書のチェーンを提供すると、プラットフォームはチェーン内のすべての証明書の署名を検証して、デバイス証明書が信頼された証明書によって直接または間接的に署名されていることを確認できます。 証明書のチェーンの長さは異なる場合があるため、プラットフォームが証明書 A を信頼し、証明書 B が A によって署名され、証明書 C が B によって署名されている場合、証明書 C も信頼されます。 ただし、注意すべき点がいくつかあります。

バージョン3のX.509証明書の構造は、以下のようになります。

X.509 証明書による認証がどのように機能するかを示すために、簡略化された相互 SSL ハンドシェイクの例を示します。

Simplified mutual SSL handshake

サーバーは、クライアントの公開鍵を使用してメッセージの暗号化されたコピーを復号化した後、クライアントが送信した証明書の所有者であることを認識します。 サーバーが正しいセッションキーを使用している場合、クライアントは、サーバーが秘密鍵を使用して証明書を復号化したことを意味するため、送信した証明書の所有者がサーバーであることを認識します。 ユーザー名とパスワードによる Basic 認証では、ユーザーを認証するためにパスワードをネットワーク経由で送信する必要があります。 証明書を使用する場合、秘密鍵は送信されないため、証明書の使用はユーザー名とパスワードによる Basic 認証よりもはるかに安全になります。

X.509証明書には3つの重要な用語があり、これからX.509証明書を使う人は誰もが知っておく必要があります。

キーストアとトラストストアは同じファイルに保存できますが、セキュリティ上の理由から、別々に保存することをお勧めします。 トラストストアには公開データが含まれ、キーストアには所有者のみが知る必要がある秘密鍵が含まれます。 これらのファイルの最も一般的な形式は次のとおりです。

証明書の生成と署名

証明書を生成するには、OpenSSL ツールキットを使用します。 まだインストールしていない場合は、次のWebサイトからダウンロードすることができます。
https://www.openssl.org/source/

自己署名CA証明書の作成

  1. ルート証明書と署名構成用のディレクトリを作成します。次に例を示します。

    mkdir /home/user/Desktop/caCertificate

  2. 作成したディレクトリに移動し、CA 証明書の構成ファイルを作成します。

    touch caConfig.cnf

  3. CAによって署名された証明書の履歴を保持するためのデータベースファイルを作成します。

    touch database.txt

  4. 署名付き証明書の識別に使用される初期シリアル番号を含むシリアルファイルを作成します。このシリアルを署名付き証明書に割り当てると、このファイル内の値が自動的に増加します。

    echo 1000 > serial

  5. 署名付き証明書と証明書失効リスト用のサブディレクトリを作成します。

    mkdir deviceCertificates crl

  6. 設定ファイルに記入します。 これは設定例であり、ディレクトリ dir を独自のディレクトリに変更した後、テストに使用することができます。 本番環境で使用する場合は、まずセキュリティーの専門家に相談してください。

    [ ca ]
    default_ca = CA_default
    [ CA_default ]
    # Directory and file locations.
    dir               = /home/user/Desktop/caCertificate
    certs             = $dir # directory where the CA certificate will be stored.
    crl_dir           = $dir/crl # directory where the certificate revocation list will be stored.
    new_certs_dir     = $dir/deviceCertificates # directory where certificates signed by CA certificate will be stored.
    database          = $dir/database.txt # database file, where the history of the certificates signing operations will be stored.
    serial            = $dir/serial # directory to the file, which stores next value that will be assigned to signed certificate.
    
    # The CA key and CA certificate for signing other certificates.
    private_key       = $dir/caKey.pem # CA private key which will be used for signing certificates.
    certificate       = $dir/caCert.pem # CA certificate, which will be the issuer of signed certificate.
    
    default_md        = sha256 # hash function
    default_days      = 375 # default number of days for which the certificate will be valid since the date of its generation.
    preserve          = no # if set to 'no' then it will determine the same order of the distinguished name in every signed certificate.
    policy            = signing_policy # the name of the tag in this file that specifies the fields of the certificate. The fields must be filled in or even match the CA certificate values to be signed.
    
    # For certificate revocation lists.
    crl               = $crl_dir/caCrl.pem # CA certificate revocation list
    crlnumber         = $crl_dir/crlnumber # serial, but for the certificate revocation list
    crl_extensions    = crl_ext # the name of the tag in this file, which specifies certificates revocation list extensions, which will be added to the certificate revocation by default.
    default_crl_days  = 30 # default number of days for which the certificate revocation list will be valid since the date of its generation. After that date it should be updated to see if there are new entries on the list.
    
    [ req ]
    default_bits        = 4096 # default key size in bits.
    distinguished_name  = req_distinguished_name # the name of the tag in this file, which specifies certificates fields description during certificate creation and eventually set some default values.
    string_mask         = utf8only # permitted string type mask.
    default_md          = sha256 # hash function.
    x509_extensions     = v3_ca # the name of the tag in this file, which specifies certificates extensions, which will be added to the created certificate by default.
    
    # descriptions and default values of the created certificate fields.
    [ req_distinguished_name ]
    countryName                     = Country Name (2 letter code)
    stateOrProvinceName             = State or Province Name
    localityName                    = Locality Name
    organizationName                = Organization Name
    organizationalUnitName          = Organizational Unit Name
    commonName                      = Common Name
    emailAddress                    = Email Address
    
    # A default value for each field can be set by adding an extra line with field name and postfix "_default". For example: "countryName_default = PL". If you add this line here, then leaving country name empty during certificate creation will result in the value "PL" being used. If the default value was specified there, but during certificate creation you do not want to use this value, then instead use "." as the value. It will leave the value empty and not use the default.
    
    # default extensions for the CA certificate.
    [ v3_ca ]
    subjectKeyIdentifier = hash # subject key value will be calculated using hash funtion. It's the recommended setting by PKIX.
    authorityKeyIdentifier = keyid:always,issuer # The subject key identifier will be copied from the parent certificate. It's the recommended setting by PKIX.
    basicConstraints = critical, CA:true, pathlen:10 # "critical" specifies that the extension is important and must be read by the platform. CA says if it is the CA certificate so it can be used to sign different certificates. "pathlen" specifies the maximum path length between this certificate and the device certificate in the chain of certificates during authentication. Path length is set here only to show how it is done. If you do not want to specify max path length, you can keep only the "basicConstraints = critical, CA:true" part here.
    keyUsage = digitalSignature, cRLSign, keyCertSign # specifies permitted key usages.
    
    # Default extensions for the device certificate. This tag is not used directly anywhere in this file, but will be used from the command line to create signed certificate with "-extensions v3_signed" parameter.
    [ v3_signed ]
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    basicConstraints = critical, CA:false
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    
    # default extensions for certificate revocation list
    [ crl_ext ]
    authorityKeyIdentifier=keyid:always
    
    # Policy of certificates signing. It specifies which certificate fields must be filled in during certificate creation. There are three possible values here:
    # "optional" - field value can be empty
    # "supplied" - field value must be filled in
    # "match" - signed certificate field value must match the CA certificate value to be created
    [ signing_policy ]
    countryName             = optional
    stateOrProvinceName     = optional
    organizationName        = optional
    organizationalUnitName  = optional
    commonName              = supplied # every certificate should have a unique common name, so this value should not be changed.
    emailAddress            = optional
    
  7. 暗号化方式が aes256 で、長さが 2,048 ビット以上の秘密鍵を作成します。作成時には鍵のパスワードを設定するよう求められます。

    openssl genrsa -aes256 -out caKey.pem 4096

  8. 構成ファイルの仕様を使用して自己署名証明書を作成します。「days」パラメータは、この証明書が生成されてから有効期限を示すため、お好みに応じて設定してください。

    openssl req -config caConfig.cnf -key caKey.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out caCert.pem

  9. 次のコマンドを使用して、作成した証明書を印刷できます。

    openssl x509 -noout -text -in caCert.pem

中間証明書の作成

中間証明書は CA 証明書によって署名されますが、デバイス証明書の署名にも使用されます。 このステップは任意です。 すべてのデバイス証明書を1つの共通のCA証明書で署名しても問題ない場合は、このステップをスキップできます。 ただし、CA 証明書とデバイス証明書の間にいくつかの証明書が必要な場合は、このステップを実行することが適切です。 Things Cloudクラウドでは、セキュリティー上の理由から証明書のチェーンの最大長が現在2に制限されているため、CA証明書とデバイス証明書の間で中間証明書を使用できないことに注意してください。 ただし、専用インストールの場合は、プラットフォーム全体の構成設定を変更し、証明書チェーンの許容最大長を 2 より長くすることで、この動作を変更できます。 中間証明書を作成するには、次の手順を実行します。

  1. caCertificate パス内に中間証明書用の新しいディレクトリを作成します: mkdir intermediateCertificate
  2. このディレクトリに移動し、中間証明書の構成ファイルを作成します: touch intermediateConfig.cnf
  3. 中間証明書によって署名された証明書の履歴を保持するためのデータベース ファイルを作成します: touch database.txt
  4. 署名付き証明書を識別するために使用される、初期シリアル番号を含むシリアルファイルを作成します: echo 1000 > serial
  5. 署名付き証明書と証明書失効リスト用のサブディレクトリを作成します: mkdir deviceCertificates crl
  6. CA 構成と同じように構成ファイルに記入しますが、一般ディレクトリ (「dir」) をintermediateCertificate フォルダに変更することを忘れないでください。また、private_key、certificates、crlの名前を、現在の「ca」プレフィクスから「intermediate」プレフィクスに変更しなければなりません(例: caKey.pem -> intermediateKey.pem)。次のステップで、このプレフィクスを持つファイルを生成するためです。
  7. 中間証明書の秘密鍵を生成します: openssl genrsa -aes256 -out intermediateKey.pem 4096
  8. 証明書署名リクエストを生成します: openssl req -config intermediateConfig.cnf -new -sha256 -key intermediateKey.pem -out intermediateCsr.pem
  9. caCertificate ディレクトリに移動し、署名付き中間証明書を生成します。構成ファイルで指定された秘密鍵が証明書の署名に使用されるため、ここでは CA 構成を使用する必要があります: openssl ca -config caConfig.cnf -extensions v3_ca -days 3650 -notext -md sha256 -in intermediateCertificate/intermediateCsr.pem -out intermediateCertificate/intermediateCert.pem
  10. 生成された証明書が CA によって正しく署名されているかどうかを確認します: openssl verify -CAfile caCert.pem intermediateCertificate/intermediateCert.pem

CAまたは中間認証局によって署名されたデバイス証明書の作成

  1. デバイス証明書の署名にどちらが使用されているかに応じて、caCertificate またはintermediateCertificate のディレクトリに移動します。
  2. 新しい証明書の秘密鍵を生成します: openssl genrsa -aes256 -out deviceCertificates/deviceKey.pem 4096
  3. 証明書署名リクエストを生成します (intermediateCertificate ディレクトリにいる場合は、「caConfig.cnf」を「intermediateConfig.cnf」に変更します):
    openssl req -config caConfig.cnf -new -sha256 -key deviceCertificates/deviceKey.pem -out deviceCertificates/deviceCsr.pem コンソールで指定するように求められるデバイス証明書の commonName は、接続中にデバイスの ClientId と一致する必要があることに注意してください。
  4. CA または中間認証局によって署名された証明書を生成します (intermediateCertificate ディレクトリにいる場合は、「caConfig.cnf」を「intermediateConfig.cnf」に変更します): openssl ca -config caConfig.cnf -extensions v3_signed -days 365 -notext -md sha256 -in deviceCertificates/deviceCsr.pem -out deviceCertificates/deviceCert.pem
  5. 生成された証明書が CA または中間認証局によって正しく署名されているかどうかを確認します (intermediateCertificate ディレクトリにいる場合は、「caCert.pem」を「intermediateCert.pem」に変更します): openssl verify -partial_chain -CAfile caCert.pem deviceCertificates/deviceCert.pem

証明書チェーンの作成

caCertificate ディレクトリに移動します。

CA 証明書を作成し、それを中間証明書の署名に使用し、中間証明書を使用してデバイス証明書の署名を行った場合は、次のコマンドを使用してチェーンを作成します。

cat intermediateCertificate/deviceCertificates/deviceCert.pem intermediateCertificate/intermediateCert.pem caCert.pem > intermediateCertificate/deviceCertificates/deviceCertChain.pem

中間証明書を使用しない場合のコマンドは次の通りです。

cat deviceCertificates/deviceCert.pem caCert.pem > deviceCertificates/deviceCertChain.pem

CA証明書とデバイス証明書の間に複数の中間証明書を使用する場合、チェーン作成時に正しい順序を保つ必要があることを忘れないでください(すべての証明書の後に、その証明書によって署名されている証明書が続く必要があります)。

キーストアとトラストストアの作成

  1. デバイスの秘密鍵と生成された証明書のチェーンを使用して、deviceCertificates ディレクトリに移動します。CA証明書とデバイス証明書の間に中間証明書を使用している場合は、caCertificate/intermediateCertificate/deviceCertificatesがパスになり、それ以外の場合はcaCertificate/deviceCertificatesになります。生成された証明書のチェーンとデバイスの秘密鍵を使用してキーストアを作成します: openssl pkcs12 -export -name devicekeyentry -inkey deviceKey.pem -in deviceCertChain.pem -out deviceKeystore.pkcs12
  2. キーストアを JKS 形式に変換する場合は、通常 Java Development Kit と一緒にダウンロードされる Java Keytool が必要になります: keytool -importkeystore -srckeystore deviceKeystore.pkcs12 -srcstoretype PKCS12 -destkeystore deviceKeystore.jks -deststoretype JKS
  3. サーバー証明書がない場合は、次のコマンドで取得します: openssl s_client -showcerts -connect <cumulocity url>:<mqtt mutual ssl port (currently 8883, but that can be changed in the future)> | openssl x509 -outform PEM > serverCertificate.pem
  4. これで、サーバー証明書を格納するトラストストアを作成することができます。これはJava Keytoolで作成する必要があります (opensslはトラストストアの作成をサポートしていないので、Java keytoolを使用しない場合は、すべての信頼できる証明書を別のPEMファイルに保持する必要があります)。 alias は、すべてのキーストアまたはトラストストア エントリの一意の識別子であることに注意してください。これは、同じトラストストアに 2 番目の信頼できる証明書を追加する場合は、以下のコマンドのエイリアスを servercertificate から別の名前に変更する必要があることを意味します:
    • PKCS12 形式の場合: keytool -importcert -noprompt -keystore deviceTruststore.pkcs12 -alias servercertificate -file serverCertificate.pem
    • JKS 形式の場合: keytool -import -file serverCertificate.pem -alias servercertificate -keystore deviceTruststore.jks
  5. オプションとして、トラストストアの新しいファイルを作成する代わりに、作成したキーストアに信頼できる証明書を追加し、すべてを 1 つのファイルに保存できますが、これは推奨される解決策ではありません:
    • キーストアが PKCS12 形式の場合: keytool -importcert -noprompt -keystore deviceKeystore.pkcs12 -alias servercertificate -file serverCertificate.pem
    • キーストアが JKS 形式の場合: keytool -import -file serverCertificate.pem -alias servercertificate -keystore deviceKeystore.jks
  6. 次のコマンドを使用して、キーストア (またはトラストストア) の内容を確認できます: keytool -list -v -keystore deviceKeystore.jks

MQTT クライアントで作成した証明書をテストする方法

キーストアとトラストストア

まだキーストアとトラストストアを生成していない場合は、証明書の生成と署名の説明に従って行います。

CA 証明書のアップロード

CA(または中間)証明書をプラットフォームにアップロードします。この操作により、アップロードされた証明書がサーバーのトラストストアに追加されます。この操作には2つの方法があり、どちらもROLE_TENANT_ADMINまたはROLE_TENANT_MANAGEMENT_ADMINのいずれかのロール要件があります。

UIの場合

  1. デバイス管理アプリケーションを開き、管理メニューに移動して信頼できる証明書を選択します。
  2. caCert.pem (またはintermediateCert.pem) をドロップします。
  3. 自動登録フィールドを確認します。
  4. 証明書のステータスをクリックして有効に設定します。
  5. カスタム名を挿入します。
  6. 証明書を追加をクリックします。

証明書の追加を除くすべての手順を完了すると、フォームは次のようになります:

Trusted certificate addition

その後、追加された証明書が表示されるはずです。

Trusted certificate added

REST APIの場合

  1. Things Cloud プラットフォームにアップロードする CA (または中間) 証明書を表示し、その PEM 値をコピーします。この値は、「—–BEGIN CERTIFICATE—–」で始まり、「—–END CERTIFICATE—–」で終わります (ハイフン含む)。改行記号(\n)が各行の末尾に自動的に追加された場合は、それを削除します: openssl x509 -in caCert.pem -text
  2. POSTリクエストを介してプラットフォームに送信します。
    POST /tenant/tenants/<TENANT_ID>/trusted-certificates
    Host: https://<TENANT_DOMAIN>/
    Authorization: Basic <YOUR_AUTHENTICATION>
    Content-Type: application/json
    {
        "status" :  "ENABLED",
        "name" : "certificateName",
        "autoRegistrationEnabled" : "true",
        "certInPemFormat" : "<CERT_PEM_VALUE>"
    }

MQTT クライアント例

x.509 証明書を使用してプラットフォームに接続する、Java で実装された Things Cloud MQTT サンプル クライアントのコードは、ここから入手できます:
https://github.com/SoftwareAG/cumulocity-examples/tree/develop/mqtt-client
このサンプル クライアントでは、Eclipse Paho の実装が使用されています。詳細については、Webサイト(https://www.eclipse.org/paho/index.php?page=documentation.php) で説明されています。

以下は、Eclipse Paho クライアントを使用するために必要な依存関係を Maven に追加する方法を示す例です。

<dependency>
    <groupId>org.eclipse.paho</groupId>
    <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
    <version>${paho.version}</version>
</dependency>

次に、MQTT クライアントのインスタンスを 1 行で作成できます。

MqttClient mqttClient = new MqttClient(BROKER_URL, "d:" + CLIENT_ID, new MemoryPersistence());

BROKER_URL には、ssl://<cumulocity url>:8883 のように、クライアントが接続するプロトコル、URL、ポートが含まれている必要があります。 CLIENT_ID 値は、使用されるデバイス証明書のコモンネームの値と一致する必要があります。 「d:」プレフィクスは、デバイス接続のために Things Cloud で使用されているため、削除または変更しないでください。 SSL 接続を確立するために設定する必要があるのは、コードフラグメントにパスを入力することだけです。

sslProperties.put(SSLSocketFactoryFactory.KEYSTORE, getClass().getClassLoader().getResource(KEYSTORE_NAME).getPath());
sslProperties.put(SSLSocketFactoryFactory.KEYSTOREPWD, KEYSTORE_PASSWORD);
sslProperties.put(SSLSocketFactoryFactory.KEYSTORETYPE, KEYSTORE_FORMAT);
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTORE, getClass().getClassLoader().getResource(TRUSTSTORE_NAME).getPath());
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTOREPWD, TRUSTSTORE_PASSWORD);
sslProperties.put(SSLSocketFactoryFactory.TRUSTSTORETYPE, TRUSTSTORE_FORMAT);

このデータを入力すると、サンプルクライアントは提供されたデータを使用し、証明書を用いて指定されたプラットフォームに接続します。 この例では、接続用のコールバックを作成する方法も示しています。 まず最初に、インターフェース MqttCallbackExtended を実装するクラスを作成します。 次に、このようなクラスを作成し、そのインスタンスを MQTT クライアントに提供できます: mqttClient.setCallback(this);

一般的に、MQTT Eclipse Paho クライアントは Java Development Kit の一部である Java Secure Socket Extension を使用して SSL によるセキュアな接続を提供します。 JSSE は、開発者がそのクラスを使用して構成できる SSL および TLS プロトコルの Java 実装を提供します。 Java Secure Socket Extension のドキュメントでは、SSL 接続がどのように確立されるかを示し、実装をカスタマイズする例をいくつか提供しています。 完全なドキュメントは、Oracle 公式 Webサイト で入手できます。

デバイスインテグレーション

概要

デバイスをThings Cloud に統合するための基本的なライフサイクルについては、デバイスのインターフェースで説明しています。

このセクションでは、MQTT実装を使用してこのライフサイクルを管理する方法を説明します。

ライフサイクルは、起動フェーズとサイクルフェーズの2つのフェーズで構成されます。

起動フェーズは、認証情報を確認するだけで済みます。

サイクルフェーズは、次の2種類のアクションで構成されます。

MQTTフェーズ

スタートアップフェーズ

ステップ0: デバイスの認証情報をリクエストする

Things Cloudでは、すべてのMQTT接続を認証する必要があります。MQTT実装のデバイス認証トピックを使用して、デバイスの新しい認証情報を生成できます。

Things Cloudが提供している静的認証情報を使ってデバイスが認証情報を取得したら、その認証情報をローカルに保存して、取得した認証情報を使い接続する必要があります。

接続を確立するには、次のパラメータを構成する必要があります。

詳細については、Hello MQTTセクションをご覧ください。

プロセスは次の通りです。

認証情報を受信した後、デバイスはMQTT接続を閉じて、受信した認証情報で新しい接続を作成できます。

ステップ1: デバイスを確認する

クライアントがデータを送信しデバイスが存在しないときに MQTTはデバイスの自動作成をサポートするため、このステップはデバイスを手動で作成する場合にのみ必要です。

デバイスの作成は、静的テンプレート100を使用することで実現できます。このテンプレートは、デバイスがまだ存在しない場合にのみ作成するため、デバイスのブートのたびに何もせずに使用できます。

デバイスは、クライアントがMQTT ClientIdで使用するIDに自動的にリンクされます。

100,Device Name,Device Type
備考
Things Cloudがあらかじめ用意している静的テンプレートに使われているトピックはs/usです。

ステップ2: 子デバイスを確認する

ルートデバイスと同様に、その子デバイスも自動デバイス作成の対象となります。

このステップを手動で行うには、子デバイスを作成するための静的テンプレート101を送信します。テンプレートは、子デバイスが存在しない場合にのみ作成します。

101,Unique Child ID,Child Name,Child Type

ステップ3: トピックをサブスクライブする

デバイスがオペレーションをサポートしている場合は、必要なすべてのトピック(静的テンプレートおよびSmartREST 2.0)をサブスクライブする必要があります。

サイクルフェーズ

ステップA: CSVデータを送信する

デバイスはアクティブなMQTT接続を保持していますが、静的テンプレートのトピックまたはSmartRESTテンプレートのトピックのいずれかでパブリッシュして、サーバーにデータを送信できます。

MQTT ClientIdに基づいて、物理デバイスはThings Cloud内のデバイスオブジェクトに直接接続されます。したがって、送信するデータは自動的にデバイスに接続されます。

子デバイスにデータを送信するには、デバイス階層で説明されているトピックにデータをパブリッシュします。

ステップB: CSVオペレーションを受信する

トピックをサブスクライブすると、デバイスは自動的にThings Cloudにオペレーションを受け取りたいことを伝えます。作成されたオペレーションは、静的テンプレートまたはデバイスが定義するテンプレートを使用して自動的に解析されます。