]> git.basschouten.com Git - openhab-addons.git/blob
d27cbb6aedec9b6082cc7b12bf8284e71c936d45
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mihome.internal;
14
15 import java.nio.charset.StandardCharsets;
16 import java.security.InvalidAlgorithmParameterException;
17 import java.security.InvalidKeyException;
18 import java.security.NoSuchAlgorithmException;
19
20 import javax.crypto.BadPaddingException;
21 import javax.crypto.Cipher;
22 import javax.crypto.IllegalBlockSizeException;
23 import javax.crypto.NoSuchPaddingException;
24 import javax.crypto.spec.IvParameterSpec;
25 import javax.crypto.spec.SecretKeySpec;
26
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Encrypts communication between openhab & xiaomi bridge (required by xiaomi).
32  *
33  * @author Ondřej Pečta - Initial contribution to Xiaomi MiHome Binding for OH 1.x
34  * @author Dieter Schmidt - Refactor logger
35  */
36 public class EncryptionHelper {
37
38     protected static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
39
40     // AES‐CBC 128 initial vector, taken from protocol description
41     protected static final byte[] IV = parseHexBinary("17996D093D28DDB3BA695A2E6F58562E");
42
43     private final Logger logger = LoggerFactory.getLogger(EncryptionHelper.class);
44
45     public String encrypt(String text, String key) {
46         return encrypt(text, key, IV);
47     }
48
49     public String encrypt(String text, String key, byte[] iv) {
50         IvParameterSpec vector = new IvParameterSpec(iv);
51         Cipher cipher;
52         try {
53             cipher = Cipher.getInstance("AES/CBC/NoPadding");
54         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
55             logger.warn("Failed to construct Cipher");
56             return "";
57         }
58         SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
59         try {
60             cipher.init(Cipher.ENCRYPT_MODE, keySpec, vector);
61         } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
62             logger.warn("Failed to init Cipher");
63             return "";
64         }
65         byte[] encrypted;
66         try {
67             encrypted = cipher.doFinal(text.getBytes());
68             return bytesToHex(encrypted);
69         } catch (IllegalBlockSizeException | BadPaddingException e) {
70             logger.warn("Failed to finally encrypt");
71             return "";
72         }
73     }
74
75     private static byte[] parseHexBinary(String s) {
76         final int len = s.length();
77
78         // "111" is not a valid hex encoding.
79         if (len % 2 != 0) {
80             throw new IllegalArgumentException("hexBinary needs to be even-length: " + s);
81         }
82
83         byte[] out = new byte[len / 2];
84
85         for (int i = 0; i < len; i += 2) {
86             int h = hexToBin(s.charAt(i));
87             int l = hexToBin(s.charAt(i + 1));
88             if (h == -1 || l == -1) {
89                 throw new IllegalArgumentException("contains illegal character for hexBinary: " + s);
90             }
91
92             out[i / 2] = (byte) (h * 16 + l);
93         }
94
95         return out;
96     }
97
98     private static int hexToBin(char ch) {
99         if ('0' <= ch && ch <= '9') {
100             return ch - '0';
101         }
102         if ('A' <= ch && ch <= 'F') {
103             return ch - 'A' + 10;
104         }
105         if ('a' <= ch && ch <= 'f') {
106             return ch - 'a' + 10;
107         }
108         return -1;
109     }
110
111     private String bytesToHex(byte[] bytes) {
112         char[] hexChars = new char[bytes.length * 2];
113         for (int j = 0; j < bytes.length; j++) {
114             int v = bytes[j] & 0xFF;
115             hexChars[j * 2] = HEX_ARRAY[v >>> 4];
116             hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
117         }
118         return new String(hexChars);
119     }
120 }