1# Copyright (C) 2022 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15load("@bazel_skylib//lib:paths.bzl", "paths") 16load("android_app_certificate.bzl", "AndroidAppCertificateInfo") 17 18AndroidAppKeystoreInfo = provider( 19 "Info needed for Android app keystores", 20 fields = { 21 "keystore": "JKS .keystore file housing certificate info", 22 }, 23) 24 25def _pk8_to_private_pem(ctx, openssl, pk8_file, private_pem_file): 26 """Converts a .pk8 private key file in DER format to a .pem private key file in PEM format.""" 27 args = ctx.actions.args() 28 args.add("pkcs8") 29 args.add_all(["-in", pk8_file]) 30 args.add_all(["-inform", "DER"]) 31 args.add_all(["-outform", "PEM"]) 32 args.add_all(["-out", private_pem_file]) 33 args.add("-nocrypt") # don't bother encrypting this private key since it is just an intermediate file 34 35 ctx.actions.run( 36 inputs = [pk8_file], 37 executable = openssl, 38 outputs = [private_pem_file], 39 arguments = [args], 40 mnemonic = "CreatePrivPEM", 41 ) 42 43def _pem_to_pk12(ctx, openssl, certificate_pem, private_key_pem, pk12_file): 44 """Converts an X.509 certificate and private key pair of PEM files to a single PKCS12 keystore file.""" 45 args = ctx.actions.args() 46 args.add("pkcs12") 47 args.add("-export") 48 args.add_all(["-in", certificate_pem]) 49 args.add_all(["-inkey", private_key_pem]) 50 args.add_all(["-out", pk12_file]) 51 args.add_all(["-name", "android"]) 52 53 # openssl requires a password and will request a 54 # password from STDIN if we don't supply one here 55 args.add_all(["-passout", "pass:android"]) 56 57 ctx.actions.run( 58 inputs = [ 59 certificate_pem, 60 private_key_pem, 61 ], 62 executable = openssl, 63 outputs = [pk12_file], 64 arguments = [args], 65 mnemonic = "CreatePK12", 66 ) 67 68def _pk12_to_keystore(ctx, pk12_file, keystore_file): 69 """Converts a PKCS12 keystore file to a JKS keystore file.""" 70 java_runtime = ctx.attr._java_runtime[java_common.JavaRuntimeInfo] 71 keytool = paths.join(java_runtime.java_home, "bin", "keytool") 72 args = ctx.actions.args() 73 args.add("-importkeystore") 74 args.add_all(["-destkeystore", keystore_file]) 75 args.add_all(["-srckeystore", pk12_file]) 76 args.add_all(["-srcstoretype", "PKCS12"]) 77 args.add_all(["-srcstorepass", "android"]) 78 79 # apksigner expects keystores provided by the debug_signing_keys attribute 80 # to be secured with the password "android" 81 args.add_all(["-deststorepass", "android"]) 82 83 ctx.actions.run( 84 inputs = [pk12_file], 85 executable = keytool, 86 tools = [java_runtime.files], 87 outputs = [keystore_file], 88 arguments = [args], 89 mnemonic = "CreateKeystore", 90 ) 91 92def _android_app_keystore_rule_impl(ctx): 93 openssl = ctx.executable._openssl 94 95 private_pem = ctx.actions.declare_file(ctx.attr.name + ".priv.pem") 96 pk12 = ctx.actions.declare_file(ctx.attr.name + ".pk12") 97 keystore = ctx.actions.declare_file(ctx.attr.name + ".keystore") 98 99 pk8_file = ctx.attr.certificate[AndroidAppCertificateInfo].pk8 100 pem_file = ctx.attr.certificate[AndroidAppCertificateInfo].pem 101 _pk8_to_private_pem(ctx, openssl, pk8_file, private_pem) 102 _pem_to_pk12(ctx, openssl, pem_file, private_pem, pk12) 103 _pk12_to_keystore(ctx, pk12, keystore) 104 105 return [ 106 AndroidAppKeystoreInfo( 107 keystore = keystore, 108 ), 109 DefaultInfo(files = depset(direct = [keystore])), 110 ] 111 112"""Converts an android_app_certificate (i.e. pem/pk8 pair) into a JKS keystore""" 113android_app_keystore = rule( 114 implementation = _android_app_keystore_rule_impl, 115 attrs = { 116 "certificate": attr.label(mandatory = True, providers = [AndroidAppCertificateInfo]), 117 "_openssl": attr.label( 118 default = Label("//prebuilts/build-tools:linux-x86/bin/openssl"), 119 allow_single_file = True, 120 executable = True, 121 cfg = "exec", 122 doc = "An OpenSSL compatible tool.", 123 ), 124 "_java_runtime": attr.label( 125 default = Label("@bazel_tools//tools/jdk:current_java_runtime"), 126 cfg = "exec", 127 providers = [java_common.JavaRuntimeInfo], 128 ), 129 }, 130 provides = [AndroidAppKeystoreInfo], 131) 132