2020년 11월 9일 월요일

[java] tls 를 적용한 mqtt java 클라이언트 샘플

이전 포스팅에서 mqtt 프로토콜을 구현한 mosquitto 브로커 를 셋팅 하였다

이제 자바 데몬 프로젝트 내에서 mqtt 프로토콜 메세지 송수신을 위한 mqtt 클라이언트 코드를

작성 할 것이다.


1. 일단 서버에서 생성한 인증서 관련 파일(ca.crt, client.crt, client.key) 세가지를 서버로부터 

다운로드 한다.


2. 이클립스에서 maven java 프로젝트 를 생성하고 /src/main/resources 경로에

ca 폴더를 만든다.


3. 서버에서 다운받은 ca.crt,client,crt,client.key 파일을 2번에서 생성한 ca 폴더에 위치

시킨다. (인증서 관련 파일명은 이름은 다를수 있음)


4. SslUtil class 파일 작성 (본인은 kr.co.alan.utils 패키지에 작성 하였다) 

package kr.co.alan.utils;


import java.io.ByteArrayInputStream;

import java.io.InputStreamReader;

import java.security.KeyPair;

import java.security.KeyStore;

import java.security.Security;

import java.security.cert.X509Certificate;


import javax.net.ssl.KeyManagerFactory;

import javax.net.ssl.SSLContext;

import javax.net.ssl.SSLSocketFactory;

import javax.net.ssl.TrustManagerFactory;


import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.openssl.PEMReader;

import org.bouncycastle.openssl.PasswordFinder;


public class SslUtil {

    // public static SSLSocketFactory getSocketFactory (final URI caCrtFile, final URI crtFile, final URI keyFile, final String password, String tlsVersion) {

    // try {

    // System.out.println("####################### getSocketFactory ########################");

    // System.out.println("caCrtFile : " + caCrtFile);

    // System.out.println("crtFile : " + crtFile);

    // System.out.println("keyFile : " + keyFile);

    // System.out.println("####################### getSocketFactory ########################");

    //

    // Security.addProvider(new BouncyCastleProvider());

    //     // load CA certificate

    //     PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));

    //     X509Certificate caCert = (X509Certificate)reader.readObject();

    //     reader.close();

    //     // load client certificate

    //     reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));

    //     X509Certificate cert = (X509Certificate)reader.readObject();Q

    //     reader.close();

    //     // load client private key

    //     reader = new PEMReader(

    //             new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),

    //             new PasswordFinder() {

    //                 public char[] getPassword() {

    //                     return password.toCharArray();

    //                 }

    //             }

    //     );

    //     KeyPair key = (KeyPair)reader.readObject();

    //     reader.close();

    //     KeyStore caKs = KeyStore.getInstance("JKS");

    //     caKs.load(null, null);

    //     caKs.setCertificateEntry("ca-certificate", caCert);

    //     TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");

    //     tmf.init(caKs);

    //     KeyStore ks = KeyStore.getInstance("JKS");

    //     ks.load(null, null);

    //     ks.setCertificateEntry("certificate", cert);

    //     ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});

    //     KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");

    //     kmf.init(ks, password.toCharArray());

    // SSLContext context = SSLContext.getInstance(tlsVersion);

    //     context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    //     return context.getSocketFactory();

    // }catch (Exception e) {

    // e.printStackTrace();

    // // TODO: handle exception

    // return null;

    // }

    //

    // }


    // public static SSLSocketFactory getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile, final String password, String tlsVersion) {

    // try {

    //

    //

    // System.out.println("####################### getSocketFactory ########################");

    // System.out.println("caCrtFile : " + caCrtFile);

    // System.out.println("crtFile : " + crtFile);

    // System.out.println("keyFile : " + keyFile);

    // System.out.println("####################### getSocketFactory ########################");

    //

    // Security.addProvider(new BouncyCastleProvider());

    //     // load CA certificate

    //     PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));

    //     X509Certificate caCert = (X509Certificate)reader.readObject();

    //     reader.close();

    //     // load client certificate

    //     reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));

    //     X509Certificate cert = (X509Certificate)reader.readObject();

    //     reader.close();

    //     // load client private key

    //     reader = new PEMReader(

    //             new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),

    //             new PasswordFinder() {

    //                 public char[] getPassword() {

    //                     return password.toCharArray();

    //                 }

    //             }

    //     );

    //     KeyPair key = (KeyPair)reader.readObject();

    //     reader.close();

    //     KeyStore caKs = KeyStore.getInstance("JKS");

    //     caKs.load(null, null);

    //     caKs.setCertificateEntry("ca-certificate", caCert);

    //     TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");

    //     tmf.init(caKs);

    //     KeyStore ks = KeyStore.getInstance("JKS");

    //     ks.load(null, null);

    //     ks.setCertificateEntry("certificate", cert);

    //     ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});

    //     KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");

    //     kmf.init(ks, password.toCharArray());

    // SSLContext context = SSLContext.getInstance(tlsVersion);

    //     context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    //     return context.getSocketFactory();

    // }catch (Exception e) {

    // e.printStackTrace();

    // // TODO: handle exception

    // return null;

    // }

    //

    // }



    // public static SSLSocketFactory getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile,final String password, String tlsVersion) throws Exception{

    //

    // System.out.println("####################### getSocketFactory ########################");

    // System.out.println("caCrtFile : " + caCrtFile);

    // System.out.println("crtFile : " + crtFile);

    // System.out.println("keyFile : " + keyFile);

    // System.out.println("####################### getSocketFactory ########################");

    //

    // Security.addProvider(new BouncyCastleProvider());

    //

    // // load CA certificate

    // PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));

    // X509Certificate caCert = (X509Certificate)reader.readObject();

    // reader.close();

    //

    // // load client certificate

    // reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));

    // X509Certificate cert = (X509Certificate)reader.readObject();

    // reader.close();

    //

    // // load client private key

    // reader = new PEMReader(

    // new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),

    // new PasswordFinder() {

    // @Override

    // public char[] getPassword() {

    // return password.toCharArray();

    // }

    // }

    // );

    // KeyPair key = (KeyPair)reader.readObject();

    // reader.close();

    //

    // // CA certificate is used to authenticate server

    // KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());

    // caKs.load(null, null);

    // caKs.setCertificateEntry("ca-certificate", caCert);

    // TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

    // tmf.init(caKs);

    //

    // // client key and certificates are sent to server so it can authenticate us

    // KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

    // ks.load(null, null);

    // ks.setCertificateEntry("certificate", cert);

    // ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});

    // KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

    // kmf.init(ks, password.toCharArray());

    //

    // // finally, create SSL socket factory

    // SSLContext context = SSLContext.getInstance(tlsVersion);

    // context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    // return context.getSocketFactory();

    // }


    // public static SSLSocketFactory getSocketFactory (final URI caCrtFile, final URI crtFile, final URI keyFile,final String password, String tlsVersion) throws Exception{

    //

    // System.out.println("####################### getSocketFactory ########################");

    // System.out.println("caCrtFile : " + caCrtFile);

    // System.out.println("crtFile : " + crtFile);

    // System.out.println("keyFile : " + keyFile);

    // System.out.println("####################### getSocketFactory ########################");

    //

    // Security.addProvider(new BouncyCastleProvider());

    //

    // // load CA certificate

    // PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));

    // X509Certificate caCert = (X509Certificate)reader.readObject();

    // reader.close();

    //

    // // load client certificate

    // reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));

    // X509Certificate cert = (X509Certificate)reader.readObject();

    // reader.close();

    //

    // // load client private key

    // reader = new PEMReader(

    // new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),

    // new PasswordFinder() {

    // @Override

    // public char[] getPassword() {

    // return password.toCharArray();

    // }

    // }

    // );

    // KeyPair key = (KeyPair)reader.readObject();

    // reader.close();

    //

    // // CA certificate is used to authenticate server

    // KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());

    // caKs.load(null, null);

    // caKs.setCertificateEntry("ca-certificate", caCert);

    // TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

    // tmf.init(caKs);

    //

    // // client key and certificates are sent to server so it can authenticate us

    // KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

    // ks.load(null, null);

    // ks.setCertificateEntry("certificate", cert);

    // ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});

    // KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

    // kmf.init(ks, password.toCharArray());

    //

    // // finally, create SSL socket factory

    // SSLContext context = SSLContext.getInstance(tlsVersion);

    // context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    // return context.getSocketFactory();

    // }


    public static SSLSocketFactory getSocketFactory(final byte[] caCrt, final byte[] clientCrt, final byte[] clientKey, final String password, String tlsVersion) throws Exception {

        try {

            System.out.println("####################### getSocketFactory ########################");

            System.out.println("caCrtFile : " + caCrt);

            System.out.println("crtFile : " + clientCrt);

            System.out.println("keyFile : " + clientKey);

            System.out.println("####################### getSocketFactory ########################");


            Security.addProvider(new BouncyCastleProvider());


            // load CA certificate

            PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(caCrt)));

            X509Certificate caCert = (X509Certificate) reader.readObject();

            reader.close();


            // load client certificate

            reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(clientCrt)));

            X509Certificate cert = (X509Certificate) reader.readObject();

            reader.close();


            // load client private key

            reader = new PEMReader(

                new InputStreamReader(new ByteArrayInputStream(clientKey)),

                new PasswordFinder() {

                    @Override

                    public char[] getPassword() {

                        return password.toCharArray();

                    }

                }

            );

            KeyPair key = (KeyPair) reader.readObject();

            reader.close();


            // CA certificate is used to authenticate server

            KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());

            caKs.load(null, null);

            caKs.setCertificateEntry("ca-certificate", caCert);

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            tmf.init(caKs);


            // client key and certificates are sent to server so it can authenticate us

            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

            ks.load(null, null);

            ks.setCertificateEntry("certificate", cert);

            ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[] {

                cert

            });

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

            kmf.init(ks, password.toCharArray());


            // finally, create SSL socket factory

            SSLContext context = SSLContext.getInstance(tlsVersion);

            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            return context.getSocketFactory();

        } catch (Exception e) {

            // TODO: handle exception

            e.printStackTrace();

            return null;

        }

    }

}


주의사항!! (삽질 주의)

** 주석 처리된 부분이 있는데. getSocketFactory 메소드에서 fat.jar 로 변경후 실행시

src/main/resources 하위 설정 파일을 읽어오지 못하는 nio 관련 버그를 발견함. 

Paths.get << 부분에서 오류가 남 jdk 1.7에서 일어난 버그리포팅이 있었음  1.8에서도 안됨

버전을 변경 할수는 없고 ByteArrayInputStream 로 변경 하여 처리 했더니 잘됨.



** java -jar openssl  이라서 신뢰되지 않은 인증서이기 때문에 요청이 안나감..ㅠ

설치된 java 경로가서 아래와 같이 서버로부터 다운받은 ca.crt 파일을 등록해줘서

해결

keytool -import -keystore '자바설치된경로\jre1.8.0_201\lib\security\cacerts' -file C:\Users\user\Desktop\CA\ca.crt -alias 'mqtt-ssl-cert' -storepass "changeit"



5 java 데몬에서 사용하는 서비스 로직 부분

@Override

public void start() throws Exception {

    try {

        EventBus eb = vertx.eventBus();

        Properties prop = new Properties();

        ClassLoader cl;

        cl = Thread.currentThread().getContextClassLoader();

        URL url = cl.getResource("config/mqtt.properties");


        prop.load(url.openStream());

        String brokerUrl = prop.getProperty("alan.mqtt.broker.url");

        String clientName = prop.getProperty("alan.mqtt.client.name");

        int connectionTimeout = Integer.parseInt(prop.getProperty("alan.mqtt.connection.timeout"));

        int keepaliveTimeout = Integer.parseInt(prop.getProperty("alan.mqtt.keepalive.timeout"));

        byte[] caCrt = FileCopyUtils.copyToByteArray(new ClassPathResource(prop.getProperty("alan.mqtt.tls.cafile.path")).getInputStream());

        byte[] clientCrt = FileCopyUtils.copyToByteArray(new ClassPathResource(prop.getProperty("alan.mqtt.tls.client.crt.path")).getInputStream());

        byte[] clientKey = FileCopyUtils.copyToByteArray(new ClassPathResource(prop.getProperty("alan.mqtt.tls.client.key.path")).getInputStream());

        String clientPassword = prop.getProperty("alan.mqtt.tls.password");

        int qos = Integer.parseInt(prop.getProperty("alan.mqtt.qos"));

        String tlsVersion = prop.getProperty("alan.mqtt.tls.version");

        String topic = prop.getProperty("alan.mqtt.sub.topic");


        System.out.println("######### mqtt client config ########");

        System.out.println("######### brokerUrl : " + brokerUrl + " ########");

        System.out.println("######### clientName : " + clientName + " ########");

        System.out.println("######### connectionTimeout : " + connectionTimeout + " ########");

        System.out.println("######### keepaliveTimeout : " + keepaliveTimeout + " ########");

        System.out.println("######### caCrt : " + caCrt + " ########");

        System.out.println("######### clientCrt : " + clientCrt + " ########");

        System.out.println("######### clientKey : " + clientKey + " ########");

        System.out.println("######### clientPassword : " + clientPassword + " ########");

        System.out.println("######### qos : " + qos + " ########");

        System.out.println("######### tlsVersion : " + tlsVersion + " ########");

        System.out.println("######### topic : " + topic + " ########");


        MqttClient client = new MqttClient(brokerUrl, clientName);

        client.setCallback(new MqttCallback() {

            @Override

            public void messageArrived(String topic, MqttMessage message) throws Exception {

                // TODO Auto-generated method stub

                System.out.println("mqtt -> daemon : " + new Date().getTime() + " : " + message);

                try {

                    //toString

                    eb.publish("mqtt", message.toString());

                } catch (Exception e) {

                    // TODO: handle exception

                    e.printStackTrace();

                }

            }


            @Override

            public void deliveryComplete(IMqttDeliveryToken token) {

                // TODO Auto-generated method stub

                System.out.println("deliveryComplete");

                System.out.println(token.toString());

            }


            @Override

            public void connectionLost(Throwable cause) {

                // TODO Auto-generated method stub

                System.out.println("connectionLost");

                System.out.println(cause.toString());

            }

        });

        MqttConnectOptions options = new MqttConnectOptions();

        options.setConnectionTimeout(connectionTimeout);

        options.setKeepAliveInterval(keepaliveTimeout);

        options.setSocketFactory(SslUtil.getSocketFactory(caCrt, clientCrt, clientKey, clientPassword, tlsVersion));

        client.connect(options);

        client.subscribe(topic, qos);

    } catch (Exception e) {

        // TODO: handle exception

        e.printStackTrace();

    }


}


mqtt 클라이언트 생성하여 콜백 인터페이스 까지 구현한 코드

java 비동이 socket framework 인 vertx 내에 verticle 에서 사용한 예제


이상 java mqtt 클라이언트 구현 샘플. 부분 참조만 할것. 


    <!-- mqtt client-->

    <dependency>

        <groupId>org.eclipse.paho</groupId>

        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>

        <version>1.2.2</version>

    </dependency>

    <!--  ssl support  -->

    <dependency>

        <groupId>org.bouncycastle</groupId>

        <artifactId>bcprov-jdk16</artifactId>

        <version>1.45</version>

    </dependency>


위의 의존 라이브러리 필요.




댓글 없음:

댓글 쓰기

[lunux]리눅스 폴더별 용량 확인

리눅스 폴더별 용량 확인 조회 하고자 하는 디렉토리 리스트가있는 경로로 이동후 du -h --max-depth=1