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