0
0

AndroidでのMQTTのTLS/SSLによる認証ガイダンス

Posted at

AndroidでのMQTTのTLS/SSLによる認証についてのステップ・バイ・ステップ・ガイド

AndroidでのMQTTのTLS/SSLによる認証についてのステップ・バイ・ステップ・ガイド

MQTTは、IoTデバイス開発者のために柔軟性とハードウェア/ネットワークリソースのバランスを取ることを目的とした、軽量かつ柔軟なIoTメッセージ交換とデータ転送プロトコルです。安全な通信を確保するために、通信暗号化にTLS/SSLがよく利用されます。

この記事では、主にAndroidとMQTTを用いてTLS/SSLの一方向および双方向認証を行う方法について説明します。

準備

この記事では、Eclipse Paho Android ServiceBouncyCastle を使用して依存関係を追加します。

dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    implementation 'org.bouncycastle:bcpkix-jdk15on:1.59' 
}

AndroidがTLS/SSLを使って接続するためのコアコードセクションは以下の通りです。

MqttConnectOptions options = new MqttConnectOptions();
SSLSocketFactory sslSocketFactory = ...
options.setSocketFactory(sslSocketFactory);

フォーカスはSSLSocketFactoryの取得方法です。一方向認証と双方向認証について以下で説明します。

一方向認証

一方向認証は、サーバ側がクライアントを認証することを意味します。コアコードは次の通りです。

public static SSLSocketFactory getSingleSocketFactory(InputStream caCrtFileInputStream) throws Exception {
    Security.addProvider(new BouncyCastleProvider());
    X509Certificate caCert = null;

    BufferedInputStream bis = new BufferedInputStream(caCrtFileInputStream); 
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    while (bis.available() > 0) {
        caCert = (X509Certificate) cf.generateCertificate(bis);
    }
    KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
    caKs.load(null, null);
    caKs.setCertificateEntry("cert-certificate", caCert);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(caKs);
    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, tmf.getTrustManagers(), null);
    return sslContext.getSocketFactory();
}

res/rawca.crtを置き、次のように呼び出します。

try {
    InputStream caCrtFileI = context.getResources().openRawResource(R.raw.ca); 
    options.setSocketFactory(getSingleSocketFactory(caCrtFile));
} catch (Exception e) {
    e.printStackTrace();
}

双方向認証

双方向認証は、サーバ側とクライアントが互いを認証することを意味します。コアコードは次の通りです。

public static SSLSocketFactory getSocketFactory(InputStream caCrtFile, InputStream crtFile, InputStream keyFile, 
                                                    String password) throws Exception {
    Security.addProvider(new BouncyCastleProvider());

    // CA証明書のロード
    X509Certificate caCert = null; 

    BufferedInputStream bis = new BufferedInputStream(caCrtFile);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    while (bis.available() > 0) {
        caCert = (X509Certificate) cf.generateCertificate(bis);
    }
    
    // クライアント証明書のロード 
    bis = new BufferedInputStream(crtFile);
    X509Certificate cert = null;
    while (bis.available() > 0) {
        cert = (X509Certificate) cf.generateCertificate(bis);
    }

    // クライアント秘密鍵のロード
    PEMParser pemParser = new PEMParser(new InputStreamReader(keyFile));
    Object object = pemParser.readObject();
    JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
    KeyPair key = converter.getKeyPair((PEMKeyPair) object);

    KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
    caKs.load(null, null);
    caKs.setCertificateEntry("cert-certificate", caCert);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(caKs);

    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null, null);
    ks.setCertificateEntry("certificate", cert);
    ks.setKeyEntry("private-cert", key.getPrivate(), password.toCharArray(), 
            new java.security.cert.Certificate[]{cert});
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(ks, password.toCharArray());

    SSLContext context = SSLContext.getInstance("TLSv1.2");
    context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    return context.getSocketFactory();
}

サーバ側の証明書が必要で、クライアントの証明書と秘密鍵をres/raw配下に置き、次のように呼び出します。パスワードは空文字列に設定することに注意してください。

try {
  InputStream caCrtFile = context.getResources().openRawResource(R.raw.ca);
  InputStream crtFile = context.getResources().openRawResource(R.raw.cert); 
  InputStream keyFile = context.getResources().openRawResource(R.raw.key);
  options.setSocketFactory(getSocketFactory(caCrtFile, crtFile, keyFile, "")); 
} catch (Exception e) {
  e.printStackTrace();
}

以上が、AndroidのMQTTでTLS/SSLの一方向および双方向認証を実行する方法です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0