xref: /aosp_15_r20/build/bazel/rules/android/android_app_keystore.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
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