Securing Connection Through Certificate-Based Bi-directional Authentication using JDK¶
This section helps you to quickly get started with using certificate-based authentication to secure the connection between the device and the cloud with JDK.
Prerequisites¶
The premise of this step is that you have already completed the steps of creating models, products, and devices by referring to Connecting Smart Devices to EnOS Cloud and Connecting Non-smart Devices to EnOS Cloud via Edge. When creating, pay attention to the following special configurations of this tutorial:
Creating an EnOS Edge Product
You need to enable Certificate-Based Authentication when you create the edge product. The default certificate validity period and maximum certificate validity period can use the default values. As shown in the figure below:

The inverter product does not need to have Certificate-Based Authentication enabled because the inverter connects to EnOS Cloud through EnOS Edge. You only need to enable the authentication for the connection between the Edge and the Cloud.
Creating an Edge Device
Create an edge device instance named Edge01_Certificate
based on the product that you just created.

In the device list, click View , and note down the device triplet (Product Key, Device Key, and Device Secret) of Edge01_Certificate, which will be used to create the certificate request file.
Creating a Sub-Device
Refer to Step 3 in Connecting Smart Devices to EnOS Cloud to create an inverter device, and configure the following values for the device:
Basic Information:
Product: Inverter_Product
Device Name: INV002
Time Zone/City: UTC+14:00
Attributes:
Inverter Type: 1:String
Capacity: 20
Step 1: Creating JKS Files Using JDK¶
The code snippet in this step serves the following purposes:
Generating a certificate signing request (Certificate Signing Request, CSR) file.
Calling EnOS API to apply for a certificate.
Generating the JKS file needed to connect EnOS Edge to EnOS Cloud.
import com.envision.apim.poseidon.config.PConfig;
import com.envision.apim.poseidon.core.Poseidon;
import com.envisioniot.enos.connect_service.v2_1.cert.ApplyCertificateRequest;
import com.envisioniot.enos.connect_service.v2_1.cert.ApplyCertificateResponse;
import com.envisioniot.enos.connect_service.vo.DeviceIdentifier;
import sun.security.pkcs10.PKCS10;
import sun.security.x509.X500Name;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class sampleCode {
/**
* Configuration for generating a certificate key
*/
// ALGORITHM indicates the algorithm used by the certificate. Possible values are: RSA or EC, which stands for RSA and ECC algorithm respectively.
public static final String ALGORITHM = "EC";
// ISSUE_AUTHORITY indicates the certificate type. Possible values are RSA and ECC.
public static final String ISSUE_AUTHORITY = "ECC";
// VALID_DAY indicates the validity period of the certificate.
public static final Integer VALID_DAY = 300;
//IS_ECC_CONNECT is boolean, indicating whether to use ECC certificate for connection. If you are using RSA, its value should be false.
public static boolean IS_ECC_CONNECT = true;
/**
* Information needed for device authentication: the device key, product key, device secret, and asset ID of the device.
* Use either DEVICE_KEY+PRODUCT_KEY or ASSET_ID to identify the device.
* Delete the unused parameters.
*/
public static final String DEVICE_KEY = "yourDeviceKey";
public static final String PRODUCT_KEY = "yourProductKey";
public static final String DEVICE_SECRET = "yourDeviceSecret";
public static final String ASSET_ID = "yourAssetID";
/**
* For RSA, SIZE should be 2048, indicating the length of the key is 2048 bit.
* For ECC, SIZE should be 256, indicating that the prime256v1 algorithm is used.
*/
public static final Integer SIZE = 256;
/**
* SIGNER_ALGORITHM indicates the signature algorithm.
* If RSA certificate is used, its value should be SHA256withRSA.
* If ECC certificate is used, its value should be SHA256WITHECDSA.
*/
public static final String SIGNER_ALGORITHM = "SHA256WITHECDSA";
/**
* CSR applicant information:
* COMMON_NAME: no more than 64 characters.
* ORGANIZATION_UNIT: no more than 64 characters
* ORGANIZATION: no more than 64 characters
* LOCALE: no more than 64 characters
* STATE_OR_PROVINCE_NAME: no more than 64 characters
* COUNTRY: 2-character country code. See countrycode.org
*/
public static final String COMMON_NAME = "doc ecc cert test";
public static final String ORGANIZATION_UNIT = "EnOS Cloud";
public static final String ORGANIZATION = "EnOS";
public static final String LOCALE = "Shanghai";
public static final String STATE_OR_PROVINCE_NAME = "Shanghai";
public static final String COUNTRY = "CN";
/**
* The access key and secret key of an application registered on EnOS, used for calling EnOS API. You can find the keys in Application Registration on EnOS Application Portal > Developer Console > Application Registration.
*/
public static final String ACCESS_KEY = "yourAccessKey";
public static final String SECRET_KEY = "yourSecretKey";
// The URL of the API gateway. You can find it in Help > Environment Information in EnOS Application Portal > Developer Console.
public static final String API_GATEWAY_URL = "http://apim-apigw-proxy";
/**
* Organization ID. Mouse over the OU name to obtain it in EnOS Application Portal > Developer Console.
*/
public static final String ORG_ID = "yourOrgID";
//Used to save the generated public-private key pair.
private static PrivateKey PRIVATE_KEY;
/**
* The following parameters are used to save the generated files:
* SAVE_CSR_FILE_PATH: The CSR file
* SAVE_DEVICE_CERT_FILE_PATH: The certificate requested
* SAVE_ROOT_CERT_FILE_PATH: The root certificate
* JKS_PASSWORD: The JKS password.
*/
public static final String SAVE_CSR_FILE_PATH = "edge.csr";
public static final String SAVE_DEVICE_CERT_FILE_PATH = "edge.pem";
public static final String SAVE_ROOT_CERT_FILE_PATH = "cacert.pem";
public static final String JKS_PASSWORD = "123456";
public static final String PRIVATE_ENTRY_NAME = "edge";
public static final String CA_CERT_ENTRY_NAME = "cacert";
public static final String JKS_FILE_NAME = "edge.jks";
//Generates the public-private key pair of the CSR file
public static KeyPair generateKeyPair(String algorithm, int size) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(size, new SecureRandom());
return keyPairGen.generateKeyPair();
}
//Generates the CSR file
public static void createCsrFile() throws CertificateException, SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException {
try (ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs)) {
KeyPair keyPair = generateKeyPair(ALGORITHM, SIZE);
PKCS10 pkcs10 = new PKCS10(keyPair.getPublic());
Signature signature = Signature.getInstance(SIGNER_ALGORITHM);
signature.initSign(keyPair.getPrivate());
// Save the private key for generating JKS file
PRIVATE_KEY = keyPair.getPrivate();
X500Name x500Name = new X500Name(COMMON_NAME, ORGANIZATION_UNIT, ORGANIZATION, LOCALE, STATE_OR_PROVINCE_NAME, COUNTRY);
pkcs10.encodeAndSign(x500Name, signature);
pkcs10.print(ps);
byte[] c = bs.toByteArray();
String csrFileString = new String(c);
// Save the CSR file to the specified path
saveFile(csrFileString, SAVE_CSR_FILE_PATH);
}
}
/**
* Save a file
*/
public static void saveFile(String fileContent, String filePath) throws IOException {
File fp = new File(filePath);
try (OutputStream os = new FileOutputStream(fp)) {
os.write(fileContent.getBytes());
}
}
public static void main(String[] args) throws NoSuchAlgorithmException, CertificateException, SignatureException, InvalidKeyException, IOException, KeyStoreException {
// step1: Make sure the value of ISSUE_AUTHORITY is consistent with your certificate type.
createCsrFile();
// step2: Call EnOS API to obtain a certificate
applyCertToDevice();
// step3: Use the certificate and the root certificate to generate JKS
createDeviceJks();
}
/**
*Generate the JKS file
*
*/
public static void createDeviceJks() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
// step1: Read the device certificate
String deviceCertificate = readFile(SAVE_DEVICE_CERT_FILE_PATH);
// ==> Convert it into X.509 certificate
X509Certificate x509Certificate = parseCertificate(deviceCertificate);
// step2: Read the root certificate
String rootCaCertificate = readFile(SAVE_ROOT_CERT_FILE_PATH);
X509Certificate rootCertificate = parseCertificate(rootCaCertificate);
// step3: Create a key library
KeyStore keyStore = KeyStore.getInstance("jks");
// Initiate
keyStore.load(null, null);
// step4: Save the certificate and private key to the library
addPrivateEntry(keyStore, JKS_PASSWORD, PRIVATE_KEY, PRIVATE_ENTRY_NAME, x509Certificate, rootCertificate);
// step5: Add the root certificate to the JKS trusted list
addTrustEntry(keyStore, CA_CERT_ENTRY_NAME, rootCertificate);
// step6: Save the JKS file
saveKeystore(keyStore, JKS_FILE_NAME, JKS_PASSWORD);
}
//Save the certificate and private key to the library
public static void addPrivateEntry(KeyStore caKs, String password, PrivateKey privateKey, String privateEntryName, X509Certificate... chainCerts) {
try {
if (caKs != null && chainCerts != null && privateKey != null &&
privateEntryName != null && !privateEntryName.isEmpty()) {
assert chainCerts.length > 0 : "chainCert don't input!";
KeyStore.PrivateKeyEntry skEntry = new KeyStore.PrivateKeyEntry(privateKey, chainCerts);
caKs.setEntry(privateEntryName, skEntry, new KeyStore.PasswordProtection(password.toCharArray()));
}
} catch (KeyStoreException e) {
System.err.println("when add private entry is occur error!");
}
}
//Add the root certificate to the JKS trusted list
public static void addTrustEntry(KeyStore caKs, String entryName, X509Certificate rootCert) {
try {
if (rootCert != null && caKs != null && entryName != null && !entryName.isEmpty()) {
caKs.setEntry(entryName, new KeyStore.TrustedCertificateEntry(rootCert), null);
}
} catch (KeyStoreException e) {
System.err.println("when add trust entry is occur error!");
}
}
//Save the JKS file
public static void saveKeystore(KeyStore keyStore, String jksFileName, String password)
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException {
// Save as .jks file
try (FileOutputStream fos = new FileOutputStream(jksFileName)) {
keyStore.store(fos, password.toCharArray());
}
}
//Convert into X.509 certificate
public static X509Certificate parseCertificate(String certificateContentString) throws IOException {
if (certificateContentString != null && !certificateContentString.trim().isEmpty()) {
try (InputStream inputStream = new ByteArrayInputStream(certificateContentString.getBytes())) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(inputStream);
} catch (CertificateException e) {
e.printStackTrace();
}
}
return null;
}
//Bind the certificate to the device
public static void applyCertToDevice() throws IOException {
// Read the generated CSR file
String certificateRequest = readFile(SAVE_CSR_FILE_PATH);
// Configure the parameters of the device to which the certificate will be bound
ApplyCertificateRequest applyCertificateRequest = createApplyCertParam(certificateRequest);
// Make a request using EnOS API
ApplyCertificateResponse certRsp =
Poseidon.config(PConfig.init().appKey(ACCESS_KEY).appSecret(SECRET_KEY).debug())
.url(API_GATEWAY_URL)
.getResponse(applyCertificateRequest, ApplyCertificateResponse.class);
//Obtain the certificate and the root certificate of the device
if (certRsp.success()) {
// Save the device certificate
saveFile(certRsp.getData().getCert(), SAVE_DEVICE_CERT_FILE_PATH);
// Save the root certificate
saveFile(certRsp.getData().getCaCert(), SAVE_ROOT_CERT_FILE_PATH);
}
}
//Configure the parameter of the request to bind the certificate to the device
public static ApplyCertificateRequest createApplyCertParam(String certificateRequest) {
ApplyCertificateRequest applyCertificateRequest = new ApplyCertificateRequest();
applyCertificateRequest.setCsr(certificateRequest);
/*
* Make sure the value of ISSUE_AUTHORITY is consistent with the CSR certificate type.
*/
applyCertificateRequest.setIssueAuthority(ISSUE_AUTHORITY);
/*
* Make sure the validity period of the certificate is less than the maximum validity period of the device.
*/
applyCertificateRequest.setValidDay(VALID_DAY);
/*
* Device identity
*/
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
/*
* Use one of the following methods to identify a device:
* ASSET_ID
* PRODUCT_KEY + DEVICE_KEY
*/
applyCertificateRequest.setAssetId(ASSET_ID);
applyCertificateRequest.setProductKey(PRODUCT_KEY);
applyCertificateRequest.setDeviceKey(DEVICE_KEY);
/*
* Organization ID
*/
applyCertificateRequest.setOrgId(ORG_ID);
return applyCertificateRequest;
}
/**
* Read CSR file
*/
public static String readFile(String path) {
try {
return Files.lines(Paths.get(path), StandardCharsets.UTF_8)
.collect(Collectors.joining("\n"));
} catch (IOException e) {
e.printStackTrace();
}
return EMPTY_STRING;
}
}
Step 2: Using SDK to Connect Edge Device to EnOS¶
You can use either MQTT SDK or HTTP SDK to connect.
MQTT SDK¶
Add the following Maven dependency to your Java project. The version of the MQTT SDK must be 2.2.5 or later.
<dependency>
<groupId>com.envisioniot</groupId>
<artifactId>enos-mqtt</artifactId>
<version>2.2.16</version>
</dependency>
The code sample to connect EnOS Edge to EnOS is as follows. Replace the code the “EnOS Edge Online” sample code in Step 5 of Connecting Non-Smart Devices to EnOS Cloud via Edge.
public static boolean IS_ECC_CONNECT = true; //The value of this parameter is false for RSA certificate and true for ECC certificate.
public static final String DEVICE_KEY = "yourDeviceKey";
public static final String PRODUCT_KEY = "yourProductKey";
public static final String DEVICE_SECRET = "yourDeviceSecret";
public static final String JKS_PASSWORD = "yourJksPassword";
public static final String JKS_FILE_NAME = "edge.jks";
public static final String SSL_CONNECT_URL = "ssl://MqttBrokerUrl:18883";
private static void connectEnos() {
DefaultProfile defaultProfile = new DefaultProfile(SSL_CONNECT_URL, PRODUCT_KEY, DEVICE_KEY,DEVICE_SECRET);
// Configure the connection
defaultProfile.setConnectionTimeout(60).setKeepAlive(180).setAutoReconnect(false)
.setSSLSecured(true)
// Configure bi-directional authentication, the JKS file and password
.setSSLJksPath(JKS_FILE_NAME, JKS_PASSWORD)
// Configure whether to use ECC certificate to connect to EnOS
.setEccConnect(IS_ECC_CONNECT);
final MqttClient mqttClient = new MqttClient(defaultProfile);
mqttClient.connect(new ConnCallback() {
@Override
public void connectComplete(boolean reconnect) {
System.out.println("connect success");
}
@Override
public void connectLost(Throwable cause) {
System.out.println("connect lost");
}
@Override
public void connectFailed(Throwable cause) {
System.out.println("onConnectFailed : " + cause);
}
});
}
HTTP SDK¶
Add the following Maven dependency to your Java project. The version of the HTTP SDK must be 0.1.9 or later.
<dependency>
<groupId>com.envisioniot</groupId>
<artifactId>enos-http</artifactId>
<version>0.2.1</version>
</dependency>
The code sample to connect EnOS Edge to EnOS is as follows. Replace the code snippet in Step 5 of Connecting Non-smart Devices to EnOS Cloud via Edge.
public class HttpBiDirectionalAuthenticate {
// EnOS HTTP Broker URL, which can be obtained from Environment Information page in EnOS Application Portal > Developer Console
// ssl port 8443
static final String BROKER_URL = "https://broker_url:8443/";
// Device credentials, which can be obtained from Device Details page in EnOS Application Portal > Developer Console
static final String PRODUCT_KEY = "productKey";
static final String DEVICE_KEY = "deviceKey";
static final String DEVICE_SECRET = "deviceSecret";
private static String jksPath = "jskPath";
private static String jksPassword = "jskPassword";
/** Ecc cert flag
* if use ECC certificate, chose true
* if use RSA certificate, chose false */
static final boolean IS_ECC_CONNECT = false;
public static void main(String[] args) throws EnvisionException {
// construct a static device credential via ProductKey, DeviceKey and DeviceSecret
StaticDeviceCredential credential = new StaticDeviceCredential(
PRODUCT_KEY, DEVICE_KEY, DEVICE_SECRET);
// construct a http connection
SessionConfiguration configuration = SessionConfiguration
.builder()
.lifetime(30_000)
.sslSecured(true)
.isEccConnect(IS_ECC_CONNECT)
.jksPath(jksPath)
.jksPassword(jksPassword)
.build();
HttpConnection connection = new HttpConnection.Builder(BROKER_URL, credential)
.sessionConfiguration(configuration)
.build();
MeasurepointPostRequest request = buildMeasurepointPostRequest();
try
{
MeasurepointPostResponse response = connection.publish(request, null);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(response));
} catch (EnvisionException | IOException e)
{
e.printStackTrace();
}
}
private static MeasurepointPostRequest buildMeasurepointPostRequest()
{
// Measurepoints are defined in ThingModel
return MeasurepointPostRequest.builder()
.addMeasurePoint("Int_value", 100)
.addMeasurePoint("DI_value_01", 5)
.build();
}
}
Step 3: Starting Sample Program¶
After completing code replacements in Step 2, run the updated sample code.
Step 4: Checking Device Connection Status¶
After running the sample program, EnOS Edge will come online, add sub-devices as topology, and proxy their connections to the cloud. The device connection status will appear as shown below:

Step 5: Checking Device Data¶
In the device list, locate the INV001 device and click its View
icon to enter the Device Details page.
Click the Measurement Points tab, find the measurement point INV.GenActivePW, then click View Data
to access the Data Insights page and view the latest data.
You may also retrieve the latest data through TSDB Data Service.