Move adb RSA utilities into its own library.

Since both the client and daemon will now be generating keys.

BUG: b/111434128

Test: atest adb_crypto_test
Change-Id: I6fac562ae5629ab30b6639fbd88d822dae6e96bd
diff --git a/Android.bp b/Android.bp
index 675525c..c71138a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -255,6 +255,8 @@
     },
 
     static_libs: [
+        "libadb_crypto",
+        "libadb_protos",
         "libbase",
         "libcrypto_utils",
         "libcrypto",
@@ -272,6 +274,7 @@
     defaults: ["adb_defaults"],
     srcs: libadb_test_srcs,
     static_libs: [
+        "libadb_crypto",
         "libadb_host",
         "libbase",
         "libcutils",
@@ -347,6 +350,7 @@
     ],
 
     static_libs: [
+        "libadb_crypto",
         "libadb_host",
         "libandroidfw",
         "libbase",
@@ -422,6 +426,7 @@
     ],
 
     shared_libs: [
+        "libadb_crypto",
         "libadbd_auth",
         "libasyncio",
         "libbase",
@@ -765,6 +770,7 @@
         "fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
     ],
     static_libs: [
+        "libadb_crypto",
         "libadb_host",
         "libandroidfw",
         "libbase",
diff --git a/client/auth.cpp b/client/auth.cpp
index e8be784..dcf4bc0 100644
--- a/client/auth.cpp
+++ b/client/auth.cpp
@@ -29,6 +29,7 @@
 #include <set>
 #include <string>
 
+#include <adb/crypto/rsa_2048_key.h>
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -53,100 +54,50 @@
     *new std::map<std::string, std::shared_ptr<RSA>>;
 static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
 
-static std::string get_user_info() {
-    std::string hostname;
-    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
-    char buf[64];
-    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
-    if (hostname.empty()) hostname = "unknown";
+using namespace adb::crypto;
 
-    std::string username;
-    if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined(_WIN32)
-    if (username.empty() && getlogin()) username = getlogin();
-#endif
-    if (username.empty()) hostname = "unknown";
-
-    return " " + username + "@" + hostname;
-}
-
-static bool calculate_public_key(std::string* out, RSA* private_key) {
-    uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
-    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
-        LOG(ERROR) << "Failed to convert to public key";
-        return false;
-    }
-
-    size_t expected_length;
-    if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
-        LOG(ERROR) << "Public key too large to base64 encode";
-        return false;
-    }
-
-    out->resize(expected_length);
-    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
-                                           sizeof(binary_key_data));
-    out->resize(actual_length);
-    out->append(get_user_info());
-    return true;
-}
-
-static int generate_key(const std::string& file) {
+static bool generate_key(const std::string& file) {
     LOG(INFO) << "generate_key(" << file << ")...";
 
-    mode_t old_mask;
-    FILE *f = nullptr;
-    int ret = 0;
+    auto rsa_2048 = CreateRSA2048Key();
+    if (!rsa_2048) {
+        LOG(ERROR) << "Unable to create key";
+        return false;
+    }
     std::string pubkey;
 
-    EVP_PKEY* pkey = EVP_PKEY_new();
-    BIGNUM* exponent = BN_new();
-    RSA* rsa = RSA_new();
-    if (!pkey || !exponent || !rsa) {
-        LOG(ERROR) << "Failed to allocate key";
-        goto out;
-    }
+    RSA* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    CHECK(rsa);
 
-    BN_set_word(exponent, RSA_F4);
-    RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
-    EVP_PKEY_set1_RSA(pkey, rsa);
-
-    if (!calculate_public_key(&pubkey, rsa)) {
+    if (!CalculatePublicKey(&pubkey, rsa)) {
         LOG(ERROR) << "failed to calculate public key";
-        goto out;
+        return false;
     }
 
-    old_mask = umask(077);
+    mode_t old_mask = umask(077);
 
-    f = fopen(file.c_str(), "w");
+    std::unique_ptr<FILE, decltype(&fclose)> f(nullptr, &fclose);
+    f.reset(fopen(file.c_str(), "w"));
     if (!f) {
         PLOG(ERROR) << "Failed to open " << file;
         umask(old_mask);
-        goto out;
+        return false;
     }
 
     umask(old_mask);
 
-    if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
+    if (!PEM_write_PrivateKey(f.get(), rsa_2048->GetEvpPkey(), nullptr, nullptr, 0, nullptr,
+                              nullptr)) {
         LOG(ERROR) << "Failed to write key";
-        goto out;
+        return false;
     }
 
     if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
         PLOG(ERROR) << "failed to write public key";
-        goto out;
+        return false;
     }
 
-    ret = 1;
-
-out:
-    if (f) fclose(f);
-    EVP_PKEY_free(pkey);
-    RSA_free(rsa);
-    BN_free(exponent);
-    return ret;
+    return true;
 }
 
 static std::string hash_key(RSA* key) {
@@ -325,7 +276,7 @@
     if (!privkey) {
         return false;
     }
-    return calculate_public_key(out, privkey.get());
+    return CalculatePublicKey(out, privkey.get());
 }
 
 std::string adb_auth_get_userkey() {
@@ -343,7 +294,7 @@
 }
 
 int adb_auth_keygen(const char* filename) {
-    return (generate_key(filename) == 0);
+    return !generate_key(filename);
 }
 
 int adb_auth_pubkey(const char* filename) {
diff --git a/crypto/Android.bp b/crypto/Android.bp
new file mode 100644
index 0000000..da4869a
--- /dev/null
+++ b/crypto/Android.bp
@@ -0,0 +1,85 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "libadb_crypto_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+
+    compile_multilib: "both",
+
+    srcs: [
+        "key.cpp",
+        "rsa_2048_key.cpp",
+        "x509_generator.cpp",
+    ],
+
+    target: {
+        windows: {
+            compile_multilib: "first",
+            enabled: true,
+        },
+    },
+
+    export_include_dirs: ["include"],
+
+    visibility: [
+        "//system/core/adb:__subpackages__",
+    ],
+
+    host_supported: true,
+    recovery_available: true,
+
+    stl: "libc++_static",
+
+    shared_libs: [
+        "libadb_protos",
+        "libbase",
+        "liblog",
+        "libcrypto",
+        "libcrypto_utils",
+    ],
+}
+
+cc_library {
+    name: "libadb_crypto",
+    defaults: ["libadb_crypto_defaults"],
+
+    apex_available: [
+        "com.android.adbd",
+        "test_com.android.adbd",
+    ],
+
+    static_libs: [
+        "libadb_protos",
+    ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+    name: "libadb_crypto_static",
+    defaults: ["libadb_crypto_defaults"],
+
+    apex_available: [
+        "//apex_available:platform",
+    ],
+
+    static_libs: [
+        "libadb_protos_static",
+    ],
+}
diff --git a/crypto/include/adb/crypto/key.h b/crypto/include/adb/crypto/key.h
new file mode 100644
index 0000000..d9ce69e
--- /dev/null
+++ b/crypto/include/adb/crypto/key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <openssl/evp.h>
+
+#include "key_type.pb.h"
+
+namespace adb {
+namespace crypto {
+
+// Class that represents a public/private key pair.
+class Key {
+  public:
+    explicit Key(bssl::UniquePtr<EVP_PKEY>&& pkey, adb::proto::KeyType type)
+        : pkey_(std::move(pkey)), key_type_(type) {}
+    Key(Key&&) = default;
+    Key& operator=(Key&&) = default;
+
+    EVP_PKEY* GetEvpPkey() const { return pkey_.get(); }
+    adb::proto::KeyType GetKeyType() const { return key_type_; }
+    static std::string ToPEMString(EVP_PKEY* pkey);
+
+  private:
+    bssl::UniquePtr<EVP_PKEY> pkey_;
+    adb::proto::KeyType key_type_;
+};  // Key
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/include/adb/crypto/rsa_2048_key.h b/crypto/include/adb/crypto/rsa_2048_key.h
new file mode 100644
index 0000000..2983a84
--- /dev/null
+++ b/crypto/include/adb/crypto/rsa_2048_key.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include "adb/crypto/key.h"
+
+namespace adb {
+namespace crypto {
+
+// Create a new RSA2048 key pair.
+std::optional<Key> CreateRSA2048Key();
+
+// Generates the public key from the RSA private key.
+bool CalculatePublicKey(std::string* out, RSA* private_key);
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/include/adb/crypto/x509_generator.h b/crypto/include/adb/crypto/x509_generator.h
new file mode 100644
index 0000000..a269243
--- /dev/null
+++ b/crypto/include/adb/crypto/x509_generator.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/x509v3.h>
+
+namespace adb {
+namespace crypto {
+
+// Generate a X.509 certificate based on the key |pkey|.
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey);
+
+// Convert X509* to PEM string format
+std::string X509ToPEMString(X509* x509);
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/key.cpp b/crypto/key.cpp
new file mode 100644
index 0000000..4d87006
--- /dev/null
+++ b/crypto/key.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/key.h"
+
+#include <android-base/logging.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+// static
+std::string Key::ToPEMString(EVP_PKEY* pkey) {
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    int rc = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr);
+    if (rc != 1) {
+        LOG(ERROR) << "PEM_write_bio_PKCS8PrivateKey failed";
+        return "";
+    }
+
+    BUF_MEM* mem = nullptr;
+    BIO_get_mem_ptr(bio.get(), &mem);
+    if (!mem || !mem->data || !mem->length) {
+        LOG(ERROR) << "BIO_get_mem_ptr failed";
+        return "";
+    }
+
+    return std::string(mem->data, mem->length);
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/rsa_2048_key.cpp b/crypto/rsa_2048_key.cpp
new file mode 100644
index 0000000..7911af9
--- /dev/null
+++ b/crypto/rsa_2048_key.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/rsa_2048_key.h"
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+std::string get_user_info() {
+    std::string hostname;
+    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+    char buf[64];
+    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+    if (hostname.empty()) hostname = "unknown";
+
+    std::string username;
+    if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined(_WIN32)
+    if (username.empty() && getlogin()) username = getlogin();
+#endif
+    if (username.empty()) hostname = "unknown";
+
+    return " " + username + "@" + hostname;
+}
+
+}  // namespace
+
+bool CalculatePublicKey(std::string* out, RSA* private_key) {
+    uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Failed to convert to public key";
+        return false;
+    }
+
+    size_t expected_length;
+    if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Public key too large to base64 encode";
+        return false;
+    }
+
+    out->resize(expected_length);
+    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
+                                           sizeof(binary_key_data));
+    out->resize(actual_length);
+    out->append(get_user_info());
+    return true;
+}
+
+std::optional<Key> CreateRSA2048Key() {
+    bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+    bssl::UniquePtr<BIGNUM> exponent(BN_new());
+    bssl::UniquePtr<RSA> rsa(RSA_new());
+    if (!pkey || !exponent || !rsa) {
+        LOG(ERROR) << "Failed to allocate key";
+        return std::nullopt;
+    }
+
+    BN_set_word(exponent.get(), RSA_F4);
+    RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
+    EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
+
+    return std::optional<Key>{Key(std::move(pkey), adb::proto::KeyType::RSA_2048)};
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/tests/Android.bp b/crypto/tests/Android.bp
new file mode 100644
index 0000000..b32dcf7
--- /dev/null
+++ b/crypto/tests/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "adb_crypto_test",
+    srcs: [
+        "rsa_2048_key_test.cpp",
+        "x509_generator_test.cpp",
+    ],
+
+    compile_multilib: "first",
+
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libprotobuf-cpp-lite",
+    ],
+
+    // Let's statically link them so we don't have to install it onto the
+    // system image for testing.
+    static_libs: [
+        "libadb_crypto_static",
+        "libadb_protos_static",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/crypto/tests/key_test.cpp b/crypto/tests/key_test.cpp
new file mode 100644
index 0000000..1feb6e8
--- /dev/null
+++ b/crypto/tests/key_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+    auto rsa_2048 = CreateRSA2048Key();
+    EXPECT_NE(rsa_2048, std::nullopt);
+    EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+    ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+    // The public key string format is expected to be: "<pub_key> <host_name>"
+    std::string pub_key_plus_name;
+    auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+    std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+    EXPECT_EQ(split.size(), 2);
+
+    LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+    // Try to sign something and decode it.
+    const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+    std::vector<uint8_t> sig(RSA_size(rsa));
+    unsigned sig_len;
+    EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+                       &sig_len, rsa),
+              1);
+    sig.resize(sig_len);
+
+    {
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+        RSA* key = nullptr;
+        ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+        EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+                             sig.data(), sig.size(), key),
+                  1);
+        RSA_free(key);
+    }
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/tests/rsa_2048_key_test.cpp b/crypto/tests/rsa_2048_key_test.cpp
new file mode 100644
index 0000000..1d8880e
--- /dev/null
+++ b/crypto/tests/rsa_2048_key_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+    auto rsa_2048 = CreateRSA2048Key();
+    EXPECT_NE(rsa_2048, std::nullopt);
+    EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+    ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+    // The public key string format is expected to be: "<pub_key> <host_name>"
+    std::string pub_key_plus_name;
+    auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+    std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+    EXPECT_EQ(split.size(), 2);
+
+    LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+    std::string pemString = Key::ToPEMString(rsa_2048->GetEvpPkey());
+    ASSERT_FALSE(pemString.empty());
+
+    // Try to sign something and decode it.
+    const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+    std::vector<uint8_t> sig(RSA_size(rsa));
+    unsigned sig_len;
+    EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+                       &sig_len, rsa),
+              1);
+    sig.resize(sig_len);
+
+    {
+        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+        const std::string& pubkey = split[0];
+        ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+        RSA* key = nullptr;
+        ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+        EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+                             sig.data(), sig.size(), key),
+                  1);
+        RSA_free(key);
+    }
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/tests/x509_generator_test.cpp b/crypto/tests/x509_generator_test.cpp
new file mode 100644
index 0000000..281776b
--- /dev/null
+++ b/crypto/tests/x509_generator_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(X509Generator, Smoke) {
+    auto rsa_2048 = CreateRSA2048Key();
+
+    std::string pub_key_plus_name;
+    auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+    ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+    std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+    EXPECT_EQ(split.size(), 2);
+
+    LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+    auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey());
+    ASSERT_NE(x509_cert.get(), nullptr);
+
+    std::string x509_str = X509ToPEMString(x509_cert.get());
+    ASSERT_FALSE(x509_str.empty());
+}
+
+}  // namespace crypto
+}  // namespace adb
diff --git a/crypto/x509_generator.cpp b/crypto/x509_generator.cpp
new file mode 100644
index 0000000..43b8153
--- /dev/null
+++ b/crypto/x509_generator.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/x509_generator.h"
+
+#include <vector>
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+
+const char kBasicConstraints[] = "critical,CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+bool add_ext(X509* cert, int nid, const char* value) {
+    size_t len = strlen(value) + 1;
+    std::vector<char> mutableValue(value, value + len);
+    X509V3_CTX context;
+
+    X509V3_set_ctx_nodb(&context);
+
+    X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+    X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+    if (!ex) {
+        return false;
+    }
+
+    X509_add_ext(cert, ex, -1);
+    X509_EXTENSION_free(ex);
+    return true;
+}
+
+}  // namespace
+
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey) {
+    CHECK(pkey);
+    bssl::UniquePtr<X509> x509(X509_new());
+    if (!x509) {
+        LOG(ERROR) << "Unable to allocate x509 container";
+        return nullptr;
+    }
+    X509_set_version(x509.get(), 2);
+
+    ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
+    X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+    X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+
+    if (!X509_set_pubkey(x509.get(), pkey)) {
+        LOG(ERROR) << "Unable to set x509 public key";
+        return nullptr;
+    }
+
+    X509_NAME* name = X509_get_subject_name(x509.get());
+    if (!name) {
+        LOG(ERROR) << "Unable to get x509 subject name";
+        return nullptr;
+    }
+    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("Adb"), -1, -1, 0);
+    if (!X509_set_issuer_name(x509.get(), name)) {
+        LOG(ERROR) << "Unable to set x509 issuer name";
+        return nullptr;
+    }
+
+    add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+    add_ext(x509.get(), NID_key_usage, kKeyUsage);
+    add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+
+    int bytes = X509_sign(x509.get(), pkey, EVP_sha256());
+    if (bytes <= 0) {
+        LOG(ERROR) << "Unable to sign x509 certificate";
+        return nullptr;
+    }
+
+    return x509;
+}
+
+std::string X509ToPEMString(X509* x509) {
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    int rc = PEM_write_bio_X509(bio.get(), x509);
+    if (rc != 1) {
+        LOG(ERROR) << "PEM_write_bio_X509 failed";
+        return "";
+    }
+
+    BUF_MEM* mem = nullptr;
+    BIO_get_mem_ptr(bio.get(), &mem);
+    if (!mem || !mem->data || !mem->length) {
+        LOG(ERROR) << "BIO_get_mem_ptr failed";
+        return "";
+    }
+
+    return std::string(mem->data, mem->length);
+}
+
+}  // namespace crypto
+}  // namespace adb