GithubHelp home page GithubHelp logo

datatheorem / trustkit-android Goto Github PK

View Code? Open in Web Editor NEW
582.0 24.0 87.0 855 KB

Easy SSL pinning validation and reporting for Android.

License: MIT License

Java 98.58% Kotlin 1.42%
ssl-pinning ssl-reporting android ssl

trustkit-android's Introduction

TrustKit Android

Build Status API Version MIT License Gitter chat

TrustKit Android is an open source library that makes it easy to deploy SSL public key pinning and reporting in any Android App.

If you need SSL pinning/reporting in your iOS App. we have also released TrustKit for iOS and macOS at https://github.com/datatheorem/TrustKit.

Overview

TrustKit Android works by extending the Android N Network Security Configuration in two ways:

  • It provides support for the <pin-set> (for SSL pinning) and <debug-overrides> functionality of the Network Security Configuration to earlier versions of Android, down to API level 17. This allows Apps that support versions of Android earlier than N to implement SSL pinning in a way that is future-proof.
  • It adds the ability to send reports when pinning validation failed for a specific connection. Reports have a format that is similar to the report-uri feature of HTTP Public Key Pinning and TrustKit iOS.

For better compatibility, TrustKit will also run on API levels 15 and 16 but its functionality will be disabled.

Getting Started

Sample Usage

Adding TrustKit as a Dependency

Add TrustKit to your project's build.gradle:

implementation 'com.datatheorem.android.trustkit:trustkit:<last_version>'

Configuring a Pinning Policy

Deploying SSL pinning in the App requires initializing TrustKit with a pinning policy (domains, pins, and additional settings). The policy is wrapped in the official Android N Network Security Configuration i.e :

<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <!-- Pin the domain www.datatheorem.com -->
  <!-- Official Android N API -->
  <domain-config>
    <domain>www.datatheorem.com</domain>
    <pin-set>
      <pin digest="SHA-256">k3XnEYQCK79AtL9GYnT/nyhsabas03V+bhRQYHQbpXU=</pin>
      <pin digest="SHA-256">2kOi4HdYYsvTR1sTIR7RHwlf2SescTrpza9ZrWy7poQ=</pin>
    </pin-set>
    <!-- TrustKit Android API -->
    <!-- Do not enforce pinning validation -->
    <trustkit-config enforcePinning="false">
      <!-- Add a reporting URL for pin validation reports -->
      <report-uri>http://report.datatheorem.com/log_report</report-uri>
    </trustkit-config>
  </domain-config>
  <debug-overrides>
    <trust-anchors>
      <!-- For debugging purposes, add a debug CA and override pins -->
      <certificates overridePins="true" src="@raw/debugca" />
    </trust-anchors>
  </debug-overrides>
</network-security-config>

Initializing TrustKit with the Pinning Policy

The path to the XML policy should then be specified in the App's manifest in order to enable it as the App's Network Security Configuration on Android N:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

Then, TrustKit should be initialized with the same path:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.OnCreate(savedInstanceState);

  // Using the default path - res/xml/network_security_config.xml
  TrustKit.initializeWithNetworkSecurityConfiguration(this);

  // OR using a custom resource (TrustKit can't be initialized twice)
  TrustKit.initializeWithNetworkSecurityConfiguration(this, R.xml.my_custom_network_security_config);

  URL url = new URL("https://www.datatheorem.com");
  String serverHostname = url.getHost();
  
  //Optionally add a local broadcast receiver to receive PinningFailureReports
  PinningValidationReportTestBroadcastReceiver receiver = new PinningValidationReportTestBroadcastReceiver();
          LocalBroadcastManager.getInstance(context)
                  .registerReceiver(receiver, new IntentFilter(BackgroundReporter.REPORT_VALIDATION_EVENT));

  // HttpsUrlConnection
  HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
  connection.setSSLSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname));

  // OkHttp 2.x
  OkHttpClient client =
    new OkHttpClient()
        .setSslSocketFactory(OkHttp2Helper.getSSLSocketFactory());
  client.interceptors().add(OkHttp2Helper.getPinningInterceptor());
  client.setFollowRedirects(false);

  // OkHttp 3.0.x, 3.1.x and 3.2.x
  OkHttpClient client =
    new OkHttpClient.Builder()
        .sslSocketFactory(OkHttp3Helper.getSSLSocketFactory())
        .addInterceptor(OkHttp3Helper.getPinningInterceptor())
        .followRedirects(false)
        .followSslRedirects(false)

  // OkHttp 3.3.x and higher
  OkHttpClient client =
    new OkHttpClient.Builder()
        .sslSocketFactory(OkHttp3Helper.getSSLSocketFactory(), OkHttp3Helper.getTrustManager())
        .addInterceptor(OkHttp3Helper.getPinningInterceptor())
        .followRedirects(false)
        .followSslRedirects(false)
    .build();
}

class PinningFailureReportBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        PinningFailureReport report = (PinningFailureReport) intent.getSerializableExtra(BackgroundReporter.EXTRA_REPORT);
    }
}

Once TrustKit has been initialized and the client or connection's SSLSocketFactory has been set, it will verify the server's certificate chain against the configured pinning policy whenever an HTTPS connection is initiated. If a report URI has been configured, the App will also send reports to the specified URI whenever a pin validation failure occurred.

You can also create and register local broadcast receivers to receive the same certificate pinning error reports that would be sent to the report_uris.

Limitations

On Android N devices, TrustKit uses the OS's implementation of pinning, and it is not affected by the following limitations.

On Android M and earlier devices, TrustKit provides uses its own implementation of pinning that is mostly-compatible with Android N's pinning behavior. However, in order to keep the code base as simple as possible, it has the following limitations:

  • The pinning policy will only be applied to connections that were configured to use a TrustKit-provided SSLSocketFactory or X509TrustManager.
  • The SSLSocketFactory or X509TrustManager provided by TrustKit can only be used for connections to the domain that was passed to the getTrustManager() and getSSLSocketFactory() methods. Hence, if a redirection to a different domain occurs, the new domain will fail SSL validation and the connection will fail. In practice, this should not be a problem because pinning validation is only meant to be used on the few specific domains on which the App's main server API is hosted --- redirections should not happen in this scenario.
  • The <trust-anchors> setting is only applied when used within the global <debug-overrides> tag. Hence, custom trust anchors for specific domains cannot be set.
  • Within the <trust-anchors> tag, only <certificate> tags pointing to a raw certificate file are supported (the user or system values for the src attribute will be ignored).

For consumers of TrustKit's OkHttpHelper solutions, redirects must to be disabled as Pinning will currently only work properly on the initial request and not any redirects

License

TrustKit Android is released under the MIT license. See LICENSE for details.

trustkit-android's People

Contributors

aetherknight avatar chintanrathod avatar freszu avatar jboles31 avatar jobot0 avatar lewie9021 avatar malashkevich avatar nabla-c0d3 avatar nathan-l avatar simonlaalo avatar umangmathur92 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trustkit-android's Issues

java.lang.IllegalStateException: TrustKit has already been initialized

When app is closed though "back" button and resume, onCreate will be called again.
If I put TrustKit.initializeWithNetworkSecurityConfiguration inside onCreate as readme shows it throws TrustKit has already been initialized error.

shall I add a try catch around it to avoid the crash?

Domain exception to be made on local IP address 10.0.2.2

Describe the bug
From React Native version 0.58 onwards, with Android API version 28, no clear text traffic is allowed by default. But currently the React Native packager and debugger connects to the device or emulator via HTTP taking either localhost or 10.0.0.2. These values are hardcoded in RN's codebase.

We tried to make an exception to them by putting below config block in the network_security_config.xml file which is further referenced in the AndroidManifest.xml file.

<domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="false">localhost</domain>
    <domain includeSubdomains="false">10.0.2.2</domain>
    <domain includeSubdomains="false">10.0.3.2</domain>
    <trustkit-config enforcePinning="false"/>
</domain-config>

This SO entry explains this approach in detail.

But this config is not acknowledge by the TrustKit module with below exception, hence causing the app to crash upon launch.

03-02 16:18:56.826 19455 19455 E AndroidRuntime: java.lang.RuntimeException: Unable to create application ***.***.MainApplication: com.datatheorem.android.trustkit.config.ConfigurationException: Tried to pin an invalid domain: 10.0.3.2
...
03-02 16:18:56.826 19455 19455 E AndroidRuntime: Caused by: com.datatheorem.android.trustkit.config.ConfigurationException: Tried to pin an invalid domain: 10.0.3.2
03-02 16:18:56.826 19455 19455 E AndroidRuntime: 	at com.datatheorem.android.trustkit.config.DomainPinningPolicy.<init>(DomainPinningPolicy.java:48)

This problem is very similar to #25 for which only localhost was exempted.

To Reproduce
Put above XML config block for clear text traffic in an RN (version >= 0.58) app with TrustKit module installed, run react-native run-android to install and launch it in an Android emulator. The app will crash upon launch and the above exception messages can be read via logcat.

Expected behavior
Certain local IP addresses like 10.0.2.2 used by the RN packager and debugger should be considered as valid domain.

TrustKit configuration

<domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="false">localhost</domain>
    <domain includeSubdomains="false">10.0.2.2</domain>
    <domain includeSubdomains="false">10.0.3.2</domain>
    <trustkit-config enforcePinning="false"/>
</domain-config>

App details:

  • App target SDK: 28.0.3
  • App language: JS/React Native
  • Android version to reproduce the bug: Andorid 9.0.

domain-config without pin set results in ConfigurationException: Policy contains 0 domains to pin

Describe the bug
In the network_security_config.xml file, if there is a domain-config block without any pin set like below, an com.datatheorem.android.trustkit.config.ConfigurationException: Policy contains 0 domains to pin exception will be thrown upon app launch which causes the app to crash. I believe this results from the fix to #49 .

<domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="false">localhost</domain>
    <domain includeSubdomains="false">10.0.2.2</domain>
    <domain includeSubdomains="false">10.0.3.2</domain>
    <trustkit-config enforcePinning="false"/>
</domain-config>

Exception stack trace from logcat:

04-02 17:15:30.089 23250 23250 E AndroidRuntime: java.lang.RuntimeException: Unable to create application xxx.xxx.xxx.MainApplication: com.datatheorem.android.trustkit.config.ConfigurationException: Policy contains 0 domains to pin
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5876)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.app.ActivityThread.access$1100(ActivityThread.java:199)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:193)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6669)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: Caused by: com.datatheorem.android.trustkit.config.ConfigurationException: Policy contains 0 domains to pin
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.datatheorem.android.trustkit.config.TrustKitConfiguration.<init>(TrustKitConfiguration.java:42)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.datatheorem.android.trustkit.config.TrustKitConfiguration.<init>(TrustKitConfiguration.java:33)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.datatheorem.android.trustkit.config.TrustKitConfigurationParser.fromXmlPolicy(TrustKitConfigurationParser.java:71)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.datatheorem.android.trustkit.config.TrustKitConfiguration.fromXmlPolicy(TrustKitConfiguration.java:28)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.datatheorem.android.trustkit.TrustKit.initializeWithNetworkSecurityConfiguration(TrustKit.java:311)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.datatheorem.android.trustkit.TrustKit.initializeWithNetworkSecurityConfiguration(TrustKit.java:271)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at com.hpb.nhp.MainApplication.onCreate(MainApplication.java:76)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1154)
04-02 17:15:30.089 23250 23250 E AndroidRuntime: 	at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5871)

To Reproduce
Put above XML config block for clear text traffic in an RN (version >= 0.58) app with TrustKit module (v1.1.1) installed, run react-native run-android to install and launch it in an Android emulator. The app will crash upon launch and the above exception messages can be read via logcat.

Expected behavior
Domains without pin set should just be ignored as promised in the release notes of version 1.1.1.

TrustKit version
1.1.1

App details:
App target SDK: 28.0.3
App language: JS/React Native
Android version to reproduce the bug: Andorid 9.0.

Debug certificate resource can not be parsed

Giving a debug certificate in the network_security_config.xml does not work. This only reveals itself on API < 24, as the certificate is read by TrustKit and not the Android system, but the parsing fails in all cases.

<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">test.com</domain>
        <pin-set>
            <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
            <pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin>
        </pin-set>

        <trustkit-config enforcePinning="true" disableDefaultReportUri="true" />
    </domain-config>
    <debug-overrides>
        <trust-anchors>
            <!-- Use a debug certificate -->
            <certificates overridePins="false" src="@raw/certificate" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

When debugging the Trustkit initialization, the TrustKitConfigurationParser#readDebugOverrides line String caPathFromUser = parser.getAttributeValue(null, "src").trim();
returns @2131558400 instead of the expected @raw/certificate.

This is probably because aapt converts all the references instead of keeping raw strings.

As a result, the parser skips this resource, because it does not start with @raw.

To Reproduce
Use the network file with a certificate on API <24, it will not be read and thus not used in subsequent HTTP calls.

App details:

  • App target SDK: 19
  • App language: Kotlin
  • Android version to reproduce the bug: Android Marshmallow (23), Android Pie (28)

Include only specific domains

Hi,

I am using multiple frameworks in my project like Firebase, Azure and other 3rd party servers.
So, I just want to include my server API domains for certificate pinning and Firebase, Azure and other 3rd party's API should be excluded.

How we can achieve this in Android N and above as Its throwing exception for 3rd party servers

Question on WebViews

Hey @nabla-c0d3 and @jobot0,

I was doing a few tests here and found out that a device prior to API 24 won't have pinning when calling the WebView class. Is that an expected limitation or something I might be doing wrong?

Thanks!

Enforce pinning not respected in Android 5.0

Describe the bug
When using the TrustKit on 5.0 device, I am getting javax.net.ssl.SSLHandshakeException: Certificate validation failed for ...
When checking the log, it has correct parameters:
E/CERT ERROR: "include-subdomains": false,
E/CERT ERROR: "enforce-pinning": false,
E/CERT ERROR: "validation-result": 2,

These are respected on Android 8.0
The CERT ERROR: "validated-certificate-chain": contains the correct certificate, that is in the xml file and served-certificate-chain": are also same. known-pins are also correct.

To Reproduce
I can only reproduce on the 5.0 device, works correctly in emulator
I am not including the domain, because it is accessible only on private network

Expected behavior
It shouldn't fail and if it fails, it shouldn't block the traffic with enforce-pinning set to false (works well on other devices)

TrustKit configuration

domain.com f3n+wu1f9Z4QvyZZAItVF55NNBJpDFf8f68P/uLyRHA= BnGNbPrwbfsIyAu+IjeZ/nAOloLMEVQXNAyQse4u/nA= subdomain.domain.com KZOAUwc92hHmVhuD8TaDaMp3yLKF1Y6vaurRigRS8w8= EjGCjEFfzshXkT1QHH0fVfCjhy5CqZkB3TxXYRrokUI=

App details:

  • App target SDK: 28
  • App language:Java
  • Android version to reproduce the bug: 5.0

Allow to get all domain policies from TrustKitConfiguration

Hello,
I'm trying to use TrustKit-Android with React Native. As discussed in #6, I'm also going to use CertificatePinner in OkHttp3 but I need hostname to retrieve pins from TrustKitConfiguration in this scenario.

Currently, my application has a lot of target domains like*.domain-a.com and *.domain-b.com.
So, I'd like to manage domains and pins at one place in order to keep maintainability. Also, I have to insert the pinner for all domains at first because RN doesn't allow us to recreate OkHttpClient for another domain at that time.

To create the pinner for all necessary domains, I'd like to retrieve all pins from TrustKitConfiguration without hostname. For example,

final TrustKitConfiguration config = TrustKit.getInstance().getConfiguration();
Set<DomainPinningPolicy> domainPolicies = config.getAllPolicies(); // <-- new api to retrieve all policies
 
CertificatePinner.Builder certificatePinnerBuilder = new CertificatePinner.Builder();
for (DomainPinningPolicy domainPolicy : domainPolicies) {
    Set<PublicKeyPin> pins = domainPolicy.getPublicKeyPins();
    for (PublicKeyPin pin : pins) {
        certificatePinnerBuilder.add(domainPolicy.getHostname(), "sha256/" + pin.toString());
    }
}
CertificatePinner certificatePinner = certificatePinnerBuilder.build();
 
final OkHttpClient.Builder builder = OkHttpClientProvider.createClient().newBuilder()
        .certificatePinner(certificatePinner);

I can contribute if this idea is acceptable for the library. What about it?
Thanks

Not working on Android 19

The trustkit setup works perfectly on Android 21 and above but it fails in Android 19.

here is my network-security config file:

<network-security-config> <domain-config> <domain includeSubdomains="true">endpoint</domain> <pin-set> <pin digest="SHA-256">hash1</pin> <pin digest="SHA-256">hash2</pin> </pin-set> <trustkit-config enforcePinning="true" /> </domain-config> </network-security-config>

The issue is when a different pin hash is provided, it still allows connection.

Allow to disable pinning for subdomains

Hey,
I want to enable pinning for all our domain, but disable it for a specific subdomain. So I tried it with something like that:

<network-security-config>
  <!-- Pin the domain www.datatheorem.com -->
  <!-- Official Android N API -->
  <domain-config>
    <domain includeSubdomains="true">www.mydomain.com</domain>
    <pin-set>
      <pin digest="SHA-256">....</pin> <!-- production -->
      <pin digest="SHA-256">...</pin> <!-- backup -->
    </pin-set>
    <!-- TrustKit Android API -->
    <!-- Do not enforce pinning validation -->
    <trustkit-config enforcePinning="true" disableDefaultReportUri="true">
      <!-- Add a reporting URL for pin validation reports-->
      <report-uri>https://report-uri</report-uri>
    </trustkit-config>
    <domain-config>
      <domain>unsecure.mydomain.com</domain>
      <pin-set></pin-set>
      <trustkit-config enforcePinning="false" disableDefaultReportUri="true">
        <!-- Add a reporting URL for pin validation reports-->
        <report-uri>https://report-uri</report-uri>
      </trustkit-config>
    </domain-config>
  </domain-config>
</network-security-config>

Notice the empty pin set in the child domain configuration - this is required, otherwise Android will use the pin from the parent domain cofing. The problem is - TrustKit not allow declaring empty pinset. Maybe this is something possible to change?
I have similar issue with ios - datatheorem/TrustKit#88 - and I suggested similar change there too.
Thanks,
Omer

How does enforcePinning configuration works?

I looked at source code and the only thing I could not find anywhere that it is skipping pinning check when it is "false". And the only reference I could find is in PinningFailureReport

Run tests on multiple API levels in CI

Because there are two very different code paths in TrustKit's pinning validation logic (before VS after API level 24), it would be useful to be able to run the test suite in Travis on at least two API levels, such as 17 and 24.

enforcePinning flag not working when set to false

I have implemented TrustKit in my react-native app. I'm using TrustKit 1.1.0 and OkHttp 3.6.0.
When I set enforcePinning to false, TrustKit is still blocking the network connection when SSL Pinning validation fails.
enforcePinning does not make any difference if set to true or false, TrustKit always blocks the network connection when SSL Pinning validation fails.

Localhost and Pins Exception

Hello, thank you for such great library.
I'm currently implementing it for our app,
Basically, it works.

But,
We have in
network_sec_config.xml - a localhost domain.
when running the app, the library throw exception :

    DomainValidator domainValidator = DomainValidator.getInstance(false);
    if (!domainValidator.isValid(hostname)) {
        throw new ConfigurationException("Tried to pin an invalid domain: " + hostname);
    }

@jobot0 Can you help please ?

I see that
DomainValidator domainValidator = DomainValidator.getInstance(false);
domainValidator is always inited with false, and thats why it fails in returning true
in returning allowLocal && hostnameRegex.isValid(domain)
because allowLocal is always false.

BTW, localhost sits inside different [domain-config]
without [pin-set] , and your library throws exception where no pins provided there.
I don't want pins there..

Thanks for the help...

Jazzy.

React Native Support

Hey,
We have an android app with React Native code. React Native allow to override the default ok HTTP client for ALL network request from React Native with the following code:

        OkHttpClient client =
                    new OkHttpClient().newBuilder()
                            .sslSocketFactory(trustKit.getSSLSocketFactory("www.mycompanydomain.com"),
                                    trustKit.getTrustManager("www.mycompanydomain.com"))
                            .connectTimeout(0, TimeUnit.MILLISECONDS)
                            .readTimeout(0, TimeUnit.MILLISECONDS)
                            .writeTimeout(0, TimeUnit.MILLISECONDS)
                            .cookieJar(new ReactCookieJarContainer())
                            .build();
            OkHttpClientProvider.replaceOkHttpClient(client);

This work perfectly well, excpet for the fact that it will block all the request to API that are not belonging to my company - for example, firebase - because it will try to pin them with our domain configuration.
To solve this, we can use an approach similar to what CWAC did:

  • Add a method to PinningTrustManager that allow changing the domain configuration
  • Use the same code above to add TrustKit PinningTrustManager to React Native client
  • Add an interceptor to the client used by React Native, and change the host configuration before each request

Or we can try another approach, I am pretty open to ideas - for example, try to implement pinning in network interceptor - I will try to see if that possible.
Do you encounter similar problems? What will you recommend?
Thanks!

Initialization fails on Android 7 Instant App

Describe the bug
Hi, there seems to be an Android framework issue when building instant app on Android 7 where Truskit fails to initialize.
After debugging, the root cause is coming from parsing the ApplicationInfo in this [method].(https://github.com/datatheorem/TrustKit-Android/blob/master/trustkit/src/main/java/com/datatheorem/android/trustkit/TrustKit.java#L231)
When building instant app project, Android 7 somehow fails to create networkSecurityConfigRes=0x. This doesn't happen on the traditional app.

To Reproduce
Here is the branch of a sample instant app modules:
https://github.com/chihweil5/TrustKit-Android/tree/instant-app

The quick tryout solution I made is by changing this condition here, both instant app and app are working on Android 7. I am not sure if this is a proper fix although. Thanks in advance for looking into this issue!

App details:

  • App target SDK: [e.g. 24]
  • App language: [e.g. Kotlin]
  • Android version to reproduce the bug [e.g. Android N]

Thank you!

Support for base-config

I have a situation where the server certificate doesn't exist in the default Android system trust store for Kitkat(API 19) and below. This is for the release build. So for that purpose we would need to build custom trust store. Can you add support for it using following configuration in the XML file?
<base-config> <trust-anchors> <certificates src="@raw/extracas"/> <certificates src="system"/> </trust-anchors> </base-config>

Question about using this for all api levels

Should TrustKit be used only if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)?

It's unclear to me what will happen if we do this on N+ devices:
connection.setSSLSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname));

This example is for HttpsURLConnection, but the question applies to OkHttp too. Will there be a double pinning check in this case? One by core Android and one by TrustKit?

I did see this in the readme:

On Android N devices, TrustKit uses the OS's implementation of pinning

But it's not clear what happens if our own code is setting SSLSocketFactorys on connections in N. Maybe an explicit statement in the readme could remove doubt that this can indeed be done on N, and that we don't need to include any api level checks.

Thanks!

Android - Dynamic initialization of the configuration/pins.

Hello all

Is your feature request related to a problem? Please describe.
Dynamically initialise trustkit on android.

Describe the solution you'd like
It would be great to be able to initialise trustkit using a TrustKitConfiguration that we craft in the app. If it's not possible would you guys be open to a PR ?

Thank you.

Question reg design of TrustManager

hello,

first of all, thanks a lot for creating this library. I am using it in my personal projects.

However, I've a question.

Why are we creating a new trust manager for each hostname? Can't we have a single trust manager which takes care of all the hosts?

I am providing a singleton okhttp client through Dagger to all the consumers in my app. If I could initialize it at there itself with a custom trustmanager that handles all the urls, I won't have to install the trustmanager in every consumer's class (the classes which use retrofit and and I need to provide them okhttp), which I'd have to do it right now.

so just curious, is there any specific reason behind this design?

Thank you.

TrustKit throws IllegalArgumentException

Hello!

Describe the bug

I'm seeing one crash in Google play console:

java.lang.IllegalArgumentException: 
  at com.datatheorem.android.trustkit.config.TrustKitConfiguration.getPolicyForHostname (TrustKitConfiguration.java:90)
  at com.datatheorem.android.trustkit.pinning.TrustManagerBuilder.getTrustManager (TrustManagerBuilder.java:67)
  at com.datatheorem.android.trustkit.TrustKit.getTrustManager (TrustKit.java:390)

https://github.com/datatheorem/TrustKit-Android/blob/master/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfiguration.java#L91

To Reproduce

Unfortunately I cannot reproduce it. It happens to just two users.
I just enabled certificate pinning for one default server (and user can use their own server for which pining is not enabled).
It looks like server url is not valid, but I validate server url with:

https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/HttpUrl.java#L903

before creating http client:

okhttp3.OkHttpClient.Builder httpClient = new okhttp3.OkHttpClient.Builder();
X509TrustManager[] trustManagers = { TrustKit.getInstance().getTrustManager(backendUrl) };

so I'm confused why TrustKit throws exception.

Expected behavior
App would not crash.

Or please, let me know how should I handle this exception?

TrustKit configuration

<domain-config cleartextTrafficPermitted="false">
	<domain includeSubdomains="true">*******.***</domain>
	<pin-set>
		<pin digest="SHA-256">********************************************</pin>
		<pin digest="SHA-256">********************************************</pin>
	</pin-set>
	<trustkit-config enforcePinning="true" disableDefaultReportUri="true">
	</trustkit-config>
</domain-config>

App details:

  • App target SDK: 28
  • App language: Java
  • Android version to reproduce the bug: 7.1 and 8

Dynamic initialization of TrustKitConfiguration

Hey guys,

are you still working on improvements of the library? I would like to see the possibility to initialize Truskit with an instance of TrustKitConfiguration.

I wanted to use your library in a professional app but i need the possibility to add cert pins at runtime. The app gets his pinning-urls and hashes from a backend service. I know that can be dangerous. But this first backend call is pinned with a static buildconfig pin. After this successfull call i need to initialize TrustKit with the received hashes and urls. The key advantage of this method is that we can change pins and urls as fast as possible. Unfortunately this is not possible with your current implementation.

If you guys are willing to extend your library with this feature but don't have time for that i would like to contribute that change and create a PullRequest. What do you think?

Regarding adding pin_digest(keys) at run time dynamically

@omerlh how we can add dynamic pinning keys i.e. If I am getting pin_digest value from server for x domain how I can use it Because I have already initialized trustkit for different domain y with some static pin_digest.According to this lib we can't initialize trustkit again.Please reply ASAP

Support for tcp conntection?

Hey,
We already using TrustKit in our iOS app, and I know it support tcp connections (datatheorem/TrustKit#76). So I wanted to ask - does the android library can also support tcp connections? We are currently using SSLSocketFactory to create the TCP\SSL socket.
Thanks,
Omer

How to use TrustKit with XHR (XMLHTTPREQUEST)

Hi,
I am using trustkit with react native android and able to get it working with okhttp calls but when I use axios which use XHR, it results into a network error.
What could be the issue/ implementation for the same?
Thanks,

Integration with React Native is broken

RN API was changed from version 43 (see this commit). Since this change, there is no way to change the HTTP client used by RN to perform network request.
This PR should fix it.
This issue is mainly for documentation purpose, in case someone else will encounter this.
Thanks @AvivRubys for finding this.

How to fix this issue?

I am receiving below error in API

java.lang.IllegalStateException: Unable to load publicsuffixes.gz resource from the classpath.

Support to create Local instance

Is your feature request related to a problem? Please describe.
I am not able to create an instance of TrustKit locally just within my library project. It only allows to create a single instance which is shared across the app. So if the app that is using my library also wants to create and configure TrustKit differently, then this could be an issue.

Describe the solution you'd like
TrustKit should allow to create and configure local instances. I see this option is available in TrustKit for iOS.

OkHttp 2 support

I am trying to integrate TrustKit with OkHttp 2 but I have not succeeded yet.

URL url = new URL("https:/url");
String serverHostname = url.getHost();

okHttpClient.setSslSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname));

The problem is that the cert pinning is not happening.

May the fact that I am not passing TrustKit.getInstance().getTrustManager(serverHostname) as the second argument be the problem ?

Third party API and SDK not working.

Describe the bug
I am using trustkit 1.0.1 in my Android project. my setting is enforcePinning = "false". APIs for domain which i have mentioned in domain tag works well but rest of the other APIs are failing "Certificate validation failed". i have Urban Airship, Facebook, aws APIs and SDK in project which are failing. i am not able to launch Urban airship, can not download images from AWS.

To Reproduce
When ever i launch application.

Expected behavior
I need SSL pinning for my mentioned domain only and rest of the third party APIs and SDK should work as they are working without pinning.

TrustKit configuration
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">********.com</domain> <pin-set> <pin digest="SHA-256">****</pin> <pin digest="SHA-256">****</pin> </pin-set> <trustkit-config enforcePinning="false" /> </domain-config> </network-security-config>

App details:

  • App target SDK: 26
  • App language: Java
  • Android version to reproduce the bug [Android N]
    Update:
    Log showing urban Airship blocked.
     - UALib: Request - Request failed URL: https://device-api.urbanairship.com/api/named_users/associate/ method: POST javax.net.ssl.SSLHandshakeException: Certificate validation failed for www.*****.com at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:368) at com.android.okhttp.Connection.connectTls(Connection.java:1510) at com.android.okhttp.Connection.connectSocket(Connection.java:1458) at com.android.okhttp.Connection.connect(Connection.java:1413) at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:1707) at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:133) at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:466) at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:371) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:503) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:130) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:261) at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218) at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java) at com.android.tools.profiler.support.network.httpurl.TrackedHttpURLConnection.getOutputStream(TrackedHttpURLConnection.java:314) at com.android.tools.profiler.support.network.httpurl.HttpsURLConnection$.getOutputStream(HttpsURLConnection$.java:266) at com.urbanairship.http.Request.execute(Request.java:180) at com.urbanairship.push.BaseApiClient.performRequest(BaseApiClient.java:53) at com.urbanairship.push.NamedUserApiClient.associate(NamedUserApiClient.java:52) at com.urbanairship.push.NamedUserJobHandler.onUpdateNamedUser(NamedUserJobHandler.java:120) at com.urbanairship.push.NamedUserJobHandler.performJob(NamedUserJobHandler.java:75) at com.urbanairship.push.NamedUser.onPerformJob(NamedUser.java:97) at com.urbanairship.job.Job$1.run(Job.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:762) Caused by: java.security.cert.CertificateException: Certificate validation failed for www.*******.com at com.datatheorem.android.trustkit.pinning.PinningTrustManager.checkServerTrusted(PinningTrustManager.java:148) at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:182) at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:617) at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:364) at com.android.okhttp.Connection.connectTls(Connection.java:1510)  at com.android.okhttp.Connection.connectSocket(Connection.java:1458)  at com.android.okhttp.Connection.connect(Connection.java:1413)  at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:1707)  at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:133)  at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:466)  at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:371)  at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:503)  at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:130)  at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:261)  at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)  at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java)  at com.android.tools.profiler.support.network.httpurl.TrackedHttpURLConnection.getOutputStream(TrackedHttpURLConnection.java:314)  at com.android.tools.profiler.support.network.httpurl.HttpsURLConnection$.getOutputStream(HttpsURLConnection$.java:266)  at com.urbanairship.http.Request.execute(Request.java:180)  at com.urbanairship.push.BaseApiClient.performRequest(BaseApiClient.java:53)  at com.urbanairship.push.NamedUserApiClient.associate(NamedUserApiClient.java:52)  at com.urbanairship.push.NamedUserJobHandler.onUpdateNamedUser(NamedUserJobHandler.java:120)  at com.urbanairship.push.NamedUserJobHandler.performJob(NamedUserJobHandler.java:75)  at com.urbanairship.push.NamedUser.onPerformJob(NamedUser.java:97)  at com.urbanairship.job.Job$1.run(Job.java:91)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)  at java.lang.Thread.run(Thread.java:762) 

Report is not sent if didChainValidationFail is true

Whenever i try to do MITM attack, the ChainValidation Fail and exception is thrown without sending any report to the server dataTheorem, although with wrong pinning key and no MITM attack the report is sent and everything goes fine.

Multiple domain under domain-config

I have multiple domains under my domain config as both domain use the same certificates. It looks like TurstKit configuration only takes the last one in the list and doesn't create multiple DomainPinningPolicy for each
<domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="false">ABC.com</domain> <domain includeSubdomains="false">DEF.com</domain> <pin-set expiration="2018-09-10"> <pin digest="SHA-256">xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</pin> <pin digest="SHA-256">yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy</pin> </pin-set> </domain-config>

failed to connect on API 18 19

I followed the readme and it works fine on API 20+
However I'm getting following error when trying with API 18 and 19.
javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb8a5a5c0: Failure in SSL library, usually a protocol error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

The call works when I use sslSocketFactory(new TLSSocketFactory())

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Hi team,

I've seen this exception on our bug tracking for a while:

Caused by: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        at com.datatheorem.android.trustkit.pinning.OkHostnameVerifier.getSubjectAltNames(SourceFile:126)
        at com.datatheorem.android.trustkit.pinning.OkHostnameVerifier.verifyHostname(SourceFile:80)
        at com.datatheorem.android.trustkit.pinning.OkHostnameVerifier.verify(SourceFile:62)
        at com.datatheorem.android.trustkit.pinning.PinningTrustManager.checkServerTrusted(SourceFile:99)
        at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:182)
        at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:596)
        at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java:-2)
        at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
        at okhttp3.internal.connection.RealConnection.connectTls(SourceFile:281)
        at okhttp3.internal.connection.RealConnection.establishProtocol(SourceFile:251)
        at okhttp3.internal.connection.RealConnection.connect(SourceFile:151)
        at okhttp3.internal.connection.StreamAllocation.findConnection(SourceFile:192)
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(SourceFile:121)
        at okhttp3.internal.connection.StreamAllocation.newStream(SourceFile:100)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(SourceFile:42)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:67)
        at okhttp3.internal.cache.CacheInterceptor.intercept(SourceFile:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:67)
        at okhttp3.internal.http.BridgeInterceptor.intercept(SourceFile:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:92)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(SourceFile:120)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:67)

Unfortunately I don't have the steps to reproduce. We're using OkHttp 3.8.0 and TrustKit 1.0.1.

Thanks,

Check NetSecConfig was able to load the policy do not happen after Android N

I was looking at the library code and I noticed this check only run when the version is EXACTLY Android N. Shouldn't this be executed for later versions too? (>=)

// On Android N, ensure that the system was also able to load the policy
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
    // This will need to be updated/double-checked for subsequent versions of Android
    int systemConfigResId = getNetSecConfigResourceId(context);
    if (systemConfigResId == -1) {
        // Android did not find a policy because the supplied resource ID is wrong or the
        // policy file is not properly setup in the manifest, or contains bad data
        throw new ConfigurationException("TrustKit was initialized with a network policy " +
                "that was not properly configured for Android N - make sure it is in the " +
                "App's Manifest.");
    }
    else if (systemConfigResId != configurationResourceId) {
        throw new ConfigurationException("TrustKit was initialized with a different " +
                "network policy than the one configured in the App's manifest.");
    }
}

Am I missing something?

State Minimum OkHttp Version for the Readme Example

The example in the readme does not work with all versions of 3.x of OkHttp.

This is because the deprecated sslSocketFactory function takes 2 parameters in newer versions of OkHttp 3.x, but in older versions of 3.x, it still uses only 1 parameter which won't work.

https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.Builder.html#sslSocketFactory-javax.net.ssl.SSLSocketFactory-

Maybe we can update the example to state the minimum required OkHttp version?

API 16 support

We have app with min SDK is at 16. Can this work with API 16?

Domains under new gTLDs such as as .blog are rejected with "invalid domain" error

I performed all the steps in the documentation. I received a CA certificate for the application and included it in the application with custom SSLSocketFactory. However, I always get this error "Tried to pin unvalid domain". Where am I making a mistake?

network_security_config.xml

https://paste.ee/p/Hrj3E


CategoryFragment.java

https://paste.ee/p/Cd4To


MyApplication.java (only added TrustKit.initializeWithNetworkSecurityConfiguration(this);)

https://paste.ee/p/kx8Vo


and MyCustomSSLFactory.java

https://paste.ee/p/nlDhn

New trust manager/sslsocketfactory each request??

Is it necessary to create a new PinningTrustManager/SSLSocketFactory for every request(TrustKit.getInstance().getSSLSocketFactory(url)? Can't we build the static map by host name in TrustKit and reuse?

TrustKit has not been initialized

Describe the bug
I am setting this up in a react native application. For some reason it refuses to initialize and it gives me the error, "TrustKit has not been initialized".

To Reproduce
Create a React Native application and then add the following to MainActivity

    @Override
    public void onCreate(Bundle savedInstanceState) {
        try {
            TrustKit.initializeWithNetworkSecurityConfiguration(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
        OkHttpClientProvider.setOkHttpClientFactory(new CustomClientFactory());
        super.onCreate(savedInstanceState);
    }

My CustomClientFactory is

public class CustomClientFactory implements OkHttpClientFactory {
    private static String hostname = "*.your.service.com";

    @Override
    public OkHttpClient createNewNetworkModuleClient() {
        String hostname = null;
        try {
            hostname = new URL("https://hidden.com").getHost();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        OkHttpClient.Builder client = new OkHttpClient.Builder()
                .connectTimeout(0, TimeUnit.MILLISECONDS)
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .writeTimeout(0, TimeUnit.MILLISECONDS)
                .cookieJar(new ReactCookieJarContainer())
                .sslSocketFactory(TrustKit.getInstance().getSSLSocketFactory(hostname),TrustKit.getInstance().getTrustManager(hostname));
        return OkHttpClientProvider.enableTls12OnPreLollipop(client).build();
    }
}

My Build.gradle has the line implementation 'com.datatheorem.android.trustkit:trustkit:+'

My manifest has this as well

<application
      android:name=".MainApplication"
      android:networkSecurityConfig="@xml/network_security_config"
      ...

Expected behavior
I expect it to initialize

TrustKit configuration
In my network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">hidden.com</domain>
        <pin-set>
            <pin digest="SHA-256">AAAAAAQAAWDFSGSGSFSEFWEF=</pin> //hidden for security
        </pin-set>
        <trustkit-config enforcePinning="true">
        </trustkit-config>
    </domain-config>
</network-security-config>

App details:

  • App target SDK:26
  • App language: JS/React
  • Android version to reproduce the bug: All

Of note this is what I was following: https://medium.com/@jaedmuva/react-native-ssl-pinning-is-back-e317e6682642

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.