]> git.basschouten.com Git - openhab-addons.git/blob
16077ce82dcc01249d5f8e6f57e4c537710a39a2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.io.UnsupportedEncodingException;
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;
59         try {
60             keySpec = new SecretKeySpec(key.getBytes("UTF8"), "AES");
61         } catch (UnsupportedEncodingException e) {
62             logger.warn("Failed to construct SecretKeySpec");
63             return "";
64         }
65         try {
66             cipher.init(Cipher.ENCRYPT_MODE, keySpec, vector);
67         } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
68             logger.warn("Failed to init Cipher");
69             return "";
70         }
71         byte[] encrypted;
72         try {
73             encrypted = cipher.doFinal(text.getBytes());
74             return bytesToHex(encrypted);
75         } catch (IllegalBlockSizeException | BadPaddingException e) {
76             logger.warn("Failed to finally encrypt");
77             return "";
78         }
79     }
80
81     private static byte[] parseHexBinary(String s) {
82         final int len = s.length();
83
84         // "111" is not a valid hex encoding.
85         if (len % 2 != 0) {
86             throw new IllegalArgumentException("hexBinary needs to be even-length: " + s);
87         }
88
89         byte[] out = new byte[len / 2];
90
91         for (int i = 0; i < len; i += 2) {
92             int h = hexToBin(s.charAt(i));
93             int l = hexToBin(s.charAt(i + 1));
94             if (h == -1 || l == -1) {
95                 throw new IllegalArgumentException("contains illegal character for hexBinary: " + s);
96             }
97
98             out[i / 2] = (byte) (h * 16 + l);
99         }
100
101         return out;
102     }
103
104     private static int hexToBin(char ch) {
105         if ('0' <= ch && ch <= '9') {
106             return ch - '0';
107         }
108         if ('A' <= ch && ch <= 'F') {
109             return ch - 'A' + 10;
110         }
111         if ('a' <= ch && ch <= 'f') {
112             return ch - 'a' + 10;
113         }
114         return -1;
115     }
116
117     private String bytesToHex(byte[] bytes) {
118         char[] hexChars = new char[bytes.length * 2];
119         for (int j = 0; j < bytes.length; j++) {
120             int v = bytes[j] & 0xFF;
121             hexChars[j * 2] = HEX_ARRAY[v >>> 4];
122             hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
123         }
124         return new String(hexChars);
125     }
126 }