快速入门:使用OpenSSL实现基于证书的双向认证¶
该文章帮助你快速入门,使用OpenSSL实现设备与EnOS Cloud之间的基于证书的双向安全连接。
开始前准备:创建模型、产品、设备 ¶
本步骤的前提是你已完成直连设备连接快速入门和子设备通过edge连接至EnOS Cloud快速入门这两个示例。
创建EnOS Edge产品
本步骤大部分与上述两个快速入门示例相似,差异点在创建EnOS Edge产品时需要创建启用 证书双向认证机制 的产品,默认证书有效期和最长证书有效期使用默认值即可。如下图所示:
逆变器产品不需要开启 证书双向认证机制,因为逆变器是作为子设备由EnOS Edge代理连接EnOS Cloud,只需要edge与cloud进行基于证书的双向认证即可。
创建EnOS Edge设备
基于以上产品创建网关类型设备Edge01_Certificate。如下图所示:
记下 Edge01_Certificate 的设备三元组,将用于创建证书请求文件。以下设备三元组供您参考,您需要使用使用自己的三元组。
Product Key:
Et***YP6
Device Key:
UB***rOhJD
Device Secret:
jgWGPE***B7bShf2P5cz
创建子设备
逆变器设备参照直连设备连接快速入门进行创建。如下图所示:
步骤1:创建证书签名请求文件 ¶
需要创建的私钥名称为:edge.key。需要创建的CSR文件的文件名为:edge.csr。创建的具体步骤,参考创建证书签名申请。
步骤2:调用REST API申请证书 ¶
在生成edge.csr
以后,调用EnOS Cloud的REST API申请证书。
在POM中添加如下的Maven依赖,以便使用EnOS API:
<dependency> <groupId>com.envisioniot</groupId> <artifactId>enos-dm-api-pojo</artifactId> <version>0.2.10</version> </dependency>
使用如下代码调用EnOS API申请证书
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 java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
public class applyCert {
// ISSUE_AUTHORITY表示证书类型,可选值有: RSA 和 ECC
public static final String ISSUE_AUTHORITY = "ECC";
// VALID_DAY表示证书有效期
public static final Integer VALID_DAY = 300;
/**
* 设备认证所需信息,这些参数依次分别表示EnOS Edge设备的device key, product key
*/
public static final String DEVICE_KEY = "xIhSzeP6lh";
public static final String PRODUCT_KEY = "rHbNES3c";
/**
* 在EnOS上注册的,用于调用API的应用的Access key和secret key,可以在 “EnOS控制台 > 应用注册” 获取
*/
public static final String ACCESS_KEY = "2e0b21b7-1b51-46a5-b501-c3824331b8df";
public static final String SECRET_KEY = "46e715d8-3c38-4cb6-9d65-b0bdad1ffe89";
// API网关URL,点击控制台右上角的 帮助 > 环境信息 查询
public static final String API_GATEWAY_URL = "https://apim-ppe1.envisioniot.com";
/**
* 组织ID,鼠标悬停在EnOS控制台左上角的OU名称处可以获得
*/
public static final String ORG_ID = "o15724268424841";
/**
* 以下参数用于保存生成的各种文件:
* SAVE_CSR_FILE_PATH 用于保存生成的CSR文件
* SAVE_DEVICE_CERT_FILE_PATH 用于保存申请到的设备证书
* SAVE_ROOT_CERT_FILE_PATH 用于保存根证书
*/
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";
private static void applyCertToDevice() throws IOException {
// 读取生成的csr文件
String certificateRequest = readFile(SAVE_CSR_FILE_PATH);
// 设置绑定证书设备请求的参数
ApplyCertificateRequest applyCertificateRequest = createApplyCertParam(certificateRequest);
// 利用EnOS API发起请求
ApplyCertificateResponse certRsp =
Poseidon.config(PConfig.init().appKey(ACCESS_KEY).appSecret(SECRET_KEY).debug())
.url(API_GATEWAY_URL)
.getResponse(applyCertificateRequest, ApplyCertificateResponse.class);
// 获得设备的证书和根证书
if (certRsp.success()) {
// 保存设备的证书
saveFile(certRsp.getData().getCert(), SAVE_DEVICE_CERT_FILE_PATH);
// 保存设备证书的根证书
saveFile(certRsp.getData().getCaCert(), SAVE_ROOT_CERT_FILE_PATH);
}
}
/**
* 读取CSR文件
*/
private static String readFile(String path) {
try (InputStream inputStream = new FileInputStream(path);
Reader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(inputStreamReader)) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private static ApplyCertificateRequest createApplyCertParam(String certificateRequest) {
ApplyCertificateRequest applyCertificateRequest = new ApplyCertificateRequest();
applyCertificateRequest.setCsr(certificateRequest);
/*
* 请确定ISSUE_AUTHORITY和你生成csr对应的颁发方是一致的
*/
applyCertificateRequest.setIssueAuthority(ISSUE_AUTHORITY);
/*
* 请确保证书的有效天数满足小于所属产品规定的最大有效期
*/
applyCertificateRequest.setValidDay(VALID_DAY);
/*
* 设备信息
*/
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
/*
* 使用以下任意一个参数或参数组合以指定设备:
* ASSET_ID
* PRODUCT_KEY + DEVICE_KEY
* 本示例代码使用了 PRODUCT_KEY + DEVICE_KEY
*/
deviceIdentifier.setKey(PRODUCT_KEY, DEVICE_KEY);
applyCertificateRequest.setDevice(deviceIdentifier);
applyCertificateRequest.setOrgId(ORG_ID);
return applyCertificateRequest;
}
public static void main(String[] args) throws NoSuchAlgorithmException, CertificateException, SignatureException, InvalidKeyException, IOException, KeyStoreException {
//生成证书
applyCertToDevice();
}
/*
* 保存申请到的证书
*/
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());
}
}
}
步骤3:生成设备连接平台的jks文件¶
按照以下步骤使用keytool生成edge.jks
文件。keytool是Java原生JDK的工具,其路径为 %JAVA_HOME%\bin\keytool
。
a.查看文件¶
[root@DemoMachine cert] ll
total 12
-rw-r--r-- 1 root root 1395 Nov 28 19:51 cacert.pem
-rw-r--r-- 1 root root 1858 Nov 28 19:51 edge.key
-rw-r--r-- 1 root root 1416 Nov 28 20:08 edge.pem
b.将证书与私钥导出为.p12文件¶
[root@DemoMachine cert] openssl pkcs12 -export -in edge.pem -inkey edge.key -out edge.p12 -name edge -CAfile cacert.pem -caname cacert
Enter pass phrase for edge.key:
Enter Export Password:
Verifying - Enter Export Password:
c.查看生成的.p12文件¶
[root@DemoMachine cert] ll
total 16
-rw-r--r-- 1 root root 1395 Nov 28 19:51 cacert.pem
-rw-r--r-- 1 root root 1858 Nov 28 19:51 edge.key
-rw-r--r-- 1 root root 2654 Nov 28 20:19 edge.p12
-rw-r--r-- 1 root root 1416 Nov 28 20:08 edge.pem
d.导入.p12文件至密钥库¶
[root@DemoMachine cert] keytool -importkeystore -deststorepass 123456 -destkeypass 123456 -destkeystore edge.jks -srckeystore edge.p12 -srcstoretype PKCS12 -srcstorepass 123456 -alias edge
Importing keystore edge.p12 to edge.jks...
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore edge.jks -destkeystore edge.jks -deststoretype pkcs12".
e.查看jks文件¶
[root@DemoMachine cert] ll
total 20
-rw-r--r-- 1 root root 1395 Nov 28 19:51 cacert.pem
-rw-r--r-- 1 root root 2356 Nov 28 20:20 edge.jks
-rw-r--r-- 1 root root 1858 Nov 28 19:51 edge.key
-rw-r--r-- 1 root root 2654 Nov 28 20:19 edge.p12
-rw-r--r-- 1 root root 1416 Nov 28 20:08 edge.pem
f.检查jks文件含有一个证书条目(trusted certificate entry)¶
[root@DemoMachine cert] keytool -list --keystore edge.jks
Enter keystore password:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry
edge, Nov 28, 2018, PrivateKeyEntry,
Certificate fingerprint (SHA1): 38:16:5A:1F:1D:68:44:44:FE:56:1A:84:36:31:85:CB:14:5B:9C:5E
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore edge.jks -destkeystore edge.jks -deststoretype pkcs12".
g.导入cacert根证书至密钥库¶
[root@DemoMachine cert]# keytool -import -trustcacerts -alias cacert -file cacert.pem -keystore edge.jks -storepass 123456
Owner: EMAILADDRESS=ca@eniot.io, CN=EnOS CA, OU=EnOS CA, O=EnOS, L=Shanghai, ST=Shanghai, C=CN
Issuer: EMAILADDRESS=ca@eniot.io, CN=EnOS CA, OU=EnOS CA, O=EnOS, L=Shanghai, ST=Shanghai, C=CN
Serial number: 8c54a99157c8ef28
Valid from: Mon Nov 19 18:20:27 CST 2018 until: Thu Nov 16 18:20:27 CST 2028
Certificate fingerprints:
MD5: 4E:BF:2A:53:85:1E:21:97:70:72:AD:DF:A5:79:51:3F
SHA1: 96:BC:6B:F0:15:CD:BB:03:52:12:A2:C6:C4:BD:20:69:71:4A:75:C2
SHA256: 81:B0:E3:01:D3:2B:48:E7:CF:CC:BC:07:9A:AD:49:74:EF:92:97:A1:D4:46:E2:4E:56:94:14:32:A7:09:FA:9F
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: AE 4F F7 AF A7 19 7B 0B AE 2E 79 0F B4 7B E5 AE .O........y.....
0010: 8C F4 54 0D ..T.
]
]
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen:2147483647
]
#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: AE 4F F7 AF A7 19 7B 0B AE 2E 79 0F B4 7B E5 AE .O........y.....
0010: 8C F4 54 0D ..T.
]
]
Trust this certificate? [no]: yes
Certificate was added to keystore
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore edge.jks -destkeystore edge.jks -deststoretype pkcs12".
h.检查jks文件含有两个证书条目(trusted certificate entry)¶
[root@DemoMachine cert]# keytool -list --keystore edge.jks
Enter keystore password:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 2 entries
cacert, Nov 28, 2018, trustedCertEntry,
Certificate fingerprint (SHA1): 96:BC:6B:F0:15:CD:BB:03:52:12:A2:C6:C4:BD:20:69:71:4A:75:C2
edge, Nov 28, 2018, PrivateKeyEntry,
Certificate fingerprint (SHA1): 38:16:5A:1F:1D:68:44:44:FE:56:1A:84:36:31:85:CB:14:5B:9C:5E
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore edge.jks -destkeystore edge.jks -deststoretype pkcs12".
[root@DemoMachine cert]#
步骤4:使用MQTT-SDK连接EnOS ¶
在Java项目的pom.xml文件中添加MQTT-SDK的依赖,版本最低必须为2.2.5,如下所示:
<dependency>
<groupId>com.envisioniot</groupId>
<artifactId>enos-mqtt</artifactId>
<version>2.2.5</version>
</dependency>
使用MQTT-SDK连接EnOS的示例代码段如下所示,将这段代码替换快速入门:将非智能设备通过edge连接至EnOS Cloud 步骤5中的“网关上线”示例代码:
public static boolean IS_ECC_CONNECT = true; //使用RSA证书时,该参数值为false;使用ECC证书时,值为true
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"; // MQTT broker URL,点击控制台右上角的 帮助 > 环境信息 查询
private static void connectEnos() {
DefaultProfile defaultProfile = new DefaultProfile(SSL_CONNECT_URL, PRODUCT_KEY, DEVICE_KEY,DEVICE_SECRET);
// 设置连接属性
defaultProfile.setConnectionTimeout(60).setKeepAlive(180).setAutoReconnect(false)
.setSSLSecured(true)
// 设置双向认证,设备的jks文件路径和读取密码
.setSSLJksPath(JKS_FILE_NAME, JKS_PASSWORD)
// 设置是否是ECC证书连接平台
.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("connect failed");
}
});
}
步骤5:启动示例程序¶
完成步骤5的替换操作后,运行快速入门:将非智能设备通过edge连接至EnOS Cloud中更新后的示例代码。
步骤6:检查设备连接状态 ¶
在运行示例程序以后,EnOS Edge上线,并添加子设备作为拓扑,代理子设备连接云端。设备连接状态如下图所示:
步骤7:查看设备数据 ¶
进入控制台,选择 设备管理 > 设备资产,点击 查看 INV001 的 设备详情,打开 测点 标签页,选择 INV.GenActivePW 测点,点击 查看数据,可以查看历史数据记录。