]> git.basschouten.com Git - openhab-addons.git/blob
0e6f54fdc283914a6426c2fa556e732e3bede97a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.tapocontrol.internal.api.protocol.aes;
14
15 import static java.util.Base64.*;
16 import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*;
17
18 import java.security.KeyFactory;
19 import java.security.PrivateKey;
20 import java.security.spec.PKCS8EncodedKeySpec;
21
22 import javax.crypto.Cipher;
23 import javax.crypto.spec.IvParameterSpec;
24 import javax.crypto.spec.SecretKeySpec;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;
28 import org.openhab.binding.tapocontrol.internal.helpers.TapoKeyPair;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * TAPO-CIPHER
34  * Based on K4CZP3R's p100-java-poc
35  * 
36  * @author Christian Wild - Initial Initial contribution
37  */
38 @NonNullByDefault
39 public class SecurePasstroughCipher {
40     private final Logger logger = LoggerFactory.getLogger(SecurePasstroughCipher.class);
41     protected static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
42     protected static final String CIPHER_ALGORITHM = "AES";
43     protected static final String CIPHER_CHARSET = "UTF-8";
44     protected static final String HANDSHAKE_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
45     protected static final String HANDSHAKE_ALGORITHM = "RSA";
46     protected static final String HANDSHAKE_CHARSET = "UTF-8";
47
48     @NonNullByDefault({})
49     private Cipher encodeCipher;
50     @NonNullByDefault({})
51     private Cipher decodeCipher;
52
53     /**
54      * CREATE NEW EMPTY CIPHER
55      */
56     public SecurePasstroughCipher() {
57     }
58
59     /**
60      * CREATE NEW CIPHER WITH KEY AND CREDENTIALS
61      * 
62      * @param handshakeKey key from Handshake-Request
63      * @param keyPair keyPair
64      * @throws TapoErrorHandler
65      */
66     public SecurePasstroughCipher(String handshakeKey, TapoKeyPair keyPair) throws TapoErrorHandler {
67         setKey(handshakeKey, keyPair);
68     }
69
70     /**
71      * SET NEW KEY AND CREDENTIALS
72      * 
73      * @param handshakeKey key from Handshake-Request
74      * @param keyPair keyPair
75      */
76     public void setKey(String handshakeKey, TapoKeyPair keyPair) throws TapoErrorHandler {
77         logger.trace("Init passtroughCipher with key: {} ", handshakeKey);
78         try {
79             byte[] decode = getMimeDecoder().decode(handshakeKey.getBytes(HANDSHAKE_CHARSET));
80             byte[] decode2 = getMimeDecoder().decode(keyPair.getPrivateKeyBytes());
81             Cipher instance = Cipher.getInstance(HANDSHAKE_TRANSFORMATION);
82             KeyFactory kf = KeyFactory.getInstance(HANDSHAKE_ALGORITHM);
83             PrivateKey p = kf.generatePrivate(new PKCS8EncodedKeySpec(decode2));
84             instance.init(Cipher.DECRYPT_MODE, p);
85             byte[] doFinal = instance.doFinal(decode);
86             byte[] bArr = new byte[16];
87             byte[] bArr2 = new byte[16];
88             System.arraycopy(doFinal, 0, bArr, 0, 16);
89             System.arraycopy(doFinal, 16, bArr2, 0, 16);
90             initCipher(bArr, bArr2);
91         } catch (Exception e) {
92             logger.warn("handshake Failed: {}", e.getMessage());
93             throw new TapoErrorHandler(ERR_API_HAND_SHAKE_FAILED, e.getMessage());
94         }
95     }
96
97     /**
98      * INIT ENCODE/DECDE-CIPHERS
99      * 
100      * @param bArr
101      * @param bArr2
102      * @throws Exception
103      */
104     protected void initCipher(byte[] bArr, byte[] bArr2) throws Exception {
105         try {
106             SecretKeySpec secretKeySpec = new SecretKeySpec(bArr, CIPHER_ALGORITHM);
107             IvParameterSpec ivParameterSpec = new IvParameterSpec(bArr2);
108             encodeCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
109             decodeCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
110             encodeCipher.init(1, secretKeySpec, ivParameterSpec);
111             decodeCipher.init(2, secretKeySpec, ivParameterSpec);
112         } catch (Exception e) {
113             logger.warn("initChiper failed: {}", e.getMessage());
114             encodeCipher = null;
115             decodeCipher = null;
116         }
117     }
118
119     /**
120      * ENCODE STRING
121      * 
122      * @param str source string to encode
123      * @return encoded string
124      * @throws Exception
125      */
126     public String encode(String str) throws Exception {
127         byte[] doFinal;
128         doFinal = encodeCipher.doFinal(str.getBytes(CIPHER_CHARSET));
129         String encrypted = getMimeEncoder().encodeToString(doFinal);
130         return encrypted.replace("\r\n", "");
131     }
132
133     /**
134      * DECODE STRING
135      * 
136      * @param str source string to decode
137      * @return decoded string
138      * @throws Exception
139      */
140     public String decode(String str) throws Exception {
141         byte[] data = getMimeDecoder().decode(str.getBytes(CIPHER_CHARSET));
142         byte[] doFinal;
143         doFinal = decodeCipher.doFinal(data);
144         return new String(doFinal);
145     }
146 }