diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj
index 7980d6ce1f..33c7e641fe 100644
--- a/src/chocolatey/chocolatey.csproj
+++ b/src/chocolatey/chocolatey.csproj
@@ -133,6 +133,7 @@
+
diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs
index b1a3279e09..c0eb902e64 100644
--- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs
+++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs
@@ -173,6 +173,8 @@ private static void set_machine_sources(ChocolateyConfiguration config, ConfigFi
Name = source.Id,
Username = source.UserName,
EncryptedPassword = source.Password,
+ Certificate = source.Certificate,
+ EncryptedCertificatePassword = source.CertificatePassword,
Priority = source.Priority
});
}
diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs
index 91c82941db..537425103b 100644
--- a/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs
+++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs
@@ -53,6 +53,12 @@ public override void configure_argument_parser(OptionSet optionSet, ChocolateyCo
"p=|password=",
"Password - the user's password to the source. Defaults to empty.",
option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ .Add("cert=",
+ "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.Certificate = option.remove_surrounding_quotes())
+ .Add("cp=|certpassword=",
+ "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.CertificatePassword = option.remove_surrounding_quotes())
;
}
diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs
index 96a5b5b5c7..b631be5bb2 100644
--- a/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs
+++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInstallCommand.cs
@@ -83,6 +83,12 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon
.Add("p=|password=",
"Password - the user's password to the source. Defaults to empty.",
option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ .Add("cert=",
+ "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.Certificate = option.remove_surrounding_quotes())
+ .Add("cp=|certpassword=",
+ "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.CertificatePassword = option.remove_surrounding_quotes())
.Add("ignorechecksums|ignore-checksums",
"IgnoreChecksums - Ignore checksums provided by the package",
option =>
diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs
index 2ee9547b66..4f11b84fa1 100644
--- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs
+++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs
@@ -62,6 +62,12 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon
.Add("p=|password=",
"Password - the user's password to the source. Defaults to empty.",
option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ .Add("cert=",
+ "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.Certificate = option.remove_surrounding_quotes())
+ .Add("cp=|certpassword=",
+ "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.CertificatePassword = option.remove_surrounding_quotes())
.Add("page=",
"Page - the 'page' of results to return. Defaults to return all results.", option =>
{
diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs
index 67eb6271b0..378f529d30 100644
--- a/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs
+++ b/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs
@@ -45,6 +45,12 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon
.Add("p=|password=",
"Password - the user's password to the source. Defaults to empty.",
option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ .Add("cert=",
+ "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.Certificate = option.remove_surrounding_quotes())
+ .Add("cp=|certpassword=",
+ "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.CertificatePassword = option.remove_surrounding_quotes())
;
}
diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs
index 152224eaaa..184af8d731 100644
--- a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs
+++ b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs
@@ -53,7 +53,13 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon
option => configuration.SourceCommand.Username = option.remove_surrounding_quotes())
.Add("p=|password=",
"Password - the user's password to the source. Encrypted in chocolatey.config file.",
- option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ .Add("cert=",
+ "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.Certificate = option.remove_surrounding_quotes())
+ .Add("cp=|certpassword=",
+ "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.CertificatePassword = option.remove_surrounding_quotes())
.Add("priority=",
"Priority - The priority order of this source as compared to other sources, lower is better. Defaults to 0 (no priority). All priorities above 0 will be evaluated first, then zero-based values will be evaluated in config file order.",
option => configuration.SourceCommand.Priority = int.Parse(option.remove_surrounding_quotes()))
@@ -107,6 +113,7 @@ choco sources [list]|add|remove|disable|enable []
choco source
choco source list
choco source add -n=bob -s""https://somewhere/out/there/api/v2/""
+ choco source add -n=bob -s""https://somewhere/out/there/api/v2/"" -cert=\Users\bob\bob.pfx
choco source add -n=bob -s""https://somewhere/out/there/api/v2/"" -u=bob -p=12345
choco source disable -n=bob
choco source enable -n=bob
diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs
index e08651f867..29a32daa97 100644
--- a/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs
+++ b/src/chocolatey/infrastructure.app/commands/ChocolateyUpgradeCommand.cs
@@ -87,6 +87,12 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon
.Add("p=|password=",
"Password - the user's password to the source. Defaults to empty.",
option => configuration.SourceCommand.Password = option.remove_surrounding_quotes())
+ .Add("cert=",
+ "Client certificate - PFX pathname for an x509 authenticated feeds. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.Certificate = option.remove_surrounding_quotes())
+ .Add("cp=|certpassword=",
+ "Certificate Password - the client certificate's password to the source. Defaults to empty. Available in 0.9.10+.",
+ option => configuration.SourceCommand.CertificatePassword = option.remove_surrounding_quotes())
.Add("ignorechecksums|ignore-checksums",
"IgnoreChecksums - Ignore checksums provided by the package",
option =>
diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs
index a56f0ca0ed..9c5b360823 100644
--- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs
+++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs
@@ -402,6 +402,8 @@ public sealed class SourcesCommandConfiguration
public string Username { get; set; }
public string Password { get; set; }
public int Priority { get; set; }
+ public string Certificate { get; set; }
+ public string CertificatePassword { get; set; }
}
[Serializable]
@@ -412,6 +414,8 @@ public sealed class MachineSourceConfiguration
public string Username { get; set; }
public string EncryptedPassword { get; set; }
public int Priority { get; set; }
+ public string Certificate { get; set; }
+ public string EncryptedCertificatePassword { get; set; }
}
[Serializable]
diff --git a/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs b/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs
index b3d0d4b856..d1ae12b44f 100644
--- a/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs
+++ b/src/chocolatey/infrastructure.app/configuration/ConfigFileSourceSetting.cs
@@ -42,5 +42,11 @@ public sealed class ConfigFileSourceSetting
[XmlAttribute(AttributeName = "priority")]
public int Priority { get; set; }
+
+ [XmlAttribute(AttributeName = "certificate")]
+ public string Certificate { get; set; }
+
+ [XmlAttribute(AttributeName = "certificatePassword")]
+ public string CertificatePassword { get; set; }
}
}
\ No newline at end of file
diff --git a/src/chocolatey/infrastructure.app/nuget/ChocolateyClientCertificateProvider.cs b/src/chocolatey/infrastructure.app/nuget/ChocolateyClientCertificateProvider.cs
new file mode 100644
index 0000000000..735feca51d
--- /dev/null
+++ b/src/chocolatey/infrastructure.app/nuget/ChocolateyClientCertificateProvider.cs
@@ -0,0 +1,71 @@
+// Copyright © 2011 - Present RealDimensions Software, LLC
+//
+// 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.
+
+namespace chocolatey.infrastructure.app.nuget
+{
+ using configuration;
+ using NuGet;
+ using System;
+ using System.Linq;
+ using System.Net;
+ using System.Security.Cryptography.X509Certificates;
+
+ class ChocolateyClientCertificateProvider : IClientCertificateProvider
+ {
+ ChocolateyConfiguration _configuration;
+
+ public ChocolateyClientCertificateProvider(ChocolateyConfiguration configuration)
+ {
+ if (configuration == null)
+ throw new ArgumentNullException("configuration");
+ _configuration = configuration;
+ }
+
+ public X509Certificate GetCertificate(Uri uri)
+ {
+ if (uri.OriginalString.StartsWith(_configuration.Sources.TrimEnd('/').ToLower(),StringComparison.InvariantCultureIgnoreCase))
+ {
+ if (!string.IsNullOrWhiteSpace(_configuration.SourceCommand.Certificate))
+ {
+ this.Log().Debug("Using passed in certificate");
+
+ return new X509Certificate2(_configuration.SourceCommand.Certificate, _configuration.SourceCommand.CertificatePassword);
+ }
+ }
+
+ return _configuration.MachineSources.Where(s =>
+ {
+ var sourceUri = s.Key.TrimEnd('/').ToLower();
+ return uri.OriginalString.ToLower().StartsWith(sourceUri)
+ && !string.IsNullOrWhiteSpace(s.Certificate);
+ })
+ .Select(s =>
+ {
+ this.Log().Debug("Using machine source certificate");
+ try {
+ var decrypted = string.IsNullOrEmpty(s.EncryptedCertificatePassword)
+ ? string.Empty
+ : NugetEncryptionUtility.DecryptString(s.EncryptedCertificatePassword);
+ return new X509Certificate2(s.Certificate, decrypted);
+ } catch(Exception x)
+ {
+ this.Log().Error("Unable to load the certificate: {0}", x);
+ return null;
+ }
+ })
+ .FirstOrDefault();
+ }
+ }
+}
diff --git a/src/chocolatey/infrastructure.app/nuget/NugetCommon.cs b/src/chocolatey/infrastructure.app/nuget/NugetCommon.cs
index 8318b79f67..e4e7c3073b 100644
--- a/src/chocolatey/infrastructure.app/nuget/NugetCommon.cs
+++ b/src/chocolatey/infrastructure.app/nuget/NugetCommon.cs
@@ -61,6 +61,7 @@ public static IPackageRepository GetRemoteRepository(ChocolateyConfiguration con
// ensure credentials can be grabbed from configuration
HttpClient.DefaultCredentialProvider = new ChocolateyNugetCredentialProvider(configuration);
+ HttpClient.DefaultCertificateProvider = new ChocolateyClientCertificateProvider(configuration);
if (!string.IsNullOrWhiteSpace(configuration.Proxy.Location))
{
"chocolatey".Log().Debug("Using proxy server '{0}'.".format_with(configuration.Proxy.Location));
diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs
index 8df31be235..fd41e19707 100644
--- a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs
+++ b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs
@@ -56,14 +56,14 @@ public IEnumerable source_list(ChocolateyConfiguration configu
source.Id,
source.Disabled ? " [Disabled]" : string.Empty,
source.Value,
- string.IsNullOrWhiteSpace(source.UserName) ? string.Empty : "(Authenticated)",
+ (string.IsNullOrWhiteSpace(source.UserName) && string.IsNullOrWhiteSpace(source.Certificate)) ? string.Empty : "(Authenticated)",
source.Priority));
}
list.Add(new ChocolateySource {
Id = source.Id,
Value = source.Value,
Disabled = source.Disabled,
- Authenticated = string.IsNullOrWhiteSpace(source.Password),
+ Authenticated = !(string.IsNullOrWhiteSpace(source.UserName) && string.IsNullOrWhiteSpace(source.Certificate)),
Priority = source.Priority
});
}
@@ -81,6 +81,8 @@ public void source_add(ChocolateyConfiguration configuration)
Value = configuration.Sources,
UserName = configuration.SourceCommand.Username,
Password = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.Password),
+ Certificate = configuration.SourceCommand.Certificate,
+ CertificatePassword = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.CertificatePassword),
Priority = configuration.SourceCommand.Priority
};
configFileSettings.Sources.Add(source);
@@ -91,10 +93,13 @@ public void source_add(ChocolateyConfiguration configuration)
else
{
var currentPassword = string.IsNullOrWhiteSpace(source.Password) ? null : NugetEncryptionUtility.DecryptString(source.Password);
+ var currentCertificatePassword = string.IsNullOrWhiteSpace(source.CertificatePassword) ? null : NugetEncryptionUtility.DecryptString(source.CertificatePassword);
if (configuration.Sources.is_equal_to(source.Value) &&
configuration.SourceCommand.Priority == source.Priority &&
configuration.SourceCommand.Username.is_equal_to(source.UserName) &&
- configuration.SourceCommand.Password.is_equal_to(currentPassword)
+ configuration.SourceCommand.Password.is_equal_to(currentPassword) &&
+ configuration.SourceCommand.CertificatePassword.is_equal_to(currentCertificatePassword) &&
+ configuration.SourceCommand.Certificate.is_equal_to(source.Certificate)
)
{
if (!configuration.QuietOutput) this.Log().Warn(NO_CHANGE_MESSAGE);
@@ -105,6 +110,8 @@ public void source_add(ChocolateyConfiguration configuration)
source.Priority = configuration.SourceCommand.Priority;
source.UserName = configuration.SourceCommand.Username;
source.Password = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.Password);
+ source.CertificatePassword = NugetEncryptionUtility.EncryptString(configuration.SourceCommand.CertificatePassword);
+ source.Certificate = configuration.SourceCommand.Certificate;
_xmlService.serialize(configFileSettings, ApplicationParameters.GlobalConfigFileLocation);
if (!configuration.QuietOutput) this.Log().Warn(() => "Updated {0} - {1} (Priority {2})".format_with(source.Id, source.Value, source.Priority));