• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.virtualization.terminal
17 
18 import android.content.Context
19 import android.security.keystore.KeyGenParameterSpec
20 import android.security.keystore.KeyProperties
21 import android.util.Base64
22 import android.util.Log
23 import java.io.File
24 import java.io.FileOutputStream
25 import java.io.IOException
26 import java.lang.Exception
27 import java.lang.RuntimeException
28 import java.security.InvalidAlgorithmParameterException
29 import java.security.KeyPairGenerator
30 import java.security.KeyStore
31 import java.security.NoSuchAlgorithmException
32 import java.security.NoSuchProviderException
33 import java.security.cert.Certificate
34 import java.security.cert.CertificateEncodingException
35 import java.security.cert.CertificateExpiredException
36 import java.security.cert.CertificateNotYetValidException
37 import java.security.cert.X509Certificate
38 
39 object CertificateUtils {
40     private const val ALIAS = "ttyd"
41 
42     fun createOrGetKey(): KeyStore.PrivateKeyEntry {
43         try {
44             val ks = KeyStore.getInstance("AndroidKeyStore")
45             ks.load(null)
46 
47             if (!ks.containsAlias(ALIAS)) {
48                 Log.d(MainActivity.TAG, "there is no keypair, will generate it")
49                 createKey()
50             } else if (ks.getCertificate(ALIAS) !is X509Certificate) {
51                 Log.d(MainActivity.TAG, "certificate isn't X509Certificate or it is invalid")
52                 createKey()
53             } else {
54                 try {
55                     (ks.getCertificate(ALIAS) as X509Certificate).checkValidity()
56                 } catch (e: CertificateExpiredException) {
57                     Log.d(MainActivity.TAG, "certificate is invalid", e)
58                     createKey()
59                 } catch (e: CertificateNotYetValidException) {
60                     Log.d(MainActivity.TAG, "certificate is invalid", e)
61                     createKey()
62                 }
63             }
64             return ks.getEntry(ALIAS, null) as KeyStore.PrivateKeyEntry
65         } catch (e: Exception) {
66             throw RuntimeException("cannot generate or get key", e)
67         }
68     }
69 
70     @Throws(
71         NoSuchAlgorithmException::class,
72         NoSuchProviderException::class,
73         InvalidAlgorithmParameterException::class,
74     )
75     private fun createKey() {
76         val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
77         kpg.initialize(
78             KeyGenParameterSpec.Builder(
79                     ALIAS,
80                     KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY,
81                 )
82                 .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
83                 .build()
84         )
85 
86         kpg.generateKeyPair()
87     }
88 
89     fun writeCertificateToFile(context: Context, cert: Certificate) {
90         val certFile = File(context.getFilesDir(), "ca.crt")
91         try {
92             FileOutputStream(certFile).use { writer ->
93                 val certBegin = "-----BEGIN CERTIFICATE-----\n"
94                 val certEnd = "-----END CERTIFICATE-----\n"
95                 val output =
96                     (certBegin +
97                         Base64.encodeToString(cert.encoded, Base64.DEFAULT)
98                             .replace("(.{64})".toRegex(), "$1\n") +
99                         certEnd)
100                 writer.write(output.toByteArray())
101             }
102         } catch (e: IOException) {
103             throw RuntimeException("cannot write certs", e)
104         } catch (e: CertificateEncodingException) {
105             throw RuntimeException("cannot write certs", e)
106         }
107     }
108 }
109