Skip to content

Commit 04a4308

Browse files
dellis1972jonpryor
authored andcommitted
[Xamarin.Android.Build.Tasks] EnvVar & File support for Signing APKs (#3522)
Fixes: #3513 Both `jarsigner` and `apksigner` provide a way to use both files and environment variables for the store and key passwords. For `jarsigner` you have to suffix the parameter switch with either `:env` or `:file` to use those options. For `apksigner` you have to prefix the value with either `:env`, `:file` or `:pass`. We currently only support raw passwords. This commit adds support for using both `env:` and `file:` for signing. When providing values for the MSBuild properties such as `$(AndroidSigningStorePass)` and `$(AndroidSigningKeyPass)` all they need to do is prefix the value with `env:` or `file:` to use the alternative parameters. /p:AndroidSigningKeyPass=env:MyPasswordEnvVar /p:AndroidSigningKeyPass=file:PathToPasswordFile This will stop passwords appearing in build logs etc.
1 parent ed597fb commit 04a4308

File tree

4 files changed

+140
-16
lines changed

4 files changed

+140
-16
lines changed

Documentation/guides/BuildProcess.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,15 @@ server, the following MSBuild properties can be used:
10731073
the value entered when `keytool` asks **Enter key password for
10741074
$(AndroidSigningKeyAlias)**.
10751075
1076+
You can use the raw password here, however if you want to hide your password in logs
1077+
you can use a prefix of env: or file: to point it to an Environment variable or
1078+
a file. For example
1079+
1080+
```
1081+
env:<PasswordEnvironentVariable>
1082+
file:<PasswordFile>
1083+
```
1084+
10761085
- **AndroidSigningKeyStore** &ndash; Specifies the filename of the
10771086
keystore file created by `keytool`. This corresponds to the value
10781087
provided to the **keytool -keystore** option.
@@ -1082,6 +1091,15 @@ server, the following MSBuild properties can be used:
10821091
`keytool` when creating the keystore file and asked **Enter
10831092
keystore password:**.
10841093
1094+
You can use the raw password here, however if you want to hide your password in logs
1095+
you can use a prefix of env: or file: to point it to an Environment variable or
1096+
a file. For example
1097+
1098+
```
1099+
env:<PasswordEnvironentVariable>
1100+
file:<PasswordFile>
1101+
```
1102+
10851103
For example, consider the following `keytool` invocation:
10861104
10871105
```shell
@@ -1111,6 +1129,26 @@ To use the keystore generated above, use the property group:
11111129
</PropertyGroup>
11121130
```
11131131
1132+
To use an environment variable to store your password you can do the following
1133+
1134+
```xml
1135+
<PropertyGroup>
1136+
<AndroidSigningStorePass>env:SomeEnvironmentVariableWithThePassword</AndroidSigningStorePass>
1137+
<AndroidSigningKeyPass>env:SomeEnvironmentVariableWithThePassword</AndroidSigningKeyPass>
1138+
</PropertyGroup>
1139+
```
1140+
1141+
to use a file you can do the following
1142+
1143+
To use an environment variable to store your password you can do the following
1144+
1145+
```xml
1146+
<PropertyGroup>
1147+
<AndroidSigningStorePass>file:SomeFileWithThePassword</AndroidSigningStorePass>
1148+
<AndroidSigningKeyPass>file:SomeFileWithThePassword</AndroidSigningKeyPass>
1149+
</PropertyGroup>
1150+
```
1151+
11141152
<a name="Build_Actions" />
11151153
11161154
## Build Actions

src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,27 @@ public class AndroidApkSigner : JavaToolTask
2323
[Required]
2424
public string KeyAlias { get; set; }
2525

26+
/// <summary>
27+
/// The Password for the Key.
28+
/// You can use the raw password here, however if you want to hide your password in logs
29+
/// you can use a preview of env: or file: to point it to an Environment variable or
30+
/// a file.
31+
///
32+
/// env:<PasswordEnvironentVariable>
33+
/// file:<PasswordFile>
34+
/// </summary>
2635
[Required]
2736
public string KeyPass { get; set; }
2837

38+
/// <summary>
39+
/// The Password for the Keystore.
40+
/// You can use the raw password here, however if you want to hide your password in logs
41+
/// you can use a preview of env: or file: to point it to an Environment variable or
42+
/// a file.
43+
///
44+
/// env:<PasswordEnvironentVariable>
45+
/// file:<PasswordFile>
46+
/// </summary>
2947
[Required]
3048
public string StorePass { get; set; }
3149

@@ -41,6 +59,18 @@ public override bool Execute ()
4159
return base.Execute ();
4260
}
4361

62+
void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value)
63+
{
64+
if (value.StartsWith ("env:", StringComparison.Ordinal)) {
65+
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", value);
66+
}
67+
else if (value.StartsWith ("file:", StringComparison.Ordinal)) {
68+
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", value.Replace ("file:", string.Empty));
69+
} else {
70+
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", value);
71+
}
72+
}
73+
4474
protected override string GenerateCommandLineCommands ()
4575
{
4676
var cmd = new CommandLineBuilder ();
@@ -59,9 +89,9 @@ protected override string GenerateCommandLineCommands ()
5989
cmd.AppendSwitchIfNotNull ("-jar ", ApkSignerJar);
6090
cmd.AppendSwitch ("sign");
6191
cmd.AppendSwitchIfNotNull ("--ks ", KeyStore);
62-
cmd.AppendSwitchIfNotNull ("--ks-pass pass:", StorePass);
92+
AddStorePass (cmd, "--ks-pass", StorePass);
6393
cmd.AppendSwitchIfNotNull ("--ks-key-alias ", KeyAlias);
64-
cmd.AppendSwitchIfNotNull ("--key-pass pass:", KeyPass);
94+
AddStorePass (cmd, "--key-pass", KeyPass);
6595
cmd.AppendSwitchIfNotNull ("--min-sdk-version ", minSdk.ToString ());
6696
cmd.AppendSwitchIfNotNull ("--max-sdk-version ", maxSdk.ToString ());
6797

src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,27 @@ public class AndroidSignPackage : AndroidToolTask
2222
[Required]
2323
public string KeyAlias { get; set; }
2424

25+
/// <summary>
26+
/// The Password for the Key.
27+
/// You can use the raw password here, however if you want to hide your password in logs
28+
/// you can use a preview of env: or file: to point it to an Environment variable or
29+
/// a file.
30+
///
31+
/// env:<PasswordEnvironentVariable>
32+
/// file:<PasswordFile>
33+
/// </summary>
2534
[Required]
2635
public string KeyPass { get; set; }
2736

37+
/// <summary>
38+
/// The Password for the Keystore.
39+
/// You can use the raw password here, however if you want to hide your password in logs
40+
/// you can use a preview of env: or file: to point it to an Environment variable or
41+
/// a file.
42+
///
43+
/// env:<PasswordEnvironentVariable>
44+
/// file:<PasswordFile>
45+
/// </summary>
2846
[Required]
2947
public string StorePass { get; set; }
3048

@@ -48,6 +66,19 @@ public class AndroidSignPackage : AndroidToolTask
4866

4967
protected override string DefaultErrorCode => "ANDJS0000";
5068

69+
void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value)
70+
{
71+
string pass = value.Replace ("env:", string.Empty).Replace ("file:", string.Empty);
72+
if (value.StartsWith ("env:", StringComparison.Ordinal)) {
73+
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:env ", pass);
74+
}
75+
else if (value.StartsWith ("file:", StringComparison.Ordinal)) {
76+
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:file ", pass);
77+
} else {
78+
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", pass);
79+
}
80+
}
81+
5182
protected override string GenerateCommandLineCommands ()
5283
{
5384
var fileName = Path.GetFileNameWithoutExtension (UnsignedApk);
@@ -57,8 +88,8 @@ protected override string GenerateCommandLineCommands ()
5788
cmd.AppendSwitchIfNotNull ("-tsa ", TimestampAuthorityUrl);
5889
cmd.AppendSwitchIfNotNull ("-tsacert ", TimestampAuthorityCertificateAlias);
5990
cmd.AppendSwitchIfNotNull ("-keystore ", KeyStore);
60-
cmd.AppendSwitchIfNotNull ("-storepass ", StorePass);
61-
cmd.AppendSwitchIfNotNull ("-keypass ", KeyPass);
91+
AddStorePass (cmd, "-storepass", StorePass);
92+
AddStorePass (cmd, "-keypass", KeyPass);
6293
cmd.AppendSwitchIfNotNull ("-digestalg ", DigestAlgorithm);
6394
cmd.AppendSwitchIfNotNull ("-sigalg ", SigningAlgorithm);
6495
cmd.AppendSwitchIfNotNull ("-signedjar ", Path.Combine (SignedApkDirectory, $"{fileName}{FileSuffix}{extension}" ));

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,20 +1670,27 @@ public void BuildLibraryWhichUsesResources ([Values (false, true)] bool isReleas
16701670

16711671
#pragma warning disable 414
16721672
static object [] AndroidStoreKeyTests = new object [] {
1673-
// isRelease, AndroidKeyStore, ExpectedResult
1674-
new object[] { false , "False" , "debug.keystore"},
1675-
new object[] { true , "False" , "debug.keystore"},
1676-
new object[] { false , "True" , "-keystore test.keystore"},
1677-
new object[] { true , "True" , "-keystore test.keystore"},
1678-
new object[] { false , "" , "debug.keystore"},
1679-
new object[] { true , "" , "debug.keystore"},
1673+
// isRelease, AndroidKeyStore, password, ExpectedResult
1674+
new object[] { false , "False" , "android", "debug.keystore"},
1675+
new object[] { true , "False" , "android", "debug.keystore"},
1676+
new object[] { false , "True" , "android", "-keystore test.keystore"},
1677+
new object[] { true , "True" , "android", "-keystore test.keystore"},
1678+
new object[] { false , "" , "android", "debug.keystore"},
1679+
new object[] { true , "" , "android", "debug.keystore"},
1680+
new object[] { false , "True" , "env:android", "-keystore test.keystore"},
1681+
new object[] { true , "True" , "env:android", "-keystore test.keystore"},
1682+
new object[] { false , "True" , "file:android", "-keystore test.keystore"},
1683+
new object[] { true , "True" , "file:android", "-keystore test.keystore"},
16801684
};
16811685
#pragma warning restore 414
16821686

16831687
[Test]
16841688
[TestCaseSource (nameof (AndroidStoreKeyTests))]
1685-
public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string expected)
1689+
public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string password, string expected)
16861690
{
1691+
string path = Path.Combine ("temp", TestName);
1692+
string storepassfile = Path.Combine (Root, path, "storepass.txt");
1693+
string keypassfile = Path.Combine (Root, path, "keypass.txt");
16871694
byte [] data;
16881695
using (var stream = typeof (XamarinAndroidCommonProject).Assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.test.keystore")) {
16891696
data = new byte [stream.Length];
@@ -1692,17 +1699,35 @@ public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string
16921699
var proj = new XamarinAndroidApplicationProject () {
16931700
IsRelease = isRelease
16941701
};
1702+
Dictionary<string, string> envVar = new Dictionary<string, string> ();
1703+
if (password.StartsWith ("env:", StringComparison.Ordinal)) {
1704+
envVar.Add ("_MYPASSWORD", password.Replace ("env:", string.Empty));
1705+
proj.SetProperty ("AndroidSigningStorePass", "env:_MYPASSWORD");
1706+
proj.SetProperty ("AndroidSigningKeyPass", "env:_MYPASSWORD");
1707+
} else if (password.StartsWith ("file:", StringComparison.Ordinal)) {
1708+
proj.SetProperty ("AndroidSigningStorePass", $"file:{storepassfile}");
1709+
proj.SetProperty ("AndroidSigningKeyPass", $"file:{keypassfile}");
1710+
} else {
1711+
proj.SetProperty ("AndroidSigningStorePass", password);
1712+
proj.SetProperty ("AndroidSigningKeyPass", password);
1713+
}
16951714
proj.SetProperty ("AndroidKeyStore", androidKeyStore);
16961715
proj.SetProperty ("AndroidSigningKeyStore", "test.keystore");
1697-
proj.SetProperty ("AndroidSigningStorePass", "android");
16981716
proj.SetProperty ("AndroidSigningKeyAlias", "mykey");
1699-
proj.SetProperty ("AndroidSigningKeyPass", "android");
17001717
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "test.keystore") {
17011718
BinaryContent = () => data
17021719
});
1703-
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName), false, false)) {
1720+
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "storepass.txt") {
1721+
TextContent = () => password.Replace ("file:", string.Empty),
1722+
Encoding = Encoding.ASCII,
1723+
});
1724+
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "keypass.txt") {
1725+
TextContent = () => password.Replace ("file:", string.Empty),
1726+
Encoding = Encoding.ASCII,
1727+
});
1728+
using (var b = CreateApkBuilder (path, false, false)) {
17041729
b.Verbosity = LoggerVerbosity.Diagnostic;
1705-
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
1730+
Assert.IsTrue (b.Build (proj, environmentVariables: envVar), "Build should have succeeded.");
17061731
StringAssertEx.Contains (expected, b.LastBuildOutput,
17071732
"The Wrong keystore was used to sign the apk");
17081733
}

0 commit comments

Comments
 (0)