2021-08-05

This commit is contained in:
2021-08-05 11:46:27 +09:00
parent 2a56dff104
commit 438bf4bd5c
58 changed files with 2418 additions and 33 deletions

33
mosquitto/README.md Normal file
View File

@@ -0,0 +1,33 @@
# Mosquitto Example
```kotlin
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
```
```java
MqttClient client = new MqttClient(BROKER_URI, clientId, persistence);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setUserName("elex");
connOpts.setPassword("test".toCharArray());
connOpts.setAutomaticReconnect(true);
connOpts.setCleanSession(true);
client.connect(connOpts);
```
```java
MqttMessage msg = new MqttMessage(message.getBytes(StandardCharsets.UTF_8));
msg.setQos(qos);
client.publish(topic, msg);
```
```java
client.subscribe(topic, qos, listener);
```
------
Copyright (c) 2021. Elex.
All Rights Reserved.
https://www.elex-project.com/

View File

@@ -1,7 +1,20 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-java")
}
dependencies {
//implementation("org.eclipse.paho:org.eclipse.paho.mqttv5.client:1.2.5")
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
implementation("org.bouncycastle:bcprov-jdk15on:1.69")
// https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on
implementation("org.bouncycastle:bcpkix-jdk15on:1.69")
}

View File

@@ -0,0 +1,180 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
/**
* Mosquitto MQTT Example
*
* @author Elex
*/
@Slf4j
public final class HelloMosquitto {
static final String BROKER_URI = "tcp://127.0.0.1:1883";
static final String BROKER_URI_TLS = "ssl://127.0.0.1:8883";
static final String USER_NAME = "elex";
static final String PASSWORD = "test";
private MqttClient client;
private MemoryPersistence persistence = new MemoryPersistence();
/**
* MQTT 연결
*
* @param clientId 연결하는 클라이언트 마다 다르게 지정.
* @throws MqttException
*/
public HelloMosquitto(String clientId) throws MqttException {
client = new MqttClient(BROKER_URI, clientId, persistence);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setUserName(USER_NAME);
connOpts.setPassword(PASSWORD.toCharArray());
connOpts.setAutomaticReconnect(true);
connOpts.setCleanSession(true);
client.connect(connOpts);
}
/**
* MQTT over TLS 연결
*
* @param clientId 연결하는 클라이언트 마다 다르게 지정.
* @param socketFactory tls socket factory
* @throws MqttException
*/
public HelloMosquitto(String clientId, SSLSocketFactory socketFactory) throws MqttException {
client = new MqttClient(BROKER_URI_TLS, clientId, persistence); // 주소는 ssl로 시작하고, 포트는 8883을 사용합니다.
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setUserName(USER_NAME);
connOpts.setPassword(PASSWORD.toCharArray());
connOpts.setAutomaticReconnect(true);
connOpts.setCleanSession(true);
connOpts.setSocketFactory(socketFactory);
connOpts.setHttpsHostnameVerificationEnabled(false);
client.connect(connOpts);
}
/**
* 발행
*
* @param topic 토픽. Word separator: '/'
* @param message 메시지
* @param qos 최대 전송 메시지 개수
* @throws MqttException
*/
public void publish(String topic, String message, int qos) throws MqttException {
if (client.isConnected()) {
MqttMessage msg = new MqttMessage(message.getBytes(StandardCharsets.UTF_8));
msg.setQos(qos);
client.publish(topic, msg);
log.info("Tx: {} = {}", topic, new String(msg.getPayload(), StandardCharsets.UTF_8));
}
}
/**
* 구독 시작
*
* @param topic 토픽 패턴. Wildcard char : '?' for 1 word, '#' for words
* @param qos 최대 전송 메시지 개수
* @param listener 리스너
* @throws MqttException
*/
public void subscribe(String topic, int qos, IMqttMessageListener listener) throws MqttException {
if (client.isConnected()) {
client.subscribe(topic, qos, listener);
}
}
/**
* 구독 중단
*
* @param topic 토픽 패턴
* @throws MqttException
*/
public void unsubscribe(String topic) throws MqttException {
if (client.isConnected()) {
client.unsubscribe(topic);
}
}
public void close() throws MqttException {
client.disconnect();
}
public static void main(String... args)
throws MqttException, CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException,
KeyStoreException, KeyManagementException, IOException {
// TCP connection
HelloMosquitto client1 = new HelloMosquitto("Client_1");
client1.subscribe("hello/#", 1, new IMqttMessageListener() {
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info("Rx: {} = {}", topic, new String(message.getPayload(), StandardCharsets.UTF_8));
}
});
client1.publish(String.join("/", "hello", "tcp"), "Hi!", 1);
// TLS connection
HelloMosquitto client2 = new HelloMosquitto("Client_2", TlsHelper.socketFactory(
HelloMosquitto.class.getResourceAsStream("/clientstore.p12"),
"test".toCharArray(),
"test1".toCharArray(),
HelloMosquitto.class.getResourceAsStream("/truststore.p12"),
"test".toCharArray()
));
client2.publish(String.join("/", "hello", "tls"), "Hahaha, ...", 1);
// TLS with BC connection
HelloMosquitto client3 = new HelloMosquitto("Client_3", TlsHelperWithBouncyCastle.socketFactory(
HelloMosquitto.class.getResourceAsStream("/client.pem"),
HelloMosquitto.class.getResourceAsStream("/client.key.pem"),
"test1".toCharArray(),
HelloMosquitto.class.getResourceAsStream("/ca.pem")
));
client3.publish(String.join("/", "hello", "tls", "bc"), "Oke, ...", 1);
// TLS with BC connection, without client cert.
HelloMosquitto client4 = new HelloMosquitto("Client_4", TlsHelperWithBouncyCastle.socketFactory(
HelloMosquitto.class.getResourceAsStream("/ca.pem")
));
client4.publish(String.join("/", "hello", "tls", "bc-ca-only"), "Oke, ...", 1);
// // TLS connection, without client key store.
HelloMosquitto client5 = new HelloMosquitto("Client_4", TlsHelper.socketFactory(
HelloMosquitto.class.getResourceAsStream("/truststore.p12"),
"test".toCharArray())
);
client5.publish(String.join("/", "hello", "tls", "ca-only"), "Mmmm, ...", 1);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
log.error("Interrupted ...", e);
}
client1.close();
client2.close();
client3.close();
client4.close();
client5.close();
}
}

View File

@@ -0,0 +1,114 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;
/**
* @author Elex
*/
public final class TlsHelper {
private TlsHelper() {
}
/**
* SSL Context
*
* @param keyStoreInputStream 키스토어
* @param keyStorePassword 키스토어 비번
* @param keyPassword 키 비번
* @param trustStoreInputStream CA 인증서 키 스토어
* @param trustStorePassword 트러스트 스토어 비번
* @return
* @throws KeyStoreException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws UnrecoverableKeyException
* @throws KeyManagementException
*/
public static SSLContext context(InputStream keyStoreInputStream, char[] keyStorePassword, char[] keyPassword,
InputStream trustStoreInputStream, char[] trustStorePassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
KeyManagementException {
// OpenSSL로 키와 인증서를 만들고, PKCS12 키 저장소에 넣었습니다.
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 클라이언트 키와 인증서가 들어있습니다.
keyStore.load(keyStoreInputStream,
keyStorePassword); // 키스토어 비번
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword); // 키 비번
KeyStore trustKeyStore = KeyStore.getInstance("PKCS12");
// CA 인증서가 들어있습니다.
trustKeyStore.load(trustStoreInputStream,
trustStorePassword); // 키 스토어 비번
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKeyStore);
SSLContext context = SSLContext.getInstance("TLSv1.3");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context;
}
public static SSLSocketFactory socketFactory(InputStream keyStoreInputStream, char[] keyStorePassword, char[] keyPassword,
InputStream trustStoreInputStream, char[] trustStorePassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
KeyManagementException {
return context(keyStoreInputStream, keyStorePassword, keyPassword, trustStoreInputStream, trustStorePassword)
.getSocketFactory();
}
/**
* @param trustStoreInputStream CA 인증서 키 스토어
* @param trustStorePassword 트러스트 스토어 비번
* @return
* @throws KeyStoreException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws UnrecoverableKeyException
* @throws KeyManagementException
*/
public static SSLContext context(InputStream trustStoreInputStream, char[] trustStorePassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
KeyManagementException {
// OpenSSL로 키와 인증서를 만들고, PKCS12 키 저장소에 넣었습니다.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// 클라이언트 키와 인증서가 들어있습니다.
keyStore.load(null, null); // 키스토어 비번
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, null); // 키 비번
KeyStore trustKeyStore = KeyStore.getInstance("PKCS12");
// CA 인증서가 들어있습니다.
trustKeyStore.load(trustStoreInputStream,
trustStorePassword); // 키 스토어 비번
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKeyStore);
SSLContext context = SSLContext.getInstance("TLSv1.3");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context;
}
public static SSLSocketFactory socketFactory(InputStream trustStoreInputStream, char[] trustStorePassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
KeyManagementException {
return context(trustStoreInputStream, trustStorePassword).getSocketFactory();
}
}

View File

@@ -0,0 +1,155 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
/**
* @author Elex
*/
public final class TlsHelperWithBouncyCastle {
private TlsHelperWithBouncyCastle() {
}
/**
* Bouncy Castle을 이용.
*
* @param clientCrtInputStream cert pem
* @param clientKeyInputStream key pem
* @param clientPassword key password
* @param caCrtInputStream ca cert pem
* @return
*/
public static SSLSocketFactory socketFactory(InputStream clientCrtInputStream, InputStream clientKeyInputStream, char[] clientPassword,
InputStream caCrtInputStream)
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
return context(clientCrtInputStream, clientKeyInputStream, clientPassword, caCrtInputStream).getSocketFactory();
}
public static SSLContext context(InputStream clientCrtInputStream, InputStream clientKeyInputStream, char[] clientPassword,
InputStream caCrtInputStream)
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
Security.addProvider(new BouncyCastleProvider());
// CA 인증서 불러오기
CertificateFactory caCertFactory = CertificateFactory.getInstance("X.509");
PEMParser parser = new PEMParser(new InputStreamReader(caCrtInputStream));
X509Certificate caCert = (X509Certificate) caCertFactory
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
parser.close();
// 클라이언트 인증서 불러오기
CertificateFactory clientCertFactory = CertificateFactory.getInstance("X.509");
parser = new PEMParser(new InputStreamReader(clientCrtInputStream));
X509Certificate clientCert = (X509Certificate) clientCertFactory
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
parser.close();
// 클라이언트 비밀키 불러오기
parser = new PEMParser(new InputStreamReader(clientKeyInputStream));
Object object = parser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair;
if (object instanceof PEMEncryptedKeyPair) {
// 암호화된 키라면 패스워드가 필요하다.
PEMEncryptedKeyPair pemKeyPair = (PEMEncryptedKeyPair) object;
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(clientPassword);
keyPair = converter.getKeyPair(pemKeyPair.decryptKeyPair(decProv));
} else {
// 암호화되지 않은 키라면 비밀번호가 필요없다.
PEMKeyPair pemKeyPair = (PEMKeyPair) object;
keyPair = converter.getKeyPair(pemKeyPair);
}
parser.close();
PrivateKey clientKey = keyPair.getPrivate();
// CA 인증서를 사용해서 트러스트 스토어를 만든다.
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
caKeyStore.load(null, null);
caKeyStore.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(caKeyStore);
// 클라이언트 인증서와 비밀키를 사용해서 키 스토어를 만든다.
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
clientKeyStore.load(null, null);
clientKeyStore.setCertificateEntry("certificate", clientCert);
clientKeyStore.setKeyEntry("private-key", clientKey, clientPassword, new java.security.cert.Certificate[]{clientCert});
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, clientPassword);
SSLContext context = SSLContext.getInstance("TLSv1.3");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
return context;
}
/**
* BouncyCastle을 이용.
*
* @param caCrtInputStream ca cert pem
* @return
* @throws KeyStoreException
* @throws IOException
* @throws CertificateException
* @throws UnrecoverableKeyException
* @throws KeyManagementException
* @throws NoSuchAlgorithmException
*/
public static SSLContext context(InputStream caCrtInputStream)
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
Security.addProvider(new BouncyCastleProvider());
// CA 인증서 불러오기
CertificateFactory caCertFactory = CertificateFactory.getInstance("X.509");
PEMParser parser = new PEMParser(new InputStreamReader(caCrtInputStream));
X509Certificate caCert = (X509Certificate) caCertFactory
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
parser.close();
// CA 인증서를 사용해서 트러스트 스토어를 만든다.
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
caKeyStore.load(null, null);
caKeyStore.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(caKeyStore);
// 클라이언트 인증서와 비밀키는 없으므로, 그냥 만든다.
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
clientKeyStore.load(null, null);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, null);
SSLContext context = SSLContext.getInstance("TLSv1.3");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
return context;
}
public static SSLSocketFactory socketFactory(InputStream caCrtInputStream)
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
return context(caCrtInputStream).getSocketFactory();
}
}

View File

@@ -0,0 +1,8 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;