]> git.basschouten.com Git - openhab-addons.git/blob
aad3a8a89a8ac0267e2b7287b5ec3889933ae769
[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 keystoreFileName = "";
78     private String keystoreAlgorithm = "RSA";
79     private int keyLength = 2048;
80     private String alias = "openhab";
81     private String distName = "CN=openHAB, O=openHAB, L=None, ST=None, C=None";
82     private String cipher = "AES/GCM/NoPadding";
83     private String keyAlgorithm = "";
84
85     private @Nullable Cipher encryptionCipher;
86
87     public AndroidTVPKI() {
88         try {
89             encryptionCipher = Cipher.getInstance(cipher);
90         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
91             logger.debug("Could not get cipher instance", e);
92         }
93     }
94
95     public byte[] generateEncryptionKey() {
96         Key key;
97         try {
98             KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
99             keyGenerator.init(keySize);
100             key = keyGenerator.generateKey();
101             byte[] newKey = key.getEncoded();
102             this.keyAlgorithm = key.getAlgorithm();
103             return newKey;
104         } catch (NoSuchAlgorithmException e) {
105             logger.debug("Could not generate encryption keys", e);
106         }
107         return new byte[0];
108     }
109
110     private Key convertByteToKey(byte[] keyString) {
111         Key key = new SecretKeySpec(keyString, keyAlgorithm);
112         return key;
113     }
114
115     public String encrypt(String data, Key key) throws Exception {
116         return encrypt(data, key, this.cipher);
117     }
118
119     public String encrypt(String data, Key key, String cipher) throws Exception {
120         byte[] dataInBytes = data.getBytes();
121         Cipher encryptionCipher = this.encryptionCipher;
122         if (encryptionCipher != null) {
123             encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
124             byte[] encryptedBytes = encryptionCipher.doFinal(dataInBytes);
125             return Base64.getEncoder().encodeToString(encryptedBytes);
126         } else {
127             return "";
128         }
129     }
130
131     public String decrypt(String encryptedData, Key key) throws Exception {
132         return decrypt(encryptedData, key, this.cipher);
133     }
134
135     public String decrypt(String encryptedData, Key key, String cipher) throws Exception {
136         byte[] dataInBytes = Base64.getDecoder().decode(encryptedData);
137         Cipher decryptionCipher = Cipher.getInstance(cipher);
138         Cipher encryptionCipher = this.encryptionCipher;
139         if (encryptionCipher != null) {
140             GCMParameterSpec spec = new GCMParameterSpec(dataLength, encryptionCipher.getIV());
141             decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
142             byte[] decryptedBytes = decryptionCipher.doFinal(dataInBytes);
143             return new String(decryptedBytes);
144         } else {
145             return "";
146         }
147     }
148
149     public void setPrivKey(String privKey, byte[] keyString) throws Exception {
150         Key key = convertByteToKey(keyString);
151         this.privKey = encrypt(privKey, key);
152     }
153
154     public String getPrivKey(byte[] keyString) throws Exception {
155         Key key = convertByteToKey(keyString);
156         return decrypt(this.privKey, key);
157     }
158
159     public void setCert(String cert) {
160         this.cert = cert;
161     }
162
163     public void setCert(Certificate cert) throws CertificateEncodingException {
164         this.cert = new String(Base64.getEncoder().encode(cert.getEncoded()));
165     }
166
167     public Certificate getCert() throws CertificateException {
168         Certificate cert = CertificateFactory.getInstance("X.509")
169                 .generateCertificate(new ByteArrayInputStream(Base64.getDecoder().decode(this.cert.getBytes())));
170         return cert;
171     }
172
173     public void setAlias(String alias) {
174         this.alias = alias;
175     }
176
177     public String getAlias() {
178         return this.alias;
179     }
180
181     public void setAlgorithm(String keystoreAlgorithm) {
182         this.keystoreAlgorithm = keystoreAlgorithm;
183     }
184
185     public String getAlgorithm() {
186         return this.keystoreAlgorithm;
187     }
188
189     public void setKeyLength(int keyLength) {
190         this.keyLength = keyLength;
191     }
192
193     public int getKeyLength() {
194         return this.keyLength;
195     }
196
197     public void setDistName(String distName) {
198         this.distName = distName;
199     }
200
201     public String getDistName() {
202         return this.distName;
203     }
204
205     public void setKeystoreFileName(String keystoreFileName) {
206         this.keystoreFileName = keystoreFileName;
207     }
208
209     public String getKeystoreFileName() {
210         return this.keystoreFileName;
211     }
212
213     public void setKeys(String privKey, byte[] keyString, String cert) throws GeneralSecurityException, Exception {
214         setPrivKey(privKey, keyString);
215         setCert(cert);
216     }
217
218     public void setKeyStore(String keystoreFileName) {
219         this.keystoreFileName = keystoreFileName;
220     }
221
222     public void loadFromKeyStore(String keystoreFileName, String keystorePassword, byte[] keyString)
223             throws GeneralSecurityException, IOException, Exception {
224         this.keystoreFileName = keystoreFileName;
225         loadFromKeyStore(keystorePassword, keyString);
226     }
227
228     public void loadFromKeyStore(String keystorePassword, byte[] keyString)
229             throws GeneralSecurityException, IOException, Exception {
230         Key key = convertByteToKey(keyString);
231         KeyStore keystore = KeyStore.getInstance("JKS");
232         FileInputStream keystoreInputStream = new FileInputStream(this.keystoreFileName);
233         keystore.load(keystoreInputStream, keystorePassword.toCharArray());
234         byte[] byteKey = keystore.getKey(this.alias, keystorePassword.toCharArray()).getEncoded();
235         this.privKey = encrypt(new String(Base64.getEncoder().encode(byteKey)), key);
236         setCert(keystore.getCertificate(this.alias));
237     }
238
239     public KeyStore getKeyStore(String keystorePassword, byte[] keyString)
240             throws GeneralSecurityException, IOException, Exception {
241         KeyStore keystore = KeyStore.getInstance("JKS");
242         keystore.load(null, null);
243         byte[] pkcs8EncodedBytes = Base64.getDecoder().decode(getPrivKey(keyString));
244         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
245         KeyFactory kf = KeyFactory.getInstance(this.keystoreAlgorithm);
246         keystore.setKeyEntry(this.alias, kf.generatePrivate(keySpec), keystorePassword.toCharArray(),
247                 new java.security.cert.Certificate[] { getCert() });
248         return keystore;
249     }
250
251     public void saveKeyStore(String keystorePassword, byte[] keyString)
252             throws GeneralSecurityException, IOException, Exception {
253         saveKeyStore(this.keystoreFileName, keystorePassword, keyString);
254     }
255
256     public void saveKeyStore(String keystoreFileName, String keystorePassword, byte[] keyString)
257             throws GeneralSecurityException, IOException, Exception {
258         FileOutputStream keystoreStream = new FileOutputStream(keystoreFileName);
259         KeyStore keystore = getKeyStore(keystorePassword, keyString);
260         keystore.store(keystoreStream, keystorePassword.toCharArray());
261     }
262
263     private X509Certificate generateSelfSignedCertificate(KeyPair keyPair, String distName)
264             throws GeneralSecurityException, OperatorCreationException {
265         final Instant now = Instant.now();
266         final Date notBefore = Date.from(now);
267         final Date notAfter = Date.from(now.plus(Duration.ofDays(365 * 10)));
268         X500Name name = new X500Name(distName);
269         X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(name,
270                 BigInteger.valueOf(now.toEpochMilli()), notBefore, notAfter, name, keyPair.getPublic());
271         ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(keyPair.getPrivate());
272         return new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider())
273                 .getCertificate(certificateBuilder.build(contentSigner));
274     }
275
276     public void generateNewKeyPair(byte[] keyString)
277             throws GeneralSecurityException, OperatorCreationException, IOException, Exception {
278         Key key = convertByteToKey(keyString);
279         KeyPairGenerator kpg = KeyPairGenerator.getInstance(this.keystoreAlgorithm);
280         kpg.initialize(this.keyLength);
281         KeyPair kp = kpg.generateKeyPair();
282         Security.addProvider(new BouncyCastleProvider());
283         Signature signer = Signature.getInstance("SHA256withRSA", "BC");
284         signer.initSign(kp.getPrivate());
285         signer.update("openhab".getBytes(StandardCharsets.UTF_8));
286         signer.sign();
287         X509Certificate signedcert = generateSelfSignedCertificate(kp, this.distName);
288         this.privKey = encrypt(new String(Base64.getEncoder().encode(kp.getPrivate().getEncoded())), key);
289         setCert(signedcert);
290     }
291 }