diff --git a/Documentation/guides/BuildProcess.md b/Documentation/guides/BuildProcess.md index 71fa38e57d7..2731a5b3fdf 100644 --- a/Documentation/guides/BuildProcess.md +++ b/Documentation/guides/BuildProcess.md @@ -1073,6 +1073,15 @@ server, the following MSBuild properties can be used: the value entered when `keytool` asks **Enter key password for $(AndroidSigningKeyAlias)**. + You can use the raw password here, however if you want to hide your password in logs + you can use a prefix of env: or file: to point it to an Environment variable or + a file. For example + + ``` + env: + file: + ``` + - **AndroidSigningKeyStore** – Specifies the filename of the keystore file created by `keytool`. This corresponds to the value provided to the **keytool -keystore** option. @@ -1082,6 +1091,15 @@ server, the following MSBuild properties can be used: `keytool` when creating the keystore file and asked **Enter keystore password:**. + You can use the raw password here, however if you want to hide your password in logs + you can use a prefix of env: or file: to point it to an Environment variable or + a file. For example + + ``` + env: + file: + ``` + For example, consider the following `keytool` invocation: ```shell @@ -1111,6 +1129,26 @@ To use the keystore generated above, use the property group: ``` +To use an environment variable to store your password you can do the following + +```xml + + env:SomeEnvironmentVariableWithThePassword + env:SomeEnvironmentVariableWithThePassword + +``` + +to use a file you can do the following + +To use an environment variable to store your password you can do the following + +```xml + + file:SomeFileWithThePassword + file:SomeFileWithThePassword + +``` + ## Build Actions diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs index 3ab1ed89368..37d6bc995ae 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs @@ -23,9 +23,27 @@ public class AndroidApkSigner : JavaToolTask [Required] public string KeyAlias { get; set; } + /// + /// The Password for the Key. + /// You can use the raw password here, however if you want to hide your password in logs + /// you can use a preview of env: or file: to point it to an Environment variable or + /// a file. + /// + /// env: + /// file: + /// [Required] public string KeyPass { get; set; } + /// + /// The Password for the Keystore. + /// You can use the raw password here, however if you want to hide your password in logs + /// you can use a preview of env: or file: to point it to an Environment variable or + /// a file. + /// + /// env: + /// file: + /// [Required] public string StorePass { get; set; } @@ -41,6 +59,18 @@ public override bool Execute () return base.Execute (); } + void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value) + { + if (value.StartsWith ("env:", StringComparison.Ordinal)) { + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", value); + } + else if (value.StartsWith ("file:", StringComparison.Ordinal)) { + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", value.Replace ("file:", string.Empty)); + } else { + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", value); + } + } + protected override string GenerateCommandLineCommands () { var cmd = new CommandLineBuilder (); @@ -59,9 +89,9 @@ protected override string GenerateCommandLineCommands () cmd.AppendSwitchIfNotNull ("-jar ", ApkSignerJar); cmd.AppendSwitch ("sign"); cmd.AppendSwitchIfNotNull ("--ks ", KeyStore); - cmd.AppendSwitchIfNotNull ("--ks-pass pass:", StorePass); + AddStorePass (cmd, "--ks-pass", StorePass); cmd.AppendSwitchIfNotNull ("--ks-key-alias ", KeyAlias); - cmd.AppendSwitchIfNotNull ("--key-pass pass:", KeyPass); + AddStorePass (cmd, "--key-pass", KeyPass); cmd.AppendSwitchIfNotNull ("--min-sdk-version ", minSdk.ToString ()); cmd.AppendSwitchIfNotNull ("--max-sdk-version ", maxSdk.ToString ()); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs index 5be12b7d4bf..90a91925f96 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs @@ -22,9 +22,27 @@ public class AndroidSignPackage : AndroidToolTask [Required] public string KeyAlias { get; set; } + /// + /// The Password for the Key. + /// You can use the raw password here, however if you want to hide your password in logs + /// you can use a preview of env: or file: to point it to an Environment variable or + /// a file. + /// + /// env: + /// file: + /// [Required] public string KeyPass { get; set; } + /// + /// The Password for the Keystore. + /// You can use the raw password here, however if you want to hide your password in logs + /// you can use a preview of env: or file: to point it to an Environment variable or + /// a file. + /// + /// env: + /// file: + /// [Required] public string StorePass { get; set; } @@ -48,6 +66,19 @@ public class AndroidSignPackage : AndroidToolTask protected override string DefaultErrorCode => "ANDJS0000"; + void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value) + { + string pass = value.Replace ("env:", string.Empty).Replace ("file:", string.Empty); + if (value.StartsWith ("env:", StringComparison.Ordinal)) { + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:env ", pass); + } + else if (value.StartsWith ("file:", StringComparison.Ordinal)) { + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:file ", pass); + } else { + cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", pass); + } + } + protected override string GenerateCommandLineCommands () { var fileName = Path.GetFileNameWithoutExtension (UnsignedApk); @@ -57,8 +88,8 @@ protected override string GenerateCommandLineCommands () cmd.AppendSwitchIfNotNull ("-tsa ", TimestampAuthorityUrl); cmd.AppendSwitchIfNotNull ("-tsacert ", TimestampAuthorityCertificateAlias); cmd.AppendSwitchIfNotNull ("-keystore ", KeyStore); - cmd.AppendSwitchIfNotNull ("-storepass ", StorePass); - cmd.AppendSwitchIfNotNull ("-keypass ", KeyPass); + AddStorePass (cmd, "-storepass", StorePass); + AddStorePass (cmd, "-keypass", KeyPass); cmd.AppendSwitchIfNotNull ("-digestalg ", DigestAlgorithm); cmd.AppendSwitchIfNotNull ("-sigalg ", SigningAlgorithm); cmd.AppendSwitchIfNotNull ("-signedjar ", Path.Combine (SignedApkDirectory, $"{fileName}{FileSuffix}{extension}" )); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index ec045b62ee6..5603ddd85a3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1670,20 +1670,27 @@ public void BuildLibraryWhichUsesResources ([Values (false, true)] bool isReleas #pragma warning disable 414 static object [] AndroidStoreKeyTests = new object [] { - // isRelease, AndroidKeyStore, ExpectedResult - new object[] { false , "False" , "debug.keystore"}, - new object[] { true , "False" , "debug.keystore"}, - new object[] { false , "True" , "-keystore test.keystore"}, - new object[] { true , "True" , "-keystore test.keystore"}, - new object[] { false , "" , "debug.keystore"}, - new object[] { true , "" , "debug.keystore"}, + // isRelease, AndroidKeyStore, password, ExpectedResult + new object[] { false , "False" , "android", "debug.keystore"}, + new object[] { true , "False" , "android", "debug.keystore"}, + new object[] { false , "True" , "android", "-keystore test.keystore"}, + new object[] { true , "True" , "android", "-keystore test.keystore"}, + new object[] { false , "" , "android", "debug.keystore"}, + new object[] { true , "" , "android", "debug.keystore"}, + new object[] { false , "True" , "env:android", "-keystore test.keystore"}, + new object[] { true , "True" , "env:android", "-keystore test.keystore"}, + new object[] { false , "True" , "file:android", "-keystore test.keystore"}, + new object[] { true , "True" , "file:android", "-keystore test.keystore"}, }; #pragma warning restore 414 [Test] [TestCaseSource (nameof (AndroidStoreKeyTests))] - public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string expected) + public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string password, string expected) { + string path = Path.Combine ("temp", TestName); + string storepassfile = Path.Combine (Root, path, "storepass.txt"); + string keypassfile = Path.Combine (Root, path, "keypass.txt"); byte [] data; using (var stream = typeof (XamarinAndroidCommonProject).Assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.test.keystore")) { data = new byte [stream.Length]; @@ -1692,17 +1699,35 @@ public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string var proj = new XamarinAndroidApplicationProject () { IsRelease = isRelease }; + Dictionary envVar = new Dictionary (); + if (password.StartsWith ("env:", StringComparison.Ordinal)) { + envVar.Add ("_MYPASSWORD", password.Replace ("env:", string.Empty)); + proj.SetProperty ("AndroidSigningStorePass", "env:_MYPASSWORD"); + proj.SetProperty ("AndroidSigningKeyPass", "env:_MYPASSWORD"); + } else if (password.StartsWith ("file:", StringComparison.Ordinal)) { + proj.SetProperty ("AndroidSigningStorePass", $"file:{storepassfile}"); + proj.SetProperty ("AndroidSigningKeyPass", $"file:{keypassfile}"); + } else { + proj.SetProperty ("AndroidSigningStorePass", password); + proj.SetProperty ("AndroidSigningKeyPass", password); + } proj.SetProperty ("AndroidKeyStore", androidKeyStore); proj.SetProperty ("AndroidSigningKeyStore", "test.keystore"); - proj.SetProperty ("AndroidSigningStorePass", "android"); proj.SetProperty ("AndroidSigningKeyAlias", "mykey"); - proj.SetProperty ("AndroidSigningKeyPass", "android"); proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "test.keystore") { BinaryContent = () => data }); - using (var b = CreateApkBuilder (Path.Combine ("temp", TestName), false, false)) { + proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "storepass.txt") { + TextContent = () => password.Replace ("file:", string.Empty), + Encoding = Encoding.ASCII, + }); + proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "keypass.txt") { + TextContent = () => password.Replace ("file:", string.Empty), + Encoding = Encoding.ASCII, + }); + using (var b = CreateApkBuilder (path, false, false)) { b.Verbosity = LoggerVerbosity.Diagnostic; - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + Assert.IsTrue (b.Build (proj, environmentVariables: envVar), "Build should have succeeded."); StringAssertEx.Contains (expected, b.LastBuildOutput, "The Wrong keystore was used to sign the apk"); }