Xamarin webview Android SSL error untrusted - how to add trusted certificate - xamarin

I have SSL error untrusted on my Xamarin application on adroid app (iOS works good). I need to use Webview.
I follow this instructions for httpclient : https://thomasbandt.com/certificate-and-public-key-pinning-with-xamarin . I need to set TrustManagerFactory, KeyManagerFactory and KeyStore, but in WebViewRenderer and WebViewClient I can not find option to add my certificates as trusted. I dont want to compare certificates in OnReceivedSslError override method, because certificate that come in this method is the final certificate (that will expire up to one year). I want to add my root and intermediate certificates to list of trusted certificates before checking certificate method in webview will be called.
ExportRenderer:
public class CustomWebView : WebViewRenderer
{
private TrustManagerFactory _trustManagerFactory;
private KeyManagerFactory _keyManagerFactory;
private KeyStore _keyStore;
public CustomWebView(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var customWebViewClient = new CustomWebViewClient();
_trustManagerFactory = CertificateHelper.TrustManagerFactory;
_keyManagerFactory = CertificateHelper.KeyManagerFactory;
_keyStore = CertificateHelper.KeyStore;
Control.SetWebViewClient(customWebViewClient);
}
}
}
How to set TrustManagerFactory, KeyManagerFactory and KeyStore here?

I have finally found working solution. In my case adding end-user certificate or intermediate certificate, (separately or together), makes everything working. End-user cert is short term so I advice to add long term intermediate certificate.
Add:
android:networkSecurityConfig="#xml/network_security_config" to manifest in application section.
Add new xml file (build action AndroidResource) to Resources -> xml -> network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="#raw/untrusted_ca"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
Name of certificate should consist of only lower case letters, numbers and underscore.
Add certificate (build action: AndroidResource) in: Resources -> raw -> untrusted_ca.pem
Now android webview trusts server on application level so no more ssl error occurs, when connecting to particular server.

Related

Spring-ws security header to load configurations for multiple cert

I am trying to invoke a SOAP webservice in my spring boot application using spring-ws with a keystore which has multiple certs. The configuration always defaults to single cert.
Sample code below:
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
Merlin merlin = new Merlin();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream inputStream = new FileInputStream(ResourceUtils.getFile(("keystore.jks")));
keyStore.load(inputStream, "tester".toCharArray());
merlin.setKeyStore(keyStore);
wss4jSecurityInterceptor.setSecurementSignatureCrypto(merlin);
wss4jSecurityInterceptor.setSecurementUsername("test");
wss4jSecurityInterceptor.setSecurementPassword("");
webServiceTemplate.setInterceptors(new org.springframework.ws.client.support.interceptor.ClientInterceptor[]
{wss4jSecurityInterceptor});
When i checked the source code of the apache library class WSSecSignature class. I see there is a configuration for picking up multiple cert. But am not sure how to set the singleCert to be false in the wss4jSecurityInterceptor. It always goes to the else block in the below logic
if (!this.useSingleCert) {
this.secRef.addTokenType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
} else {
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
}
Is there a config i need to set while setting the keystore to Merin object, to make the useSingleCert as false?
Found a work around to override the Wss4jSecurityInterceptor, set the property to false and use the extended interceptor
class SecurityInterceptor extends Wss4jSecurityInterceptor
{
#Override
protected RequestData initializeRequestData(MessageContext messageContext) {
messageContext.setProperty(WSHandlerConstants.USE_SINGLE_CERTIFICATE, "false");
return super.initializeRequestData(messageContext);
}
}

Spring boot ssl how to trust all valid certificates

I have enabled ssl in my spring boot application and anytime I want to use a Rest service like googleapis or facebook I have to add the certificate in the truststore.
I use openssl to get the certificate:
openssl s_client -connect googleapis.com:443
and I import it into the truststore :
keytool.exe -import -noprompt -trustcacerts -alias googleapis.com
-file googleapis.com.cer -keystore app-server.p12 -storepass *****
The problem is that it's very inconvenient to manage, when the certificate expires I have to update the certificates in the truststore everytime.
Also even if I add the right certificate sometimes I get an error:
Caused by: javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
I know these certificates are valid so, is there a possibility to trust them automatically without adding them in the truststore?
Here is my ssl configuration
ssl:
enabled: true
key-store: classpath:keystore/app-server.p12
key-store-password: ******
key-alias: app-server
key-store-type: PKCS12
trust-store: classpath:keystore/app-server.p12
trust-store-password: *****
trust-store-type: PKCS12
keyStorePath: config/keystore/app-server.p12
Also I had to add system properties in the main method
private static String keyStorePath;
private static String keyStorePassword;
#Value("${server.ssl.keyStorePath}")
public void setKeyStorePath(String keyStorePath) {
ClientUiApplication.keyStorePath = keyStorePath;
}
#Value("${server.ssl.key-store-password}")
public void setKeyStorePassword(String keyStorePassword) {
ClientUiApplication.keyStorePassword = keyStorePassword;
}
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
System.setProperty("javax.net.ssl.trustStore", keyStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
}
Another important detail, I need a truststore because I use self-signed certificates generated with Keytool. Without, my services cannot communicate with each other.
You give the code:
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
System.setProperty("javax.net.ssl.trustStore", keyStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
}
This means that you force the java trust store to be the trust store you provide. The default trust store is not in use anymore.
So yes, you have to add every needed root certificates in this trust store to not have the issue you describe.
Why do you need to have a specific trust store?
If there is no use, remove that.
If you have a specific additional certificate you have to trust, you'd better add this certificate to the default trust store (the jre/lib/security/cacerts file)
You can also Bypass SSL Certificate Checking using CloseableHttpClient
public static CloseableHttpClient getCloseableHttpClient()
{
CloseableHttpClient httpClient = null;
try {
httpClient = HttpClients.custom().
setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).
setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy()
{
public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
return true;
}
}).build()).build();
} catch (KeyManagementException e) {
LOGGER.error("KeyManagementException in creating http client instance", e);
} catch (NoSuchAlgorithmException e) {
LOGGER.error("NoSuchAlgorithmException in creating http client instance", e);
} catch (KeyStoreException e) {
LOGGER.error("KeyStoreException in creating http client instance", e);
}
return httpClient;
}
It will trust automatically all the certificates and saves you from SSLHandshakeException: sun.security.validator.ValidatorException
Note this method is not recommended in Production

Problemns connecting Spring to Active Directory

I'm trying to connect to my company's active diretory to create an appplication capable of adding, updating and removing users from it, I configured may LdapContextSource with all the information my collegue who created de AD environment but when I try to do a simple search it doesn't work and give me this error:
org.springframework.ldap.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]; nested exception is javax.naming.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]
Here is my simple code:
public User getUserByLogin(String login) {
LdapContextSource contextSource = new LdapContextSource();
try {
contextSource.setUrl("secret");
contextSource.setBase("secret");
contextSource.setUserDn("secret");
contextSource.setPassword("secret");
contextSource.afterPropertiesSet();
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
User user = ldapTemplate.findOne(query().where("uid").is(login), User.class);
return user;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
May be relevant to let you guys to know that we're using GSS-API
Thanks in advance, hope someone can help me
The error indicates that transport encryption is required -- this generally means you need to change the LDAP server URI from ldap://server.domain.gTLD to ldaps://server.domain.gTLD but since few LDAP servers use certs issued from a public certificate authority (CA), you'll also need to set up the certificate trust. I use a private CA to sign all certificates, so can import the CA public key into the Java truststore.

Has anyone successfully implemented Azure Active Directory B2C for auth using Microsoft.Identity.Client 1.1.0-preview?

I have been struggling with this for several days (three actually). I have AAD B2C working on a web app and an api. I cannot get it running on my Xamarin mobile project. I am using the UWP project to test my configuration since it has the easiest app to troubleshoot on a Windows 10 machine. I am using Visual Studio 2015 Pro.
I am using the Microsoft.Identity.Client 1.1.0-preview.
I used this as my starting point for my attempt to implement.
https://github.com/Azure-Samples/active-directory-b2c-xamarin-native
Right now the project will compile and launch. When I click on Sign in, I get a WebView, but it doesn't look exactly right....
[First Image in Screenshots]
Here are my variables...
public class Constants
{
public static string ApplicationID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
public static string[] Scopes = {""};
public static string SignUpSignInPolicy = "B2C_1_Standard_SignUpSignIn";
public static string ResetPasswordPolicy = "B2C_1_Standard_PasswordReset";
public static string EditProfilePolicy = "B2C_1_Standard_EditProfile";
public static string Authority = "https://login.microsoftonline.com/[MyTennantName].onmicrosoft.com/B2C_1_Standard_SignUpSignIn";
public static string AuthorityEditProfile = "https://login.microsoftonline.com/[MyTennantName].onmicrosoft.com/B2C_1_Standard_EditProfile";
public static string ApiEndpoint = "https://[MyTennantName].onmicrosoft.com/apiservices";
public static UIParent UiParent = null;
}
My Login method is....
async void OnSignInSignOut(object sender, EventArgs e)
{
try
{
if (btnSignInSignOut.Text == "Sign in")
{
AuthenticationResult ar = await App.PCA.AcquireTokenAsync(Constants.Scopes, GetUserByPolicy(App.PCA.Users, Constants.SignUpSignInPolicy), Constants.UiParent);
UpdateUserInfo(ar);
UpdateSignInState(true);
}
else
{
foreach (var user in App.PCA.Users)
{
App.PCA.Remove(user);
}
UpdateSignInState(false);
}
}
catch (Exception ex)
{
// Checking the exception message
// should ONLY be done for B2C
// reset and not any other error.
if (ex.Message.Contains("AADB2C90118"))
OnPasswordReset();
// Alert if any exception excludig user cancelling sign-in dialog
else if (((ex as MsalException)?.ErrorCode != "authentication_canceled"))
await DisplayAlert($"Exception:", ex.ToString(), "Dismiss");
}
}
However before I can even enter my password I get the following....
[Second image in Screenshots]
My application definition looks like this...[Third image in screenshots]
I don't think it is recognizing my tenant and trying to log me in with a Microsoft account. I have double checked my Tenant name and Application ID.
Screenshots
I don't have enough reputation to post more than one link and one picture.
Also, the Azure AD B2C api application works for a web app. I have created a web app that can authenticate and works with the API.
It looks like while modifying the authorization value in the Sample you removed the /tfp/ part.
You should update your values as follows:
public static string Authority = "https://login.microsoftonline.com/tfp/[MyTennantName].onmicrosoft.com/B2C_1_Standard_SignUpSignIn";
public static string AuthorityEditProfile = "https://login.microsoftonline.com/tfp/[MyTennantName].onmicrosoft.com/B2C_1_Standard_EditProfile";

Embedded Jetty with https : Invalid keystore format

I'm trying to get my embedded Jetty 8.1.4 (no I can go higher!) server to work with https. I generate my keystore with the following command :
keytool -genkey -alias sitename -keyalg RSA -keystore keystore -keysize 2048
My problem is that when I run this, I get a nice exception!
WARN - AbstractLifeCycle - FAILED SslContextFactory#160b6dff(null,null): java.io.IOException: Invalid keystore format
java.io.IOException: Invalid keystore format
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:633)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:38)
at java.security.KeyStore.load(KeyStore.java:1183)
I tried generating the keystore following different online guides but to no avail.
Any ideas?
Many Thanks,
AW
Here's the server code :
public static void main(String[] args) throws Exception {
Server server = new Server();
server.setStopAtShutdown(true);
server.setGracefulShutdown(ALLOWED_SHUTDOWN_TIME);
Resource keystore = Resource.newClassPathResource("/keystore");
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreResource(keystore);
sslContextFactory.setKeyStorePassword("wicket");
sslContextFactory.setTrustStoreResource(keystore);
sslContextFactory.setKeyManagerPassword("wicket");
SslSelectChannelConnector connector = new SslSelectChannelConnector(sslContextFactory);
connector.setMaxIdleTime(30000);
connector.setPort(getServerPort());
connector.setAcceptors(4);
connector.setReuseAddress(false);
server.addConnector(connector);
final ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
context.setResourceBase("src/main/webapp");
context.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
context.getSessionHandler().getSessionManager().setMaxInactiveInterval(60 * 15);
context.getSessionHandler().getSessionManager().setSessionTrackingModes(newHashSet(SessionTrackingMode.COOKIE));
final FilterHolder openSessionInViewFilter = new FilterHolder(new OpenSessionInViewFilter());
context.addFilter(openSessionInViewFilter, "/", EnumSet.of(DispatcherType.REQUEST));
final FilterHolder wicketFilter = new FilterHolder(WicketFilter.class);
wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*");
wicketFilter.setInitParameter("applicationFactoryClassName", "org.apache.wicket.spring.SpringWebApplicationFactory");
wicketFilter.setInitParameter("configuration", "development");
context.addFilter(wicketFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
context.getInitParams().put("contextConfigLocation", "classpath:spring/spring-config.xml");
context.addEventListener(new ContextLoaderListener());
try {
server.start();
server.join();
} catch (Exception exception) {
exception.printStackTrace();
System.exit(100);
}
}
When it comes to SSL and the web, you HAVE TO STAY CURRENT both in the JVM and the Server software. The rapid changes in what is supported in SSL in the browser, proxies, hardware, etc.. force you to.
For example, Jetty 8.1.4 was created back in the days of SSL v1, SSL v2 were popular. Today, SSL is pretty much deprecated (all the way to SSL v3), replaced with TLS with NPN/ALPN being increasingly popular.
Newer versions of Jetty have even deprecated various cipher suites that cause problems online.
Even the Certificate Authorities (CA) are moving in directions that render the older SSLs irrelevant, making it almost impossible to get a Certificate that will work with these old environments.

Resources