]> git.basschouten.com Git - openhab-addons.git/blob
3e3a9e974b8c0180a9b362ec19340e0644cba52e
[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.androidtv.internal.utils;
14
15 import java.io.ByteArrayInputStream;
16 import java.io.FileInputStream;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.math.BigInteger;
20 import java.nio.charset.StandardCharsets;
21 import java.security.GeneralSecurityException;
22 import java.security.Key;
23 import java.security.KeyFactory;
24 import java.security.KeyPair;
25 import java.security.KeyPairGenerator;
26 import java.security.KeyStore;
27 import java.security.NoSuchAlgorithmException;
28 import java.security.Security;
29 import java.security.Signature;
30 import java.security.cert.Certificate;
31 import java.security.cert.CertificateEncodingException;
32 import java.security.cert.CertificateException;
33 import java.security.cert.CertificateFactory;
34 import java.security.cert.X509Certificate;
35 import java.security.spec.PKCS8EncodedKeySpec;
36 import java.time.Duration;
37 import java.time.Instant;
38 import java.util.Base64;
39 import java.util.Date;
40
41 import javax.crypto.Cipher;
42 import javax.crypto.KeyGenerator;
43 import javax.crypto.NoSuchPaddingException;
44 import javax.crypto.spec.GCMParameterSpec;
45 import javax.crypto.spec.SecretKeySpec;
46
47 import org.bouncycastle.asn1.x500.X500Name;
48 import org.bouncycastle.cert.X509v3CertificateBuilder;
49 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
50 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
51 import org.bouncycastle.jce.provider.BouncyCastleProvider;
52 import org.bouncycastle.operator.ContentSigner;
53 import org.bouncycastle.operator.OperatorCreationException;
54 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
55 import org.eclipse.jdt.annotation.NonNullByDefault;
56 import org.eclipse.jdt.annotation.Nullable;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 /**
61  * The {@link AndroidTVPKI} class controls all aspects of the PKI/keyStore
62  *
63  * Some methods adapted from Bosch binding
64  *
65  * @author Ben Rosenblum - Initial contribution
66  */
67 @NonNullByDefault
68 public class AndroidTVPKI {
69
70     private final Logger logger = LoggerFactory.getLogger(AndroidTVPKI.class);
71
72     private final int keySize = 128;
73     private final int dataLength = 128;
74
75     private String privKey = "";
76     private String cert = "";
77     private String caCert = "";
78     private String keystoreFileName = "";
79     private String keystoreAlgorithm = "RSA";
80     private int keyLength = 2048;
81     private String alias = "openhab";
82     private String distName = "CN=openHAB, O=openHAB, L=None, ST=None, C=None";
83     private String cipher = "AES/GCM/NoPadding";
84     private String keyAlgorithm = "";
85
86     private @Nullable Cipher encryptionCipher;
87
88     public AndroidTVPKI() {
89         try {
90             encryptionCipher = Cipher.getInstance(cipher);
91         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
92             logger.debug("Could not get cipher instance", e);
93         }
94     }
95
96     public byte[] generateEncryptionKey() {
97         Key key;
98         try {
99             KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
100             keyGenerator.init(keySize);
101             key = keyGenerator.generateKey();
102             byte[] newKey = key.getEncoded();
103             this.keyAlgorithm = key.getAlgorithm();
104             return newKey;
105         } catch (NoSuchAlgorithmException e) {
106             logger.debug("Could not generate encryption keys", e);
107         }
108         return new byte[0];
109     }
110
111     private Key convertByteToKey(byte[] keyString) {
112         Key key = new SecretKeySpec(keyString, keyAlgorithm);
113         return key;
114     }
115
116     public String encrypt(String data, Key key) throws Exception {
117         return encrypt(data, key, this.cipher);
118     }
119
120     public String encrypt(String data, Key key, String cipher) throws Exception {
121         byte[] dataInBytes = data.getBytes();
122         Cipher encryptionCipher = this.encryptionCipher;
123         if (encryptionCipher != null) {
124             encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
125             byte[] encryptedBytes = encryptionCipher.doFinal(dataInBytes);
126             return Base64.getEncoder().encodeToString(encryptedBytes);
127         } else {
128             return "";
129         }
130     }
131
132     public String decrypt(String encryptedData, Key key) throws Exception {
133         return decrypt(encryptedData, key, this.cipher);
134     }
135
136     public String decrypt(String encryptedData, Key key, String cipher) throws Exception {
137         byte[] dataInBytes = Base64.getDecoder().decode(encryptedData);
138         Cipher decryptionCipher = Cipher.getInstance(cipher);
139         Cipher encryptionCipher = this.encryptionCipher;
140         if (encryptionCipher != null) {
141             GCMParameterSpec spec = new GCMParameterSpec(dataLength, encryptionCipher.getIV());
142             decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
143             byte[] decryptedBytes = decryptionCipher.doFinal(dataInBytes);
144             return new String(decryptedBytes);
145         } else {
146             return "";
147         }
148     }
149
150     public void setPrivKey(String privKey, byte[] keyString) throws Exception {
151         Key key = convertByteToKey(keyString);
152         this.privKey = encrypt(privKey, key);
153     }
154
155     public String getPrivKey(byte[] keyString) throws Exception {
156         Key key = convertByteToKey(keyString);
157         return decrypt(this.privKey, key);
158     }
159
160     public void setCert(String cert) {
161         this.cert = cert;
162     }
163
164     public void setCert(Certificate cert) throws CertificateEncodingException {
165         this.cert = new String(Base64.getEncoder().encode(cert.getEncoded()));
166     }
167
168     public Certificate getCert() throws CertificateException {
169         Certificate cert = CertificateFactory.getInstance("X.509")
170                 .generateCertificate(new ByteArrayInputStream(Base64.getDecoder().decode(this.cert.getBytes())));
171         return cert;
172     }
173
174     public void setCaCert(String caCert) {
175         this.caCert = caCert;
176     }
177
178     public void setCaCert(Certificate caCert) throws CertificateEncodingException {
179         this.caCert = new String(Base64.getEncoder().encode(caCert.getEncoded()));
180     }
181
182     public Certificate getCaCert() throws CertificateException {
183         Certificate caCert = CertificateFactory.getInstance("X.509")
184                 .generateCertificate(new ByteArrayInputStream(Base64.getDecoder().decode(this.caCert.getBytes())));
185         return caCert;
186     }
187
188     public void setAlias(String alias) {
189         this.alias = alias;
190     }
191
192     public String getAlias() {
193         return this.alias;
194     }
195
196     public void setAlgorithm(String keystoreAlgorithm) {
197         this.keystoreAlgorithm = keystoreAlgorithm;
198     }
199
200     public String getAlgorithm() {
201         return this.keystoreAlgorithm;
202     }
203
204     public void setKeyLength(int keyLength) {
205         this.keyLength = keyLength;
206     }
207
208     public int getKeyLength() {
209         return this.keyLength;
210     }
211
212     public void setDistName(String distName) {
213         this.distName = distName;
214     }
215
216     public String getDistName() {
217         return this.distName;
218     }
219
220     public void setKeystoreFileName(String keystoreFileName) {
221         this.keystoreFileName = keystoreFileName;
222     }
223
224     public String getKeystoreFileName() {
225         return this.keystoreFileName;
226     }
227
228     public void setKeys(String privKey, byte[] keyString, String cert) throws GeneralSecurityException, Exception {
229         setPrivKey(privKey, keyString);
230         setCert(cert);
231     }
232
233     public void setKeyStore(String keystoreFileName) {
234         this.keystoreFileName = keystoreFileName;
235     }
236
237     public void loadFromKeyStore(String keystoreFileName, String keystorePassword, byte[] keyString)
238             throws GeneralSecurityException, IOException, Exception {
239         this.keystoreFileName = keystoreFileName;
240         loadFromKeyStore(keystorePassword, keyString);
241     }
242
243     public void loadFromKeyStore(String keystorePassword, byte[] keyString)
244             throws GeneralSecurityException, IOException, Exception {
245         Key key = convertByteToKey(keyString);
246         KeyStore keystore = KeyStore.getInstance("JKS");
247         FileInputStream keystoreInputStream = new FileInputStream(this.keystoreFileName);
248         keystore.load(keystoreInputStream, keystorePassword.toCharArray());
249         byte[] byteKey = keystore.getKey(this.alias, keystorePassword.toCharArray()).getEncoded();
250         this.privKey = encrypt(new String(Base64.getEncoder().encode(byteKey)), key);
251         setCert(keystore.getCertificate(this.alias));
252         Certificate caCert = keystore.getCertificate("trustedCa");
253         if (caCert != null) {
254             setCaCert(caCert);
255         }
256     }
257
258     public KeyStore getKeyStore(String keystorePassword, byte[] keyString)
259             throws GeneralSecurityException, IOException, Exception {
260         KeyStore keystore = KeyStore.getInstance("JKS");
261         keystore.load(null, null);
262         byte[] pkcs8EncodedBytes = Base64.getDecoder().decode(getPrivKey(keyString));
263         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
264         KeyFactory kf = KeyFactory.getInstance(this.keystoreAlgorithm);
265         keystore.setKeyEntry(this.alias, kf.generatePrivate(keySpec), keystorePassword.toCharArray(),
266                 new java.security.cert.Certificate[] { getCert() });
267         if (!caCert.isEmpty()) {
268             keystore.setCertificateEntry("trustedCa", getCaCert());
269         }
270         return keystore;
271     }
272
273     public void saveKeyStore(String keystorePassword, byte[] keyString)
274             throws GeneralSecurityException, IOException, Exception {
275         saveKeyStore(this.keystoreFileName, keystorePassword, keyString);
276     }
277
278     public void saveKeyStore(String keystoreFileName, String keystorePassword, byte[] keyString)
279             throws GeneralSecurityException, IOException, Exception {
280         FileOutputStream keystoreStream = new FileOutputStream(keystoreFileName);
281         KeyStore keystore = getKeyStore(keystorePassword, keyString);
282         keystore.store(keystoreStream, keystorePassword.toCharArray());
283     }
284
285     private X509Certificate generateSelfSignedCertificate(KeyPair keyPair, String distName)
286             throws GeneralSecurityException, OperatorCreationException {
287         final Instant now = Instant.now();
288         final Date notBefore = Date.from(now);
289         final Date notAfter = Date.from(now.plus(Duration.ofDays(365 * 10)));
290         X500Name name = new X500Name(distName);
291         X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(name,
292                 BigInteger.valueOf(now.toEpochMilli()), notBefore, notAfter, name, keyPair.getPublic());
293         ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(keyPair.getPrivate());
294         return new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider())
295                 .getCertificate(certificateBuilder.build(contentSigner));
296     }
297
298     public void generateNewKeyPair(byte[] keyString)
299             throws GeneralSecurityException, OperatorCreationException, IOException, Exception {
300         Key key = convertByteToKey(keyString);
301         KeyPairGenerator kpg = KeyPairGenerator.getInstance(this.keystoreAlgorithm);
302         kpg.initialize(this.keyLength);
303         KeyPair kp = kpg.generateKeyPair();
304         Security.addProvider(new BouncyCastleProvider());
305         Signature signer = Signature.getInstance("SHA256withRSA", "BC");
306         signer.initSign(kp.getPrivate());
307         signer.update("openhab".getBytes(StandardCharsets.UTF_8));
308         signer.sign();
309         X509Certificate signedcert = generateSelfSignedCertificate(kp, this.distName);
310         this.privKey = encrypt(new String(Base64.getEncoder().encode(kp.getPrivate().getEncoded())), key);
311         setCert(signedcert);
312     }
313 }