public void CreateDebugKeyStoreWithStrongPassword() { string keyfile = Path.Combine(TestName, "debug.keystore"); string pass = "******"; if (File.Exists(keyfile)) { File.Delete(keyfile); } var task = new AndroidCreateDebugKey { BuildEngine = engine, KeyStore = keyfile, StorePass = pass, KeyAlias = "teststringkey", KeyPass = pass, KeyAlgorithm = "RSA", Validity = 10000, StoreType = "pkcs12", Command = "-genkeypair", ToolPath = keyToolPath, }; Assert.IsTrue(task.Execute(), "Task should have succeeded."); Assert.AreEqual(0, errors.Count, "Task should have no errors."); Assert.AreEqual(0, warnings.Count, "Task should have no warnings."); Assert.AreEqual(0, task.ExitCode, "ExitCode should have been 0"); messages.Clear(); var keyToolTask = new KeyTool { BuildEngine = engine, KeyStore = keyfile, StorePass = pass, KeyAlias = "teststringkey", KeyPass = pass, Command = "-list", ToolPath = keyToolPath, }; Assert.IsTrue(keyToolTask.Execute(), "Task should have succeeded."); Assert.AreEqual(0, errors.Count, "Task should have no errors."); Assert.AreEqual(0, warnings.Count, "Task should have no warnings."); Assert.AreEqual(0, task.ExitCode, "ExitCode should have been 0"); string output = string.Join(" ", messages.Select(x => x.Message)); Assert.IsTrue(output.Contains("Certificate fingerprint (SHA"), "Certificate SHA1 or SHA-256 should have been printed."); }
public void CreateDebugKeyStore() { string keyfile = Path.Combine(TestName, "debug.keystore"); if (File.Exists(keyfile)) { File.Delete(keyfile); } var task = new AndroidCreateDebugKey { BuildEngine = engine, KeyStore = keyfile, StorePass = "******", KeyAlias = "androiddebugkey", KeyPass = "******", KeyAlgorithm = "RSA", Validity = 10000, StoreType = "pkcs12", Command = "-genkeypair", ToolPath = keyToolPath, }; Assert.IsTrue(task.Execute(), "Task should have succeeded."); Assert.AreEqual(0, errors.Count, "Task should have no errors."); Assert.AreEqual(0, warnings.Count, "Task should have no warnings."); var keyToolTask = new KeyTool { BuildEngine = engine, KeyStore = keyfile, StorePass = "******", KeyAlias = "androiddebugkey", KeyPass = "******", Command = "-list", ToolPath = keyToolPath, }; Assert.IsTrue(keyToolTask.Execute(), "Task should have succeeded."); }
public void CheckSignApk([Values(true, false)] bool useApkSigner, [Values(true, false)] bool perAbiApk) { string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".bat" : ""; var foundApkSigner = Directory.EnumerateDirectories(Path.Combine(AndroidSdkPath, "build-tools")).Any(dir => Directory.EnumerateFiles(dir, "apksigner" + ext).Any()); if (useApkSigner && !foundApkSigner) { Assert.Ignore("Skipping test. Required build-tools verison which contains apksigner is not installed."); } string keyfile = Path.Combine(Root, "temp", TestName, "release.keystore"); if (File.Exists(keyfile)) { File.Delete(keyfile); } var androidSdk = new AndroidSdkInfo((level, message) => { }, AndroidSdkPath, AndroidNdkPath); string keyToolPath = Path.Combine(androidSdk.JavaSdkPath, "bin"); var engine = new MockBuildEngine(Console.Out); string pass = "******"; var task = new AndroidCreateDebugKey { BuildEngine = engine, KeyStore = keyfile, StorePass = pass, KeyAlias = "releasestore", KeyPass = pass, KeyAlgorithm = "RSA", Validity = 30, StoreType = "pkcs12", Command = "-genkeypair", ToolPath = keyToolPath, }; Assert.IsTrue(task.Execute(), "Task should have succeeded."); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; if (useApkSigner) { proj.SetProperty("AndroidUseApkSigner", "true"); } else { proj.RemoveProperty("AndroidUseApkSigner"); } proj.SetProperty(proj.ReleaseProperties, "AndroidKeyStore", "True"); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyStore", keyfile); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyAlias", "releasestore"); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyPass", pass); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningStorePass", pass); proj.SetProperty(proj.ReleaseProperties, KnownProperties.AndroidCreatePackagePerAbi, perAbiApk); proj.SetAndroidSupportedAbis("armeabi-v7a", "x86"); using (var b = CreateApkBuilder(Path.Combine("temp", TestContext.CurrentContext.Test.Name))) { var bin = Path.Combine(Root, b.ProjectDirectory, proj.OutputPath); Assert.IsTrue(b.Build(proj), "First build failed"); Assert.IsTrue(StringAssertEx.ContainsText(b.LastBuildOutput, " 0 Warning(s)"), "First build should not contain warnings! Contains\n" + string.Join("\n", b.LastBuildOutput.Where(line => line.Contains("warning")))); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } var item = proj.AndroidResources.First(x => x.Include() == "Resources\\values\\Strings.xml"); item.TextContent = () => proj.StringsXml.Replace("${PROJECT_NAME}", "Foo"); item.Timestamp = null; Assert.IsTrue(b.Build(proj), "Second build failed"); Assert.IsTrue(StringAssertEx.ContainsText(b.LastBuildOutput, " 0 Warning(s)"), "Second build should not contain warnings! Contains\n" + string.Join("\n", b.LastBuildOutput.Where(line => line.Contains("warning")))); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } } }
public void CheckSignApk([Values(true, false)] bool useApkSigner, [Values(true, false)] bool perAbiApk) { string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".bat" : ""; var foundApkSigner = Directory.EnumerateDirectories(Path.Combine(AndroidSdkPath, "build-tools")).Any(dir => Directory.EnumerateFiles(dir, "apksigner" + ext).Any()); if (useApkSigner && !foundApkSigner) { Assert.Ignore("Skipping test. Required build-tools verison which contains apksigner is not installed."); } string keyfile = Path.Combine(Root, "temp", TestName, "release.keystore"); if (File.Exists(keyfile)) { File.Delete(keyfile); } string keyToolPath = Path.Combine(AndroidSdkResolver.GetJavaSdkPath(), "bin"); var engine = new MockBuildEngine(Console.Out); string pass = "******"; string alias = "release store"; var task = new AndroidCreateDebugKey { BuildEngine = engine, KeyStore = keyfile, StorePass = pass, KeyAlias = alias, KeyPass = pass, KeyAlgorithm = "RSA", Validity = 30, StoreType = "pkcs12", Command = "-genkeypair", ToolPath = keyToolPath, }; Assert.IsTrue(task.Execute(), "Task should have succeeded."); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; proj.SetProperty(proj.ReleaseProperties, "AndroidUseApkSigner", useApkSigner); proj.SetProperty(proj.ReleaseProperties, "AndroidKeyStore", "True"); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyStore", keyfile); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyAlias", alias); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyPass", Uri.EscapeDataString(pass)); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningStorePass", Uri.EscapeDataString(pass)); proj.SetProperty(proj.ReleaseProperties, KnownProperties.AndroidCreatePackagePerAbi, perAbiApk); if (perAbiApk) { proj.SetAndroidSupportedAbis("armeabi-v7a", "x86", "arm64-v8a", "x86_64"); } else { proj.SetAndroidSupportedAbis("armeabi-v7a", "x86"); } using (var b = CreateApkBuilder(Path.Combine("temp", TestContext.CurrentContext.Test.Name))) { var bin = Path.Combine(Root, b.ProjectDirectory, proj.OutputPath); Assert.IsTrue(b.Build(proj), "First build failed"); b.AssertHasNoWarnings(); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } // Make sure the APKs have unique version codes if (perAbiApk) { int armManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "armeabi-v7a", "AndroidManifest.xml")); int x86ManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "x86", "AndroidManifest.xml")); int arm64ManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "arm64-v8a", "AndroidManifest.xml")); int x86_64ManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "x86_64", "AndroidManifest.xml")); var versionList = new List <int> { armManifestCode, x86ManifestCode, arm64ManifestCode, x86_64ManifestCode }; Assert.True(versionList.Distinct().Count() == versionList.Count, $"APK version codes were not unique - armeabi-v7a: {armManifestCode}, x86: {x86ManifestCode}, arm64-v8a: {arm64ManifestCode}, x86_64: {x86_64ManifestCode}"); } var item = proj.AndroidResources.First(x => x.Include() == "Resources\\values\\Strings.xml"); item.TextContent = () => proj.StringsXml.Replace("${PROJECT_NAME}", "Foo"); item.Timestamp = null; Assert.IsTrue(b.Build(proj), "Second build failed"); b.AssertHasNoWarnings(); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } } int GetVersionCodeFromIntermediateManifest(string manifestFilePath) { var doc = XDocument.Load(manifestFilePath); var versionCode = doc.Descendants() .Where(e => e.Name == "manifest") .Select(m => m.Attribute("{http://schemas.android.com/apk/res/android}versionCode")).FirstOrDefault(); if (!int.TryParse(versionCode?.Value, out int parsedCode)) { Assert.Fail($"Unable to parse 'versionCode' value from manifest content: {File.ReadAllText (manifestFilePath)}."); } return(parsedCode); } }