Changeset View
Changeset View
Standalone View
Standalone View
src/org/kde/kdeconnect/Helpers/SecurityHelpers/SslHelper.java
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Line(s) | |||||
59 | import javax.net.ssl.SSLSocket; | 59 | import javax.net.ssl.SSLSocket; | ||
60 | import javax.net.ssl.SSLSocketFactory; | 60 | import javax.net.ssl.SSLSocketFactory; | ||
61 | import javax.net.ssl.TrustManager; | 61 | import javax.net.ssl.TrustManager; | ||
62 | import javax.net.ssl.TrustManagerFactory; | 62 | import javax.net.ssl.TrustManagerFactory; | ||
63 | import javax.net.ssl.X509TrustManager; | 63 | import javax.net.ssl.X509TrustManager; | ||
64 | 64 | | |||
65 | public class SslHelper { | 65 | public class SslHelper { | ||
66 | 66 | | |||
67 | public enum SslMode{ | 67 | public enum SslMode { | ||
68 | Client, | 68 | Client, | ||
69 | Server | 69 | Server | ||
70 | } | 70 | } | ||
71 | 71 | | |||
72 | public static X509Certificate certificate; //my device's certificate | 72 | public static X509Certificate certificate; //my device's certificate | ||
73 | 73 | | |||
74 | public static final BouncyCastleProvider BC = new BouncyCastleProvider(); | 74 | public static final BouncyCastleProvider BC = new BouncyCastleProvider(); | ||
75 | 75 | | |||
76 | public static void initialiseCertificate(Context context){ | 76 | public static void initialiseCertificate(Context context) { | ||
77 | PrivateKey privateKey; | 77 | PrivateKey privateKey; | ||
78 | PublicKey publicKey; | 78 | PublicKey publicKey; | ||
79 | 79 | | |||
80 | try { | 80 | try { | ||
81 | privateKey = RsaHelper.getPrivateKey(context); | 81 | privateKey = RsaHelper.getPrivateKey(context); | ||
82 | publicKey = RsaHelper.getPublicKey(context); | 82 | publicKey = RsaHelper.getPublicKey(context); | ||
83 | }catch (Exception e){ | 83 | } catch (Exception e) { | ||
84 | Log.e("SslHelper", "Error getting keys, can't create certificate"); | 84 | Log.e("SslHelper", "Error getting keys, can't create certificate"); | ||
85 | return; | 85 | return; | ||
86 | } | 86 | } | ||
87 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); | 87 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); | ||
88 | if (!settings.contains("certificate")) { | 88 | if (!settings.contains("certificate")) { | ||
89 | try { | 89 | try { | ||
90 | 90 | | |||
91 | X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); | 91 | X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); | ||
Show All 15 Lines | |||||
107 | ); | 107 | ); | ||
108 | ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(privateKey); | 108 | ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(privateKey); | ||
109 | certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateBuilder.build(contentSigner)); | 109 | certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateBuilder.build(contentSigner)); | ||
110 | 110 | | |||
111 | SharedPreferences.Editor edit = settings.edit(); | 111 | SharedPreferences.Editor edit = settings.edit(); | ||
112 | edit.putString("certificate", Base64.encodeToString(certificate.getEncoded(), 0)); | 112 | edit.putString("certificate", Base64.encodeToString(certificate.getEncoded(), 0)); | ||
113 | edit.apply(); | 113 | edit.apply(); | ||
114 | 114 | | |||
115 | } catch(Exception e) { | 115 | } catch (Exception e) { | ||
116 | e.printStackTrace(); | 116 | e.printStackTrace(); | ||
117 | Log.e("KDE/initialiseCert", "Exception"); | 117 | Log.e("KDE/initialiseCert", "Exception"); | ||
118 | } | 118 | } | ||
119 | 119 | | |||
120 | } else { | 120 | } else { | ||
121 | try { | 121 | try { | ||
122 | SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context); | 122 | SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context); | ||
123 | byte[] certificateBytes = Base64.decode(globalSettings.getString("certificate", ""), 0); | 123 | byte[] certificateBytes = Base64.decode(globalSettings.getString("certificate", ""), 0); | ||
Show All 15 Lines | |||||
139 | public static SSLContext getSslContext(Context context, String deviceId, boolean isDeviceTrusted) { | 139 | public static SSLContext getSslContext(Context context, String deviceId, boolean isDeviceTrusted) { | ||
140 | //TODO: Cache | 140 | //TODO: Cache | ||
141 | try { | 141 | try { | ||
142 | // Get device private key | 142 | // Get device private key | ||
143 | PrivateKey privateKey = RsaHelper.getPrivateKey(context); | 143 | PrivateKey privateKey = RsaHelper.getPrivateKey(context); | ||
144 | 144 | | |||
145 | // Get remote device certificate if trusted | 145 | // Get remote device certificate if trusted | ||
146 | X509Certificate remoteDeviceCertificate = null; | 146 | X509Certificate remoteDeviceCertificate = null; | ||
147 | if (isDeviceTrusted){ | 147 | if (isDeviceTrusted) { | ||
148 | SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); | 148 | SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); | ||
149 | byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0); | 149 | byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0); | ||
150 | X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes); | 150 | X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes); | ||
151 | remoteDeviceCertificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder); | 151 | remoteDeviceCertificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder); | ||
152 | } | 152 | } | ||
153 | 153 | | |||
154 | // Setup keystore | 154 | // Setup keystore | ||
155 | KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | 155 | KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | ||
156 | keyStore.load(null, null); | 156 | keyStore.load(null, null); | ||
157 | keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new Certificate[]{certificate}); | 157 | keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new Certificate[]{certificate}); | ||
158 | // Set certificate if device trusted | 158 | // Set certificate if device trusted | ||
159 | if (remoteDeviceCertificate != null){ | 159 | if (remoteDeviceCertificate != null) { | ||
160 | keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate); | 160 | keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate); | ||
161 | } | 161 | } | ||
162 | 162 | | |||
163 | // Setup key manager factory | 163 | // Setup key manager factory | ||
164 | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); | 164 | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); | ||
165 | keyManagerFactory.init(keyStore, "".toCharArray()); | 165 | keyManagerFactory.init(keyStore, "".toCharArray()); | ||
166 | 166 | | |||
167 | 167 | | |||
168 | // Setup default trust manager | 168 | // Setup default trust manager | ||
169 | TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); | 169 | TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); | ||
170 | trustManagerFactory.init(keyStore); | 170 | trustManagerFactory.init(keyStore); | ||
171 | 171 | | |||
172 | // Setup custom trust manager if device not trusted | 172 | // Setup custom trust manager if device not trusted | ||
173 | TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { | 173 | TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { | ||
174 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { | 174 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { | ||
175 | return new X509Certificate[0]; | 175 | return new X509Certificate[0]; | ||
176 | } | 176 | } | ||
177 | 177 | | |||
178 | @Override | 178 | @Override | ||
179 | public void checkClientTrusted(X509Certificate[] certs, String authType) { | 179 | public void checkClientTrusted(X509Certificate[] certs, String authType) { | ||
180 | } | 180 | } | ||
181 | 181 | | |||
182 | @Override | 182 | @Override | ||
183 | public void checkServerTrusted(X509Certificate[] certs, String authType) { | 183 | public void checkServerTrusted(X509Certificate[] certs, String authType) { | ||
184 | } | 184 | } | ||
185 | 185 | | |||
186 | } | 186 | } | ||
187 | }; | 187 | }; | ||
188 | 188 | | |||
189 | SSLContext tlsContext = SSLContext.getInstance("TLSv1"); //Newer TLS versions are only supported on API 16+ | 189 | SSLContext tlsContext = SSLContext.getInstance("TLSv1"); //Newer TLS versions are only supported on API 16+ | ||
190 | if (isDeviceTrusted) { | 190 | if (isDeviceTrusted) { | ||
191 | tlsContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), RandomHelper.secureRandom); | 191 | tlsContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), RandomHelper.secureRandom); | ||
192 | }else { | 192 | } else { | ||
193 | tlsContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, RandomHelper.secureRandom); | 193 | tlsContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, RandomHelper.secureRandom); | ||
194 | } | 194 | } | ||
195 | return tlsContext; | 195 | return tlsContext; | ||
196 | } catch (Exception e) { | 196 | } catch (Exception e) { | ||
197 | Log.e("KDE/SslHelper", "Error creating tls context"); | 197 | Log.e("KDE/SslHelper", "Error creating tls context"); | ||
198 | e.printStackTrace(); | 198 | e.printStackTrace(); | ||
199 | } | 199 | } | ||
200 | return null; | 200 | return null; | ||
201 | 201 | | |||
202 | } | 202 | } | ||
203 | 203 | | |||
204 | public static void configureSslSocket(SSLSocket socket, boolean isDeviceTrusted, boolean isClient) { | 204 | public static void configureSslSocket(SSLSocket socket, boolean isDeviceTrusted, boolean isClient) { | ||
205 | 205 | | |||
206 | socket.setEnabledProtocols(new String[]{ "TLSv1" }); //Newer TLS versions are only supported on API 16+ | 206 | socket.setEnabledProtocols(new String[]{"TLSv1"}); //Newer TLS versions are only supported on API 16+ | ||
207 | 207 | | |||
208 | // These cipher suites are most common of them that are accepted by kde and android during handshake | 208 | // These cipher suites are most common of them that are accepted by kde and android during handshake | ||
209 | ArrayList<String> supportedCiphers = new ArrayList<>(); | 209 | ArrayList<String> supportedCiphers = new ArrayList<>(); | ||
210 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | 210 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||
211 | supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); // API 20+ | 211 | supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); // API 20+ | ||
212 | supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); // API 20+ | 212 | supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); // API 20+ | ||
213 | supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // API 11+ | 213 | supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // API 11+ | ||
214 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { | 214 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { | ||
215 | supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // API 11+ | 215 | supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // API 11+ | ||
216 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA"); // API 9+ | 216 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA"); // API 9+ | ||
217 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5"); // API 9+ | 217 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5"); // API 9+ | ||
218 | } else { | 218 | } else { | ||
219 | // Following ciphers are for and due to old devices | 219 | // Following ciphers are for and due to old devices | ||
220 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA"); // API 9+ | 220 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA"); // API 9+ | ||
221 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5"); // API 9+ | 221 | supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5"); // API 9+ | ||
222 | supportedCiphers.add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA"); // API 9+ | 222 | supportedCiphers.add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA"); // API 9+ | ||
223 | } | 223 | } | ||
224 | socket.setEnabledCipherSuites(supportedCiphers.toArray(new String[supportedCiphers.size()])); | 224 | socket.setEnabledCipherSuites(supportedCiphers.toArray(new String[supportedCiphers.size()])); | ||
225 | 225 | | |||
226 | if (isClient){ | 226 | if (isClient) { | ||
227 | socket.setUseClientMode(true); | 227 | socket.setUseClientMode(true); | ||
228 | }else{ | 228 | } else { | ||
229 | socket.setUseClientMode(false); | 229 | socket.setUseClientMode(false); | ||
230 | if (isDeviceTrusted) { | 230 | if (isDeviceTrusted) { | ||
231 | socket.setNeedClientAuth(true); | 231 | socket.setNeedClientAuth(true); | ||
232 | } else { | 232 | } else { | ||
233 | socket.setWantClientAuth(true); | 233 | socket.setWantClientAuth(true); | ||
234 | } | 234 | } | ||
235 | } | 235 | } | ||
236 | 236 | | |||
237 | } | 237 | } | ||
238 | 238 | | |||
239 | public static SSLSocket convertToSslSocket(Context context, Socket socket, String deviceId, boolean isDeviceTrusted, boolean clientMode) throws IOException { | 239 | public static SSLSocket convertToSslSocket(Context context, Socket socket, String deviceId, boolean isDeviceTrusted, boolean clientMode) throws IOException { | ||
240 | SSLSocketFactory sslsocketFactory = SslHelper.getSslContext(context, deviceId, isDeviceTrusted).getSocketFactory(); | 240 | SSLSocketFactory sslsocketFactory = SslHelper.getSslContext(context, deviceId, isDeviceTrusted).getSocketFactory(); | ||
241 | SSLSocket sslsocket = (SSLSocket)sslsocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); | 241 | SSLSocket sslsocket = (SSLSocket) sslsocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); | ||
242 | SslHelper.configureSslSocket(sslsocket, isDeviceTrusted, clientMode); | 242 | SslHelper.configureSslSocket(sslsocket, isDeviceTrusted, clientMode); | ||
243 | return sslsocket; | 243 | return sslsocket; | ||
244 | } | 244 | } | ||
245 | 245 | | |||
246 | public static String getCertificateHash(Certificate certificate) { | 246 | public static String getCertificateHash(Certificate certificate) { | ||
247 | try { | 247 | try { | ||
248 | byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded()); | 248 | byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded()); | ||
249 | Formatter formatter = new Formatter(); | 249 | Formatter formatter = new Formatter(); | ||
250 | int i; | 250 | int i; | ||
251 | for (i = 0; i < hash.length-1; i++) { | 251 | for (i = 0; i < hash.length - 1; i++) { | ||
252 | formatter.format("%02x:", hash[i]); | 252 | formatter.format("%02x:", hash[i]); | ||
253 | } | 253 | } | ||
254 | formatter.format("%02x", hash[i]); | 254 | formatter.format("%02x", hash[i]); | ||
255 | return formatter.toString(); | 255 | return formatter.toString(); | ||
256 | } catch (Exception e) { | 256 | } catch (Exception e) { | ||
257 | return null; | 257 | return null; | ||
258 | } | 258 | } | ||
259 | } | 259 | } | ||
260 | 260 | | |||
261 | public static Certificate parseCertificate(byte[] certificateBytes) throws IOException, CertificateException { | 261 | public static Certificate parseCertificate(byte[] certificateBytes) throws IOException, CertificateException { | ||
262 | X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes); | 262 | X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes); | ||
263 | return new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder); | 263 | return new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder); | ||
264 | } | 264 | } | ||
265 | 265 | | |||
266 | } | 266 | } |