certificateValidationMode seems to be ignored by Azure - azure

I'm developing a solution (web app) for windows azure that uses WSFederation for authentication. Since im on azure testing phase (local tests were successful) i've decided to use the same self signed certificate i've been using for local testing.
The problem here is that i'm getting an error saying that "The X.509 certificate CN=mytestsite.com.br is not in the trusted people store. The X.509 certificate CN=mytestsite.com.br chain building failed. The certificate that was used has a trust chain that cannot be verified".
This error makes complete sense because it's a self-signed certificate, but since im on a staging enviroment (and I would absolutally hate to ask my sponsor for extra-budget for a valid certificate right now...) I would like to use the self-signed one anyway. So I changed the certificateValidationMode to "None", but I still get the same validation error... It seems that the validation mode is being ignored!!!
Does anyone knows what can I do to make things work? (buy a valid certificate would be my last shot because they're quite expensive for my budget at this point...)

I would just use the self-signed cert, use code to "deploy" it to your Trusted People store, and all should be well. This will be closer to real production use anyways (and if you use PeerTrust in production, you'll have to do the same thing there as well, even with a real certificate).
private static void CopyServerCertIntoPeopleStore()
{
var myStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
myStore.Open(OpenFlags.ReadOnly);
var peopleStore = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser);
var cert = myStore.Certificates.Find(
X509FindType.FindByThumbprint,
SettingFetcher.GetSetting(SettingFetcher.SettingType.ApplicationVariable, "WcfServiceCertificateThumbprint"),
true
).OfType<X509Certificate2>().First();
peopleStore.Open(OpenFlags.ReadWrite);
peopleStore.Add(cert);
}
Use something like this, just replace the SettingFetcher with RoleEnvironment.GetConfigurationSettingValue or whatever to grab the thumbprint.

I managed to make it by adding "certificateValidationMode" to the STS web.config.

Related

How to get a TokenCredential from a ServiceClientCredential object?

In my application, we presently are using ServiceClientCredentials from Microsoft.Rest. We are migrating parts of our application over to start using Azure.ResourceManager's ArmClient.
Basically all of our previous application integrations into Azure were using Microsoft.Azure.ResourceManager, which exposed agents like BlobClient or SecretClient, and these all accepted ServiceClientCredentials as a valid token type.
Now, with ArmClient I need to authenticate using DefaultAzureCredential which derives from Azure.Core's TokenCredential.
Surprisingly I haven't been able to find any examples yet of how to create this TokenCredential.
DefaultAzureCredential just works on my local PC since I'm signed into Visual Studio, but not on my build pipeline where I use Certificate based auth exposed as a ServiceClientCredential.
This was easier than I thought. The fix ended up being adding a new ServiceCollection extension method and passing in IWebHostEnvironment.
I use that to determine whether running in local debug, in which case we can use DefaultAzureCredential, or whether running in prod mode, in which case we should use Certificate Based auth.
It looks somewhat like this and works like a charm.
public static IServiceCollection AddDefaultAzureToken (this IServiceCollection services, IWebHostEnvironment environment)
{
if (environment.IsDevelopment())
{
var l = new DefaultAzureCredential();
services.AddSingleton<TokenCredential, l>;
}
else
{
var certCredential= new ClientCertificateCredential(null, null, "Abc");
services.AddSingleton<TokenCredential, certCredential>;
}
return services;
}
This works since DefaultAzureCredential and ClientCertficateCredential all have a common ancestor of TokenCredential, and the L in SOLID, the Liskov Substitution principle tells us that any implementation of a class can be substituted for any other instance of that class without breaking the application.
Note: the above sample was pseudocode and may need slight changing to work in your environment and should be cleaned to match your teams coding standards.

UWP on RPi - Trying to Provision to Azure - How to get X.509 certificate

I'm working with a RPi/UWP program (works well) that is signed with out X.509 certificate.
I have no problems connecting to Azure IoT hub if I manually provision the connection, but as we intend to ship these by the 100s, if not thousands, obviously that won't work.
I've looked at the code examples of using the provisioning service, but all of them are in the emulation.
Looking at the code, you have to load your X.509 certificate
certificateCollection.Import(s_certificateFileName, certificatePassword, X509KeyStorageFlags.UserKeySet);
This is where I get lost - Where on the RPi IS the certificate to load?
Basically, I THINK I understand how to do this in a plain Win10 app, but when you get to UWP on the RPi (a 3B, as the 3B+ is not supported - probably have to switch to Core) . Anyone have some sample code, or can point me in the right direction?
This document provides an overview of the cryptography features available to UWP apps.
You can generate the certificate file and then copy or import it to the device. If you copy the certificate file to device, you need to add capabilities for accessing the file to your UWP app. Please refer to this topic in MSDN forum. Following code can be used to import the pfx certificate into your device and then load it from cert store.
Import:
StorageFolder certificatesFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Certificates");
StorageFile certificateFile = await certificatesFolder.GetFileAsync("ClientCertificate .pfx ");
IBuffer certificateBuffer = await FileIO.ReadBufferAsync(certificateFile);
string encodedCertificate = Windows.Security.Cryptography.CryptographicBuffer.EncodeToBase64String(certificateBuffer);
await CertificateEnrollmentManager.ImportPfxDataAsync(encodedCertificate, "password", ExportOption.NotExportable, KeyProtectionLevel.NoConsent, InstallOptions.None, "certificateOne");
Load:
CertificateQuery certQuery = new CertificateQuery();
certQuery.FriendlyName = "certificateOne";
IReadOnlyList<Certificate> certificates = await CertificateStores.FindAllAsync(certQuery);

TLS and Client Auth in Spring Integration

I'm experimenting with TLS, mutual TLS, and host verification in my app and am having some confusion with the behavior of the Integration components. I'm using a TcpNetClientConnection Factory and a TcpNetServerConnectionFactory. I don't have a solid understanding of how these components are interacting under the hood, and want to make sure my authentication is working (or not working) as expected.
#Bean
public TcpNetClientConnectionFactory tcpClientConnectionFactory() {
TcpNetClientConnectionFactory factory =
new TcpNetClientConnectionFactory(upstreamHost, upstreamPort);
factory.setTcpSocketFactorySupport(tcpSslSocketFactorySupport);
factory.setTcpSocketSupport(new DefaultTcpSocketSupport(isHostVerificationEnabled));
return factory;
}
#Bean
TcpNetServerConnectionFactory testTcpServerConnectionFactory() {
TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(upstreamPort);
factory.setTcpSocketFactorySupport(tcpSslSocketFactorySupport);
factory.setTcpSocketSupport(new DefaultTcpSocketSupport(isHostVerificationEnabled));
return factory;
}
#Bean
public DefaultTcpNetSSLSocketFactorySupport tcpSslSocketFactorySupport() {
TcpSSLContextSupport sslContextSupport = new DefaultTcpSSLContextSupport(keyStore, trustStore, keyStorePassword,
trustStorePassword);
return new DefaultTcpNetSSLSocketFactorySupport(sslContextSupport);
}
It seems like the only way to create a TcpSSLContextSupport is to pass it both a keystore and a truststore. Why is the keystore necessary? If my server doesn't require client auth, I shouldn't have to pass a keystore to my client connection factory. The client factory should only have to have a truststore, to verify the server it's talking to.
In regard to client auth, does the server connection factory in my configuration require client auth? According to the reference, in order to enable client auth, I would need to override the postProcessServerSocket() method. However, I was getting SSL read exceptions until I added the client's cert to my server's truststore. Why was that necessary, since I haven't enabled client auth yet?
Finally, if the client connection factory does have a private key entry in its keystore, does it automatically utilize that key when opening a connection to the server? Or only if the server mandates client auth? It seems like my client connection factory is using its private key, even though I haven't set anything to mandate client auth yet.
The server doesn't require clientAuth by default; there's an example in the documentation about how to require it.
See this test case.
If I comment out the code at line 437
server.setTcpSocketSupport(new DefaultTcpSocketSupport(false) {
#Override
public void postProcessServerSocket(ServerSocket serverSocket) {
// ((SSLServerSocket) serverSocket).setNeedClientAuth(true);
}
});
The test fails with
java.lang.AssertionError:
Expecting code to raise a throwable.
at org.springframework.integration.ip.tcp.connection.SocketSupportTests.testNetClientAndServerSSLDifferentContexts(SocketSupportTests.java:414)
assertThatExceptionOfType(MessagingException.class)
.isThrownBy(() -> testNetClientAndServerSSLDifferentContexts(true));
Yes, the default implementation requires a keystore but it can be one that is empty or doesn't contain a client key when client auth is not needed. We should probably relax that; feel free to open a GitHub issue to request it. And/Or you can simply provide your own implementation.
In the test case (that expects to fail) we use a keyStore that doesn't contain the client cert.
TcpSSLContextSupport clientSslContextSupport = new DefaultTcpSSLContextSupport(
badClient ? "server.ks" : "client.ks",
"client.truststore.ks", "secret", "secret");
Note that the server socket also has setWantsClientAuth - maybe that defaults to true which would explain what you observed (failure if you provide a cert on the client side). I haven't tested that; try setting it to false.

How can I sign a JWT token on an Azure WebJob without getting a CryptographicException?

I have a WebJob that needs to create a JWT token to talk with an external service. The following code works when I run the WebJob on my local machine:
public static string SignES256(byte[] p8Certificate, object header, object payload)
{
var headerString = JsonConvert.SerializeObject(header);
var payloadString = JsonConvert.SerializeObject(payload);
CngKey key = CngKey.Import(p8Certificate, CngKeyBlobFormat.Pkcs8PrivateBlob);
using (ECDsaCng dsa = new ECDsaCng(key))
{
dsa.HashAlgorithm = CngAlgorithm.Sha256;
var unsignedJwtData = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(headerString)) + "." + Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(payloadString));
var signature = dsa.SignData(Encoding.UTF8.GetBytes(unsignedJwtData));
return unsignedJwtData + "." + Base64UrlEncoder.Encode(signature);
}
}
However, when I deploy my WebJob to Azure, I get the following exception:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: NotificationFunctions.QueueOperation ---> System.Security.Cryptography.CryptographicException: The system cannot find the file specified. at System.Security.Cryptography.NCryptNative.ImportKey(SafeNCryptProviderHandle provider, Byte[] keyBlob, String format) at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format, CngProvider provider)
It says it can't find a specified file, but the parameters I am passing in are not looking at a file location, they are in memory. From what I have gathered, there may be some kind of cryptography setting I need to enable to be able to use the CngKey.Import method, but I can't find any settings in the Azure portal to configure related to this.
I have also tried using JwtSecurityTokenHandler, but it doesn't seem to handle the ES256 hashing algorithm I need to use (even though it is referenced in the JwtAlgorithms class as ECDSA_SHA256).
Any suggestions would be appreciated!
UPDATE
It appears that CngKey.Import may actually be trying to store the certificate somewhere that is not accessible on Azure. I don't need it stored, so if there is a better way to access the certificate in memory or convert it to a different kind of certificate that would be easier to use that would work.
UPDATE 2
This issue might be related to Azure Web Apps IIS setting not loading the user profile as mentioned here. I have enabled this by setting WEBSITE_LOAD_USER_PROFILE = 1 in the Azure portal app settings. I have tried with this update when running the code both via the WebJob and the Web App in Azure but I still receive the same error.
I used a decompiler to take a look under the hood at what the CngKey.Import method was actually doing. It looks like it tries to insert the certificate I am using into the "Microsoft Software Key Storage Provider". I don't actually need this, just need to read the value of the certificate but it doesn't look like that is possible.
Once I realized a certificate is getting inserted into a store somewhere one the machine, I started thinking about how bad of a think that would be from a security standpoint if your Azure Web App was running in a shared environment, like it does for the Free and Shared tiers. Sure enough, my VM was on the Shared tier. Scaling it up to the Basic tier resolved this issue.

How to add custom certificate authority (CA) to nodejs

I'm using a CLI tool to build hybrid mobile apps which has a cool upload feature so I can test the app on a device without going through the app store (it's ionic-cli). However, in my company like so many other companies TLS requests are re-signed with the company's own custom CA certificate which I have on my machine in the keychain (OS X). However, nodejs does not use the keychain to get its list of CA's to trust. I don't control the ionic-cli app so I can't simply pass in a { ca: } property to the https module. I could also see this being a problem for any node app which I do not control. Is it possible to tell nodejs to trust a CA?
I wasn't sure if this belonged in Information Security or any of the other exchanges...
Node.js 7.3.0 (and the LTS versions 6.10.0 and 4.8.0) added NODE_EXTRA_CA_CERTS environment variable for you to pass the CA certificate file. It will be safer than disabling certificate verification using NODE_TLS_REJECT_UNAUTHORIZED.
$ export NODE_EXTRA_CA_CERTS=[your CA certificate file path]
You can specify a command line option to tell node to use the system CA store:
node --use-openssl-ca
Alternatively this can be specified as an environment variable, if you are not running the node CLI directly yourself:
NODE_OPTIONS=--use-openssl-ca
There is an undocumented, seemingly stable, API for appending a certificate to the default list:
const tls = require('tls');
const secureContext = tls.createSecureContext();
// https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt
secureContext.context.addCACert(`-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----`);
const sock = tls.connect(443, 'host', { secureContext });
For more information, checkout the open issue on the subject: https://github.com/nodejs/node/issues/27079
I'm aware of two npm modules that handle this problem when you control the app:
https://github.com/fujifish/syswide-cas (I'm the author of this one)
https://github.com/coolaj86/node-ssl-root-cas
node-ssl-root-cas bundles it's own copies of nodes root CAs and also enables adding your own CAs to trust. It places the certs on the https global agent, so it will only be used for https module, not pure tls connections. Also, you will need extra steps if you use a custom Agent instead of the global agent.
syswide-cas loads certificates from pre-defined directories (such as /etc/ssl/certs) and uses node internal API to add them to the trusted list of CAs in conjunction to the bundled root CAs. There is no need to use the ca option since it makes the change globally which affects all later TLS calls automatically.
It's also possible to add CAs from other directories/files if needed.
It was verified to work with node 0.10, node 5 and node 6.
Since you do not control the app you can create a wrapper script to enable syswide-cas (or node-ssl-root-cas) and then require the ionic-cli script:
require('syswide-cas'); // this adds your custom CAs in addition to bundled CAs
require('./path/to/real/script'); // this runs the actual script
This answer is more focused towards package maintainers/builders.
One can use this method if you do not want end users to rely on additional environment variables.
When nodejs is built from source, it (by default, can be overridden) embeds the Mozilla CA certificate database into the binary itself. One can add more certificates to this database using the following commands:
# Convert your PEM certificate to DER
openssl x509 -in /path/to/your/CA.pem -outform der -out CA.der
# Add converted certificate to certdata
nss-addbuiltin -n "MyCompany-CA" -t "CT,C,C" < CA.der >> tools/certdata.txt
# Regenerate src/node_root_certs.h header file
perl tools/mk-ca-bundle.pl
# Finally, compile
make install

Resources