Added SslCertificate class and X509Certificate class and parser, updated ServerTrustChallenge class
This commit is contained in:
parent
b9d1482887
commit
6c2a60714e
CHANGELOG.md
android/src/main/java/com/pichillilorenzo/flutter_inappwebview
example
flutter_inappwebview.imlios/Classes
lib
@ -1,6 +1,6 @@
|
||||
## 3.4.0
|
||||
|
||||
- Added `requestFocusNodeHref`, `requestImageRef`, `getMetaTags`, `getMetaThemeColor`, `getScrollX`, `getScrollY` webview methods
|
||||
- Added `requestFocusNodeHref`, `requestImageRef`, `getMetaTags`, `getMetaThemeColor`, `getScrollX`, `getScrollY`, `getCertificate` webview methods
|
||||
- Added `WebStorage`, `LocalStorage` and `SessionStorage` class to manage `window.localStorage` and `window.sessionStorage` JavaScript [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)
|
||||
- Added `supportZoom` webview option also on iOS
|
||||
- Added `HttpOnly`, `SameSite` set cookie options
|
||||
@ -8,8 +8,8 @@
|
||||
- Added `animated` option to `scrollTo` and `scrollBy` webview methods
|
||||
- Added error and message to the `ServerTrustChallenge` class for iOS (class used by the `onReceivedServerTrustAuthRequest` event)
|
||||
- Added `contentInsetAdjustmentBehavior` webview iOS-specific option
|
||||
- Added `getCertificate` android-specific webview method
|
||||
- Added `copy` and `copyWithValue` methods for webview class options
|
||||
- Added X509Certificate class and parser
|
||||
- Fixed `zoomBy`, `setOptions` webview methods on Android
|
||||
- Fixed `databaseEnabled` android webview option default value to `true`
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
- Moved `supportZoom` webview option to cross-platform
|
||||
- `builtInZoomControls` android webview options changed default value to `true`
|
||||
- Updated `ServerTrustChallenge` class used by the `onReceivedServerTrustAuthRequest` event
|
||||
- The method `getOptions`could return null now
|
||||
- The method `getOptions` could return null now
|
||||
|
||||
## 3.3.0+3
|
||||
|
||||
|
@ -973,7 +973,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
|
||||
|
||||
public Map<String, Object> getCertificate() {
|
||||
if (webView != null)
|
||||
return webView.getSslCertificate();
|
||||
return webView.getCertificateMap();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -488,7 +488,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
|
||||
break;
|
||||
case "getCertificate":
|
||||
if (webView != null) {
|
||||
result.success(webView.getSslCertificate());
|
||||
result.success(webView.getCertificateMap());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
|
@ -1864,108 +1864,56 @@ final public class InAppWebView extends InputAwareWebView {
|
||||
return obj;
|
||||
}
|
||||
|
||||
public Map<String, Object> getSslCertificate() {
|
||||
SslCertificate sslCertificate = getCertificate();
|
||||
public Map<String, Object> getCertificateMap() {
|
||||
return InAppWebView.getCertificateMap(getCertificate());
|
||||
}
|
||||
|
||||
SslCertificate.DName issuedByName = sslCertificate.getIssuedBy();
|
||||
Map<String, Object> issuedBy = new HashMap<>();
|
||||
issuedBy.put("CName", issuedByName.getCName());
|
||||
issuedBy.put("DName", issuedByName.getDName());
|
||||
issuedBy.put("OName", issuedByName.getOName());
|
||||
issuedBy.put("UName", issuedByName.getUName());
|
||||
public static Map<String, Object> getCertificateMap(SslCertificate sslCertificate) {
|
||||
if (sslCertificate != null) {
|
||||
SslCertificate.DName issuedByName = sslCertificate.getIssuedBy();
|
||||
Map<String, Object> issuedBy = new HashMap<>();
|
||||
issuedBy.put("CName", issuedByName.getCName());
|
||||
issuedBy.put("DName", issuedByName.getDName());
|
||||
issuedBy.put("OName", issuedByName.getOName());
|
||||
issuedBy.put("UName", issuedByName.getUName());
|
||||
|
||||
SslCertificate.DName issuedToName = sslCertificate.getIssuedTo();
|
||||
Map<String, Object> issuedTo = new HashMap<>();
|
||||
issuedTo.put("CName", issuedToName.getCName());
|
||||
issuedTo.put("DName", issuedToName.getDName());
|
||||
issuedTo.put("OName", issuedToName.getOName());
|
||||
issuedTo.put("UName", issuedToName.getUName());
|
||||
SslCertificate.DName issuedToName = sslCertificate.getIssuedTo();
|
||||
Map<String, Object> issuedTo = new HashMap<>();
|
||||
issuedTo.put("CName", issuedToName.getCName());
|
||||
issuedTo.put("DName", issuedToName.getDName());
|
||||
issuedTo.put("OName", issuedToName.getOName());
|
||||
issuedTo.put("UName", issuedToName.getUName());
|
||||
|
||||
Map<String, Object> x509CertificateMap = new HashMap<>();
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||
X509Certificate x509Certificate = sslCertificate.getX509Certificate();
|
||||
if (x509Certificate != null) {
|
||||
x509CertificateMap.put("basicConstraints", x509Certificate.getBasicConstraints());
|
||||
byte[] x509CertificateData = null;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
try {
|
||||
x509CertificateMap.put("extendedKeyUsage", x509Certificate.getExtendedKeyUsage());
|
||||
} catch (CertificateParsingException e) {
|
||||
x509CertificateMap.put("extendedKeyUsage", null);
|
||||
}
|
||||
|
||||
Map<String, Object> issuerDN = new HashMap<>();
|
||||
issuerDN.put("name", x509Certificate.getIssuerDN().getName());
|
||||
x509CertificateMap.put("issuerDN", issuerDN);
|
||||
|
||||
x509CertificateMap.put("issuerUniqueID", x509Certificate.getIssuerUniqueID());
|
||||
|
||||
Map<String, Object> issuerX500Principal = new HashMap<>();
|
||||
issuerX500Principal.put("name", x509Certificate.getIssuerX500Principal().getName());
|
||||
issuerX500Principal.put("encoded", x509Certificate.getIssuerX500Principal().getEncoded());
|
||||
x509CertificateMap.put("issuerX500Principal", issuerX500Principal);
|
||||
|
||||
x509CertificateMap.put("keyUsage", x509Certificate.getKeyUsage());
|
||||
x509CertificateMap.put("notAfter", x509Certificate.getNotAfter().getTime());
|
||||
x509CertificateMap.put("notBefore", x509Certificate.getNotBefore().getTime());
|
||||
x509CertificateMap.put("serialNumber", x509Certificate.getSerialNumber().longValue());
|
||||
x509CertificateMap.put("sigAlgName", x509Certificate.getSigAlgName());
|
||||
x509CertificateMap.put("sigAlgOID", x509Certificate.getSigAlgOID());
|
||||
x509CertificateMap.put("sigAlgParams", x509Certificate.getSigAlgParams());
|
||||
x509CertificateMap.put("signature", x509Certificate.getSignature());
|
||||
|
||||
Map<String, Object> subjectDN = new HashMap<>();
|
||||
subjectDN.put("name", x509Certificate.getSubjectDN().getName());
|
||||
x509CertificateMap.put("subjectDN", subjectDN);
|
||||
|
||||
x509CertificateMap.put("subjectUniqueID", x509Certificate.getSubjectUniqueID());
|
||||
|
||||
Map<String, Object> subjectX500Principal = new HashMap<>();
|
||||
subjectX500Principal.put("name", x509Certificate.getSubjectX500Principal().getName());
|
||||
subjectX500Principal.put("encoded", x509Certificate.getSubjectX500Principal().getEncoded());
|
||||
x509CertificateMap.put("subjectX500Principal", subjectX500Principal);
|
||||
|
||||
try {
|
||||
x509CertificateMap.put("TBSCertificate", x509Certificate.getTBSCertificate());
|
||||
X509Certificate certificate = sslCertificate.getX509Certificate();
|
||||
if (certificate != null) {
|
||||
x509CertificateData = certificate.getEncoded();
|
||||
}
|
||||
} catch (CertificateEncodingException e) {
|
||||
x509CertificateMap.put("TBSCertificate", null);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
x509CertificateMap.put("version", x509Certificate.getVersion());
|
||||
x509CertificateMap.put("criticalExtensionOIDs", x509Certificate.getCriticalExtensionOIDs());
|
||||
x509CertificateMap.put("nonCriticalExtensionOIDs", x509Certificate.getNonCriticalExtensionOIDs());
|
||||
} else {
|
||||
try {
|
||||
x509CertificateMap.put("encoded", x509Certificate.getEncoded());
|
||||
x509CertificateData = Util.getX509CertFromSslCertHack(sslCertificate).getEncoded();
|
||||
} catch (CertificateEncodingException e) {
|
||||
x509CertificateMap.put("encoded", null);
|
||||
}
|
||||
|
||||
Map<String, Object> publicKey = new HashMap<>();
|
||||
publicKey.put("algorithm", x509Certificate.getPublicKey().getAlgorithm());
|
||||
publicKey.put("encoded", x509Certificate.getPublicKey().getEncoded());
|
||||
publicKey.put("format", x509Certificate.getPublicKey().getFormat());
|
||||
x509CertificateMap.put("publicKey", publicKey);
|
||||
|
||||
x509CertificateMap.put("type", x509Certificate.getType());
|
||||
x509CertificateMap.put("hasUnsupportedCriticalExtension", x509Certificate.hasUnsupportedCriticalExtension());
|
||||
|
||||
try {
|
||||
x509Certificate.checkValidity();
|
||||
x509CertificateMap.put("valid", true);
|
||||
} catch (CertificateExpiredException e) {
|
||||
x509CertificateMap.put("valid", false);
|
||||
} catch (CertificateNotYetValidException e) {
|
||||
x509CertificateMap.put("valid", false);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
obj.put("issuedBy", issuedBy);
|
||||
obj.put("issuedTo", issuedTo);
|
||||
obj.put("validNotAfterDate", sslCertificate.getValidNotAfterDate().getTime());
|
||||
obj.put("validNotBeforeDate", sslCertificate.getValidNotBeforeDate().getTime());
|
||||
obj.put("x509Certificate", x509CertificateData);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
obj.put("issuedBy", issuedBy);
|
||||
obj.put("issuedTo", issuedTo);
|
||||
obj.put("validNotAfterDate", sslCertificate.getValidNotAfterDate().getTime());
|
||||
obj.put("validNotBeforeDate", sslCertificate.getValidNotBeforeDate().getTime());
|
||||
obj.put("x509Certificate", x509CertificateMap);
|
||||
|
||||
return obj;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
48
android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewClient.java
48
android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewClient.java
@ -301,7 +301,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
url = new URL(view.getUrl());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
|
||||
credentialsProposed = null;
|
||||
previousAuthRequestFailureCount = 0;
|
||||
@ -378,32 +377,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SslCertificate class does not has a public getter for the underlying
|
||||
* X509Certificate, we can only do this by hack. This only works for andorid 4.0+
|
||||
* https://groups.google.com/forum/#!topic/android-developers/eAPJ6b7mrmg
|
||||
*/
|
||||
public static X509Certificate getX509CertFromSslCertHack(SslCertificate sslCert) {
|
||||
X509Certificate x509Certificate = null;
|
||||
|
||||
Bundle bundle = SslCertificate.saveState(sslCert);
|
||||
byte[] bytes = bundle.getByteArray("x509-certificate");
|
||||
|
||||
if (bytes == null) {
|
||||
x509Certificate = null;
|
||||
} else {
|
||||
try {
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
|
||||
x509Certificate = (X509Certificate) cert;
|
||||
} catch (CertificateException e) {
|
||||
x509Certificate = null;
|
||||
}
|
||||
}
|
||||
|
||||
return x509Certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) {
|
||||
URL url;
|
||||
@ -411,7 +384,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
url = new URL(error.getUrl());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
handler.cancel();
|
||||
return;
|
||||
}
|
||||
@ -430,19 +402,7 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
obj.put("port", port);
|
||||
obj.put("androidError", error.getPrimaryError());
|
||||
obj.put("iosError", null);
|
||||
obj.put("serverCertificate", null);
|
||||
try {
|
||||
X509Certificate certificate;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
certificate = error.getCertificate().getX509Certificate();
|
||||
} else {
|
||||
certificate = getX509CertFromSslCertHack(error.getCertificate());
|
||||
}
|
||||
obj.put("serverCertificate", certificate.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG,e.getLocalizedMessage());
|
||||
}
|
||||
obj.put("sslCertificate", InAppWebView.getCertificateMap(error.getCertificate()));
|
||||
|
||||
String message;
|
||||
switch (error.getPrimaryError()) {
|
||||
@ -511,7 +471,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
url = new URL(view.getUrl());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
@ -658,7 +617,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
uri = new URI(scheme, tempUrl.getUserInfo(), tempUrl.getHost(), tempUrl.getPort(), tempUrl.getPath(), tempUrl.getQuery(), tempUrl.getRef());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.d(LOG_TAG, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -677,7 +635,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
flutterResult = Util.invokeMethodAndWait(channel, "onLoadResourceCustomScheme", obj);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -691,7 +648,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
response = webView.contentBlockerHandler.checkUrl(webView, url, res.get("content-type").toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
}
|
||||
if (response != null)
|
||||
return response;
|
||||
@ -706,7 +662,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
response = webView.contentBlockerHandler.checkUrl(webView, url);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
return response;
|
||||
@ -763,7 +718,6 @@ public class InAppWebViewClient extends WebViewClient {
|
||||
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.pichillilorenzo.flutter_inappwebview;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.http.SslCertificate;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
@ -8,6 +9,7 @@ import android.os.Looper;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
@ -16,6 +18,7 @@ import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
@ -205,4 +208,30 @@ public class Util {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SslCertificate class does not has a public getter for the underlying
|
||||
* X509Certificate, we can only do this by hack. This only works for andorid 4.0+
|
||||
* https://groups.google.com/forum/#!topic/android-developers/eAPJ6b7mrmg
|
||||
*/
|
||||
public static X509Certificate getX509CertFromSslCertHack(SslCertificate sslCert) {
|
||||
X509Certificate x509Certificate = null;
|
||||
|
||||
Bundle bundle = SslCertificate.saveState(sslCert);
|
||||
byte[] bytes = bundle.getByteArray("x509-certificate");
|
||||
|
||||
if (bytes == null) {
|
||||
x509Certificate = null;
|
||||
} else {
|
||||
try {
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
|
||||
x509Certificate = (X509Certificate) cert;
|
||||
} catch (CertificateException e) {
|
||||
x509Certificate = null;
|
||||
}
|
||||
}
|
||||
|
||||
return x509Certificate;
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-06-13 01:39:11.912485","version":"1.17.1"}
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-06-14 18:36:37.641339","version":"1.17.1"}
|
@ -1,3 +1,4 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -107,7 +108,6 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||
setState(() {
|
||||
this.url = url;
|
||||
});
|
||||
|
||||
},
|
||||
onProgressChanged: (InAppWebViewController controller, int progress) {
|
||||
setState(() {
|
||||
|
@ -26,7 +26,6 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/build" />
|
||||
|
@ -491,6 +491,13 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getCertificate":
|
||||
if webView != nil {
|
||||
result(webView!.getCertificateMap())
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
|
@ -328,6 +328,55 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "requestFocusNodeHref":
|
||||
if webView != nil {
|
||||
webView!.requestFocusNodeHref { (value, error) in
|
||||
if let err = error {
|
||||
print(err.localizedDescription)
|
||||
result(nil)
|
||||
return
|
||||
}
|
||||
result(value)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "requestImageRef":
|
||||
if webView != nil {
|
||||
webView!.requestImageRef { (value, error) in
|
||||
if let err = error {
|
||||
print(err.localizedDescription)
|
||||
result(nil)
|
||||
return
|
||||
}
|
||||
result(value)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getScrollX":
|
||||
if webView != nil {
|
||||
result(Int(webView!.scrollView.contentOffset.x))
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getScrollY":
|
||||
if webView != nil {
|
||||
result(Int(webView!.scrollView.contentOffset.y))
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getCertificate":
|
||||
if webView != nil {
|
||||
result(webView!.getCertificateMap())
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
|
@ -799,6 +799,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||
var channel: FlutterMethodChannel?
|
||||
var options: InAppWebViewOptions?
|
||||
var currentURL: URL?
|
||||
var x509CertificateData: Data?
|
||||
static var sslCertificateMap: [String: Data] = [:] // [URL host name : x509Certificate Data]
|
||||
var startPageTime: Int64 = 0
|
||||
static var credentialsProposed: [URLCredential] = []
|
||||
var lastScrollX: CGFloat = 0
|
||||
@ -1863,6 +1865,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||
}
|
||||
|
||||
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||
self.x509CertificateData = nil
|
||||
|
||||
self.startPageTime = currentTimeInMilliSeconds()
|
||||
onLoadStart(url: (currentURL?.absoluteString)!)
|
||||
|
||||
@ -2557,6 +2561,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||
let data = CFDataGetBytePtr(serverCertificateCFData)
|
||||
let size = CFDataGetLength(serverCertificateCFData)
|
||||
serverCertificateData = NSData(bytes: data, length: size)
|
||||
if (x509CertificateData == nil) {
|
||||
x509CertificateData = Data(serverCertificateData!)
|
||||
InAppWebView.sslCertificateMap[challenge.protectionSpace.host] = x509CertificateData;
|
||||
}
|
||||
}
|
||||
|
||||
let error = secResult != SecTrustResultType.proceed ? secResult.rawValue : nil
|
||||
@ -2591,7 +2599,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||
"realm": challenge.protectionSpace.realm,
|
||||
"port": challenge.protectionSpace.port,
|
||||
"previousFailureCount": challenge.previousFailureCount,
|
||||
"serverCertificate": serverCertificateData,
|
||||
"sslCertificate": InAppWebView.getCertificateMap(x509Certificate:
|
||||
((serverCertificateData != nil) ? Data(serverCertificateData!) : nil)),
|
||||
"androidError": nil,
|
||||
"iosError": error,
|
||||
"message": message,
|
||||
@ -2892,6 +2901,29 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||
self.scrollView.subviews.first?.resignFirstResponder()
|
||||
}
|
||||
|
||||
public func getCertificate() -> Data? {
|
||||
var x509Certificate = self.x509CertificateData
|
||||
if x509Certificate == nil, let scheme = url?.scheme, scheme == "https",
|
||||
let host = url?.host, let cert = InAppWebView.sslCertificateMap[host] {
|
||||
x509Certificate = cert
|
||||
}
|
||||
return x509Certificate
|
||||
}
|
||||
|
||||
public func getCertificateMap() -> [String: Any?]? {
|
||||
return InAppWebView.getCertificateMap(x509Certificate: getCertificate())
|
||||
}
|
||||
|
||||
public static func getCertificateMap(x509Certificate: Data?) -> [String: Any?]? {
|
||||
return x509Certificate != nil ? [
|
||||
"issuedBy": nil,
|
||||
"issuedTo": nil,
|
||||
"validNotAfterDate": nil,
|
||||
"validNotBeforeDate": nil,
|
||||
"x509Certificate": x509Certificate
|
||||
] : nil;
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
stopLoading()
|
||||
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog")
|
||||
|
@ -37,3 +37,4 @@ export 'src/http_auth_credentials_database.dart';
|
||||
export 'src/web_storage_manager.dart';
|
||||
export 'src/context_menu.dart';
|
||||
export 'src/web_storage.dart';
|
||||
export 'src/X509Certificate/main.dart';
|
433
lib/src/X509Certificate/asn1_decoder.dart
Normal file
433
lib/src/X509Certificate/asn1_decoder.dart
Normal file
@ -0,0 +1,433 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'asn1_identifier.dart';
|
||||
import 'asn1_object.dart';
|
||||
|
||||
class ASN1DERDecoder {
|
||||
static List<ASN1Object> decode({@required List<int> data}) {
|
||||
var iterator = data.iterator;
|
||||
return parse(iterator: iterator);
|
||||
}
|
||||
|
||||
static List<ASN1Object> parse({@required Iterator<int> iterator}) {
|
||||
var result = <ASN1Object>[];
|
||||
|
||||
while (iterator.moveNext()) {
|
||||
var nextValue = iterator.current;
|
||||
|
||||
var asn1obj = ASN1Object();
|
||||
asn1obj.identifier = ASN1Identifier(nextValue);
|
||||
|
||||
if (asn1obj.identifier.isConstructed()) {
|
||||
|
||||
var contentData = loadSubContent(iterator: iterator);
|
||||
|
||||
if (contentData.isEmpty) {
|
||||
asn1obj.sub = parse(iterator: iterator);
|
||||
} else {
|
||||
var subIterator = contentData.iterator;
|
||||
asn1obj.sub = parse(iterator: subIterator);
|
||||
}
|
||||
|
||||
asn1obj.value = null;
|
||||
|
||||
asn1obj.encoded = Uint8List.fromList(contentData);
|
||||
|
||||
for (var item in asn1obj.sub) {
|
||||
item.parent = asn1obj;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (asn1obj.identifier.typeClass() == ASN1IdentifierClass.UNIVERSAL) {
|
||||
|
||||
var contentData = loadSubContent(iterator: iterator);
|
||||
|
||||
asn1obj.encoded = Uint8List.fromList(contentData);
|
||||
|
||||
// decode the content data with come more convenient format
|
||||
|
||||
var tagNumber = asn1obj.identifier.tagNumber();
|
||||
|
||||
if (tagNumber == ASN1IdentifierTagNumber.END_OF_CONTENT) {
|
||||
return result;
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.BOOLEAN) {
|
||||
var value = contentData.length > 0 ? contentData.first : null;
|
||||
if (value != null) {
|
||||
asn1obj.value = value > 0 ? true : false;
|
||||
}
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.INTEGER) {
|
||||
while (contentData.length > 0 && contentData.first == 0) {
|
||||
contentData.removeAt(0); // remove not significant digit
|
||||
}
|
||||
asn1obj.value = contentData;
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.NULL) {
|
||||
asn1obj.value = null;
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.OBJECT_IDENTIFIER) {
|
||||
asn1obj.value = decodeOid(contentData: contentData);
|
||||
}
|
||||
else if ([
|
||||
ASN1IdentifierTagNumber.UTF8_STRING,
|
||||
ASN1IdentifierTagNumber.PRINTABLE_STRING,
|
||||
ASN1IdentifierTagNumber.NUMERIC_STRING,
|
||||
ASN1IdentifierTagNumber.GENERAL_STRING,
|
||||
ASN1IdentifierTagNumber.UNIVERSAL_STRING,
|
||||
ASN1IdentifierTagNumber.CHARACTER_STRING,
|
||||
ASN1IdentifierTagNumber.T61_STRING].contains(tagNumber)) {
|
||||
asn1obj.value = utf8.decode(contentData, allowMalformed: true);
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.BMP_STRING) {
|
||||
asn1obj.value = String.fromCharCodes(contentData);
|
||||
}
|
||||
else if ([
|
||||
ASN1IdentifierTagNumber.VISIBLE_STRING,
|
||||
ASN1IdentifierTagNumber.IA5_STRING
|
||||
].contains(tagNumber)) {
|
||||
asn1obj.value = ascii.decode(contentData, allowInvalid: true);
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.UTC_TIME) {
|
||||
asn1obj.value = utcTimeToDate(contentData: contentData);
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.GENERALIZED_TIME) {
|
||||
asn1obj.value = generalizedTimeToDate(contentData: contentData);
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.BIT_STRING) {
|
||||
if (contentData.length > 0) {
|
||||
contentData.removeAt(0); // unused bits
|
||||
}
|
||||
asn1obj.value = contentData;
|
||||
}
|
||||
else if (tagNumber == ASN1IdentifierTagNumber.OCTET_STRING) {
|
||||
try {
|
||||
var subIterator = contentData.iterator;
|
||||
asn1obj.sub = parse(iterator: subIterator);
|
||||
} catch (e) {
|
||||
|
||||
var str;
|
||||
try {
|
||||
str = utf8.decode(contentData);
|
||||
} catch(e) {}
|
||||
if (str != null) {
|
||||
asn1obj.value = str;
|
||||
} else {
|
||||
asn1obj.value = contentData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
// print("unsupported tag: ${asn1obj.identifier.tagNumber()}");
|
||||
asn1obj.value = contentData;
|
||||
}
|
||||
} else {
|
||||
// custom/private tag
|
||||
|
||||
var contentData = loadSubContent(iterator: iterator);
|
||||
|
||||
var str;
|
||||
try {
|
||||
str = utf8.decode(contentData);
|
||||
} catch(e) {}
|
||||
if (str != null) {
|
||||
asn1obj.value = str;
|
||||
} else {
|
||||
asn1obj.value = contentData;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.add(asn1obj);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BigInt getContentLength({@required Iterator<int> iterator}) {
|
||||
if (iterator.moveNext()) {
|
||||
var first = iterator.current;
|
||||
if (first != null) {
|
||||
|
||||
if ((first & 0x80) != 0) { // long
|
||||
|
||||
var octetsToRead = first - 0x80;
|
||||
var data = <int>[];
|
||||
for (var i = 0; i < octetsToRead; i++) {
|
||||
if (iterator.moveNext()) {
|
||||
var n = iterator.current;
|
||||
if (n != null) {
|
||||
data.add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return toIntValue(data) ?? BigInt.from(0);
|
||||
|
||||
} else { // short
|
||||
return BigInt.from(first);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return BigInt.from(0);
|
||||
}
|
||||
|
||||
static List<int> loadSubContent({@required Iterator<int> iterator}) {
|
||||
var len = getContentLength(iterator: iterator);
|
||||
int int64MaxValue = 9223372036854775807;
|
||||
|
||||
if (len >= BigInt.from(int64MaxValue)) {
|
||||
return <int>[];
|
||||
}
|
||||
|
||||
var byteArray = <int>[];
|
||||
|
||||
for (var i = 0; i < len.toInt(); i++) {
|
||||
if (iterator.moveNext()) {
|
||||
var n = iterator.current;
|
||||
if (n != null) {
|
||||
byteArray.add(n);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
// throw ASN1OutOfBufferError();
|
||||
}
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
/// Decode DER OID bytes to String with dot notation
|
||||
static String decodeOid({@required List<int> contentData}) {
|
||||
if (contentData.isEmpty) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var oid = "";
|
||||
|
||||
var first = contentData.removeAt(0);
|
||||
oid += "${(first / 40).truncate()}.${first % 40}";
|
||||
|
||||
var t = 0;
|
||||
while (contentData.length > 0) {
|
||||
var n = contentData.removeAt(0);
|
||||
t = (t << 7) | (n & 0x7F);
|
||||
if ((n & 0x80) == 0) {
|
||||
oid += ".$t";
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
return oid;
|
||||
}
|
||||
|
||||
///Converts a UTCTime value to a date.
|
||||
///
|
||||
///Note: GeneralizedTime has 4 digits for the year and is used for X.509
|
||||
///dates past 2049. Parsing that structure hasn't been implemented yet.
|
||||
///
|
||||
///[contentData] the UTCTime value to convert.
|
||||
static DateTime utcTimeToDate({@required List<int> contentData}) {
|
||||
/* The following formats can be used:
|
||||
YYMMDDhhmmZ
|
||||
YYMMDDhhmm+hh'mm'
|
||||
YYMMDDhhmm-hh'mm'
|
||||
YYMMDDhhmmssZ
|
||||
YYMMDDhhmmss+hh'mm'
|
||||
YYMMDDhhmmss-hh'mm'
|
||||
Where:
|
||||
YY is the least significant two digits of the year
|
||||
MM is the month (01 to 12)
|
||||
DD is the day (01 to 31)
|
||||
hh is the hour (00 to 23)
|
||||
mm are the minutes (00 to 59)
|
||||
ss are the seconds (00 to 59)
|
||||
Z indicates that local time is GMT, + indicates that local time is
|
||||
later than GMT, and - indicates that local time is earlier than GMT
|
||||
hh' is the absolute value of the offset from GMT in hours
|
||||
mm' is the absolute value of the offset from GMT in minutes */
|
||||
|
||||
String utc;
|
||||
try {
|
||||
utc = utf8.decode(contentData);
|
||||
} catch(e) {}
|
||||
if (utc == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if YY >= 50 use 19xx, if YY < 50 use 20xx
|
||||
var year = int.parse(utc.substring(0, 2), radix: 10);
|
||||
year = (year >= 50) ? 1900 + year : 2000 + year;
|
||||
var MM = int.parse(utc.substring(2, 4), radix: 10);
|
||||
var DD = int.parse(utc.substring(4, 6), radix: 10);
|
||||
var hh = int.parse(utc.substring(6, 8), radix: 10);
|
||||
var mm = int.parse(utc.substring(8, 10), radix: 10);
|
||||
var ss = 0;
|
||||
|
||||
int end;
|
||||
String c;
|
||||
// not just YYMMDDhhmmZ
|
||||
if(utc.length > 11) {
|
||||
// get character after minutes
|
||||
c = utc[10];
|
||||
end = 10;
|
||||
|
||||
// see if seconds are present
|
||||
if(c != '+' && c != '-') {
|
||||
// get seconds
|
||||
ss = int.parse(utc.substring(10, 12), radix: 10);
|
||||
end += 2;
|
||||
}
|
||||
}
|
||||
|
||||
var date = DateTime.utc(
|
||||
year,
|
||||
MM,
|
||||
DD,
|
||||
hh,
|
||||
mm,
|
||||
ss,
|
||||
0
|
||||
);
|
||||
|
||||
if(end != null) {
|
||||
// get +/- after end of time
|
||||
c = utc[end];
|
||||
if(c == '+' || c == '-') {
|
||||
// get hours+minutes offset
|
||||
var hhoffset = int.parse(utc.substring(end + 1, end + 1 + 2), radix: 10);
|
||||
var mmoffset = int.parse(utc.substring(end + 4, end + 4 + 2), radix: 10);
|
||||
|
||||
// calculate offset in milliseconds
|
||||
var offset = hhoffset * 60 + mmoffset;
|
||||
offset *= 60000;
|
||||
|
||||
var offsetDuration = Duration(milliseconds: offset);
|
||||
// apply offset
|
||||
if(c == '+') {
|
||||
date.subtract(offsetDuration);
|
||||
} else {
|
||||
date.add(offsetDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
///Converts a GeneralizedTime value to a date.
|
||||
///
|
||||
///[contentData] the GeneralizedTime value to convert.
|
||||
static DateTime generalizedTimeToDate({@required List<int> contentData}) {
|
||||
/* The following formats can be used:
|
||||
YYYYMMDDHHMMSS
|
||||
YYYYMMDDHHMMSS.fff
|
||||
YYYYMMDDHHMMSSZ
|
||||
YYYYMMDDHHMMSS.fffZ
|
||||
YYYYMMDDHHMMSS+hh'mm'
|
||||
YYYYMMDDHHMMSS.fff+hh'mm'
|
||||
YYYYMMDDHHMMSS-hh'mm'
|
||||
YYYYMMDDHHMMSS.fff-hh'mm'
|
||||
Where:
|
||||
YYYY is the year
|
||||
MM is the month (01 to 12)
|
||||
DD is the day (01 to 31)
|
||||
hh is the hour (00 to 23)
|
||||
mm are the minutes (00 to 59)
|
||||
ss are the seconds (00 to 59)
|
||||
.fff is the second fraction, accurate to three decimal places
|
||||
Z indicates that local time is GMT, + indicates that local time is
|
||||
later than GMT, and - indicates that local time is earlier than GMT
|
||||
hh' is the absolute value of the offset from GMT in hours
|
||||
mm' is the absolute value of the offset from GMT in minutes */
|
||||
|
||||
String gentime;
|
||||
try {
|
||||
gentime = utf8.decode(contentData);
|
||||
} catch(e) {}
|
||||
if (gentime == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if YY >= 50 use 19xx, if YY < 50 use 20xx
|
||||
var YYYY = int.parse(gentime.substring(0, 4), radix: 10);
|
||||
var MM = int.parse(gentime.substring(4, 6), radix: 10);
|
||||
var DD = int.parse(gentime.substring(6, 8), radix: 10);
|
||||
var hh = int.parse(gentime.substring(8, 10), radix: 10);
|
||||
var mm = int.parse(gentime.substring(10, 12), radix: 10);
|
||||
var ss = int.parse(gentime.substring(12, 14), radix: 10);
|
||||
|
||||
double fff = 0.0;
|
||||
var offset = 0;
|
||||
var isUTC = false;
|
||||
|
||||
if(gentime[gentime.length - 1] == 'Z') {
|
||||
isUTC = true;
|
||||
}
|
||||
|
||||
var end = gentime.length - 5;
|
||||
var c = gentime[end];
|
||||
if(c == '+' || c == '-') {
|
||||
// get hours+minutes offset
|
||||
var hhoffset = int.parse(gentime.substring(end + 1, end + 1 + 2), radix: 10);
|
||||
var mmoffset = int.parse(gentime.substring(end + 4, end + 4 + 2), radix: 10);
|
||||
|
||||
// calculate offset in milliseconds
|
||||
offset = hhoffset * 60 + mmoffset;
|
||||
offset *= 60000;
|
||||
|
||||
// apply offset
|
||||
if(c == '+') {
|
||||
offset *= -1;
|
||||
}
|
||||
|
||||
isUTC = true;
|
||||
}
|
||||
|
||||
// check for second fraction
|
||||
if(gentime[14] == '.') {
|
||||
fff = double.parse(gentime.substring(14)) * 1000;
|
||||
}
|
||||
|
||||
var date = DateTime.utc(
|
||||
YYYY,
|
||||
MM,
|
||||
DD,
|
||||
hh,
|
||||
mm,
|
||||
ss,
|
||||
fff.toInt()
|
||||
);
|
||||
|
||||
if(isUTC) {
|
||||
var offsetDuration = Duration(milliseconds: offset);
|
||||
date.add(offsetDuration);
|
||||
}
|
||||
|
||||
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
BigInt toIntValue(List<int> data) {
|
||||
if (data.length > 8) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BigInt value = BigInt.from(0);
|
||||
for (var index = 0; index < data.length; index++) {
|
||||
var byte = data[index];
|
||||
value += BigInt.from(byte << 8*(data.length-index-1));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
class ASN1OutOfBufferError extends Error {
|
||||
|
||||
}
|
||||
|
||||
class ASN1ParseError extends Error {
|
||||
|
||||
}
|
50
lib/src/X509Certificate/asn1_distinguished_names.dart
Normal file
50
lib/src/X509Certificate/asn1_distinguished_names.dart
Normal file
@ -0,0 +1,50 @@
|
||||
class ASN1DistinguishedNames {
|
||||
final String _oid;
|
||||
final String _representation;
|
||||
|
||||
const ASN1DistinguishedNames._internal(this._oid, this._representation);
|
||||
|
||||
static List<ASN1DistinguishedNames> values = [
|
||||
ASN1DistinguishedNames.COMMON_NAME,
|
||||
ASN1DistinguishedNames.DN_QUALIFIER,
|
||||
ASN1DistinguishedNames.SERIAL_NUMBER,
|
||||
ASN1DistinguishedNames.GIVEN_NAME,
|
||||
ASN1DistinguishedNames.SURNAME,
|
||||
ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME,
|
||||
ASN1DistinguishedNames.ORGANIZATION_NAME,
|
||||
ASN1DistinguishedNames.STREET_ADDRESS,
|
||||
ASN1DistinguishedNames.LOCALITY_NAME,
|
||||
ASN1DistinguishedNames.STATE_OR_PROVINCE_NAME,
|
||||
ASN1DistinguishedNames.COUNTRY_NAME,
|
||||
ASN1DistinguishedNames.EMAIL,
|
||||
];
|
||||
|
||||
static ASN1DistinguishedNames fromValue(String oid) {
|
||||
return ASN1DistinguishedNames.values.firstWhere((element) => element.oid() == oid, orElse: () => null);
|
||||
}
|
||||
|
||||
String oid() => _oid;
|
||||
|
||||
String representation() => _representation;
|
||||
|
||||
@override
|
||||
String toString() => "($_oid, $_representation)";
|
||||
|
||||
static const COMMON_NAME = const ASN1DistinguishedNames._internal("2.5.4.3", "CN");
|
||||
static const DN_QUALIFIER = const ASN1DistinguishedNames._internal("2.5.4.46", "DNQ");
|
||||
static const SERIAL_NUMBER = const ASN1DistinguishedNames._internal("2.5.4.5", "SERIALNUMBER");
|
||||
static const GIVEN_NAME = const ASN1DistinguishedNames._internal("2.5.4.42", "GIVENNAME");
|
||||
static const SURNAME = const ASN1DistinguishedNames._internal("2.5.4.4", "SURNAME");
|
||||
static const ORGANIZATIONAL_UNIT_NAME = const ASN1DistinguishedNames._internal("2.5.4.11", "OU");
|
||||
static const ORGANIZATION_NAME = const ASN1DistinguishedNames._internal("2.5.4.10", "O");
|
||||
static const STREET_ADDRESS = const ASN1DistinguishedNames._internal("2.5.4.9", "STREET");
|
||||
static const LOCALITY_NAME = const ASN1DistinguishedNames._internal("2.5.4.7", "L");
|
||||
static const STATE_OR_PROVINCE_NAME = const ASN1DistinguishedNames._internal("2.5.4.8", "ST");
|
||||
static const COUNTRY_NAME = const ASN1DistinguishedNames._internal("2.5.4.6", "C");
|
||||
static const EMAIL = const ASN1DistinguishedNames._internal("1.2.840.113549.1.9.1", "E");
|
||||
|
||||
bool operator ==(value) => value == _oid;
|
||||
|
||||
@override
|
||||
int get hashCode => _oid.hashCode;
|
||||
}
|
229
lib/src/X509Certificate/asn1_identifier.dart
Normal file
229
lib/src/X509Certificate/asn1_identifier.dart
Normal file
@ -0,0 +1,229 @@
|
||||
class ASN1IdentifierClass {
|
||||
final int _value;
|
||||
|
||||
const ASN1IdentifierClass._internal(this._value);
|
||||
|
||||
static List<ASN1IdentifierClass> values = [
|
||||
ASN1IdentifierClass.UNIVERSAL,
|
||||
ASN1IdentifierClass.APPLICATION,
|
||||
ASN1IdentifierClass.CONTEXT_SPECIFIC,
|
||||
ASN1IdentifierClass.PRIVATE,
|
||||
];
|
||||
|
||||
static ASN1IdentifierClass fromValue(int value) {
|
||||
if (value != null)
|
||||
return ASN1IdentifierClass.values.firstWhere((element) => element.toValue() == value, orElse: () => null);
|
||||
return null;
|
||||
}
|
||||
|
||||
int toValue() => _value;
|
||||
|
||||
static const UNIVERSAL = const ASN1IdentifierClass._internal(0x00);
|
||||
static const APPLICATION = const ASN1IdentifierClass._internal(0x40);
|
||||
static const CONTEXT_SPECIFIC = const ASN1IdentifierClass._internal(0x80);
|
||||
static const PRIVATE = const ASN1IdentifierClass._internal(0xC0);
|
||||
|
||||
String toString() {
|
||||
switch (this.toValue()) {
|
||||
case 0x00:
|
||||
return "UNIVERSAL";
|
||||
case 0x40:
|
||||
return "APPLICATION";
|
||||
case 0x80:
|
||||
return "CONTEXT_SPECIFIC";
|
||||
case 0xC0:
|
||||
return "PRIVATE";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
||||
|
||||
class ASN1IdentifierTagNumber {
|
||||
final int _value;
|
||||
|
||||
const ASN1IdentifierTagNumber._internal(this._value);
|
||||
|
||||
static List<ASN1IdentifierTagNumber> values = [
|
||||
ASN1IdentifierTagNumber.END_OF_CONTENT,
|
||||
ASN1IdentifierTagNumber.BOOLEAN,
|
||||
ASN1IdentifierTagNumber.INTEGER,
|
||||
ASN1IdentifierTagNumber.BIT_STRING,
|
||||
ASN1IdentifierTagNumber.OCTET_STRING,
|
||||
ASN1IdentifierTagNumber.NULL,
|
||||
ASN1IdentifierTagNumber.OBJECT_IDENTIFIER,
|
||||
ASN1IdentifierTagNumber.OBJECT_DESCRIPTOR,
|
||||
ASN1IdentifierTagNumber.EXTERNAL,
|
||||
ASN1IdentifierTagNumber.READ,
|
||||
ASN1IdentifierTagNumber.ENUMERATED,
|
||||
ASN1IdentifierTagNumber.EMBEDDED_PDV,
|
||||
ASN1IdentifierTagNumber.UTF8_STRING,
|
||||
ASN1IdentifierTagNumber.RELATIVE_OID,
|
||||
ASN1IdentifierTagNumber.SEQUENCE,
|
||||
ASN1IdentifierTagNumber.SET,
|
||||
ASN1IdentifierTagNumber.NUMERIC_STRING,
|
||||
ASN1IdentifierTagNumber.PRINTABLE_STRING,
|
||||
ASN1IdentifierTagNumber.T61_STRING,
|
||||
ASN1IdentifierTagNumber.VIDEOTEX_STRING,
|
||||
ASN1IdentifierTagNumber.IA5_STRING,
|
||||
ASN1IdentifierTagNumber.UTC_TIME,
|
||||
ASN1IdentifierTagNumber.GENERALIZED_TIME,
|
||||
ASN1IdentifierTagNumber.GRAPHIC_STRING,
|
||||
ASN1IdentifierTagNumber.VISIBLE_STRING,
|
||||
ASN1IdentifierTagNumber.GENERAL_STRING,
|
||||
ASN1IdentifierTagNumber.UNIVERSAL_STRING,
|
||||
ASN1IdentifierTagNumber.CHARACTER_STRING,
|
||||
ASN1IdentifierTagNumber.BMP_STRING,
|
||||
];
|
||||
|
||||
static ASN1IdentifierTagNumber fromValue(int value) {
|
||||
if (value != null)
|
||||
return ASN1IdentifierTagNumber.values.firstWhere((element) => element.toValue() == value, orElse: () => null);
|
||||
return null;
|
||||
}
|
||||
|
||||
int toValue() => _value;
|
||||
|
||||
static const END_OF_CONTENT = const ASN1IdentifierTagNumber._internal(0x00);
|
||||
static const BOOLEAN = const ASN1IdentifierTagNumber._internal(0x01);
|
||||
static const INTEGER = const ASN1IdentifierTagNumber._internal(0x02);
|
||||
static const BIT_STRING = const ASN1IdentifierTagNumber._internal(0x03);
|
||||
static const OCTET_STRING = const ASN1IdentifierTagNumber._internal(0x04);
|
||||
static const NULL = const ASN1IdentifierTagNumber._internal(0x05);
|
||||
static const OBJECT_IDENTIFIER = const ASN1IdentifierTagNumber._internal(0x06);
|
||||
static const OBJECT_DESCRIPTOR = const ASN1IdentifierTagNumber._internal(0x07);
|
||||
static const EXTERNAL = const ASN1IdentifierTagNumber._internal(0x08);
|
||||
static const READ = const ASN1IdentifierTagNumber._internal(0x09);
|
||||
static const ENUMERATED = const ASN1IdentifierTagNumber._internal(0x0A);
|
||||
static const EMBEDDED_PDV = const ASN1IdentifierTagNumber._internal(0x0B);
|
||||
static const UTF8_STRING = const ASN1IdentifierTagNumber._internal(0x0C);
|
||||
static const RELATIVE_OID = const ASN1IdentifierTagNumber._internal(0x0D);
|
||||
static const SEQUENCE = const ASN1IdentifierTagNumber._internal(0x10);
|
||||
static const SET = const ASN1IdentifierTagNumber._internal(0x11);
|
||||
static const NUMERIC_STRING = const ASN1IdentifierTagNumber._internal(0x12);
|
||||
static const PRINTABLE_STRING = const ASN1IdentifierTagNumber._internal(0x13);
|
||||
static const T61_STRING = const ASN1IdentifierTagNumber._internal(0x14);
|
||||
static const VIDEOTEX_STRING = const ASN1IdentifierTagNumber._internal(0x15);
|
||||
static const IA5_STRING = const ASN1IdentifierTagNumber._internal(0x16);
|
||||
static const UTC_TIME = const ASN1IdentifierTagNumber._internal(0x17);
|
||||
static const GENERALIZED_TIME = const ASN1IdentifierTagNumber._internal(0x18);
|
||||
static const GRAPHIC_STRING = const ASN1IdentifierTagNumber._internal(0x19);
|
||||
static const VISIBLE_STRING = const ASN1IdentifierTagNumber._internal(0x1A);
|
||||
static const GENERAL_STRING = const ASN1IdentifierTagNumber._internal(0x1B);
|
||||
static const UNIVERSAL_STRING = const ASN1IdentifierTagNumber._internal(0x1C);
|
||||
static const CHARACTER_STRING = const ASN1IdentifierTagNumber._internal(0x1D);
|
||||
static const BMP_STRING = const ASN1IdentifierTagNumber._internal(0x1E);
|
||||
|
||||
String toString() {
|
||||
switch (this.toValue()) {
|
||||
case 0x00:
|
||||
return "END_OF_CONTENT";
|
||||
case 0x01:
|
||||
return "BOOLEAN";
|
||||
case 0x02:
|
||||
return "INTEGER";
|
||||
case 0x03:
|
||||
return "BIT_STRING";
|
||||
case 0x04:
|
||||
return "OCTET_STRING";
|
||||
case 0x05:
|
||||
return "NULL";
|
||||
case 0x06:
|
||||
return "OBJECT_IDENTIFIER";
|
||||
case 0x07:
|
||||
return "OBJECT_DESCRIPTOR";
|
||||
case 0x08:
|
||||
return "EXTERNAL";
|
||||
case 0x09:
|
||||
return "READ";
|
||||
case 0x0A:
|
||||
return "ENUMERATED";
|
||||
case 0x0B:
|
||||
return "EMBEDDED_PDV";
|
||||
case 0x0C:
|
||||
return "UTF8_STRING";
|
||||
case 0x0D:
|
||||
return "RELATIVE_OID";
|
||||
case 0x10:
|
||||
return "SEQUENCE";
|
||||
case 0x11:
|
||||
return "SET";
|
||||
case 0x12:
|
||||
return "NUMERIC_STRING";
|
||||
case 0x13:
|
||||
return "PRINTABLE_STRING";
|
||||
case 0x14:
|
||||
return "T61_STRING";
|
||||
case 0x15:
|
||||
return "VIDEOTEX_STRING";
|
||||
case 0x16:
|
||||
return "IA5_STRING";
|
||||
case 0x17:
|
||||
return "UTC_TIME";
|
||||
case 0x18:
|
||||
return "GENERALIZED_TIME";
|
||||
case 0x19:
|
||||
return "GRAPHIC_STRING";
|
||||
case 0x1A:
|
||||
return "VISIBLE_STRING";
|
||||
case 0x1B:
|
||||
return "GENERAL_STRING";
|
||||
case 0x1C:
|
||||
return "UNIVERSAL_STRING";
|
||||
case 0x1D:
|
||||
return "CHARACTER_STRING";
|
||||
case 0x1E:
|
||||
return "BMP_STRING";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
||||
|
||||
class ASN1Identifier {
|
||||
int rawValue;
|
||||
|
||||
ASN1Identifier(this.rawValue);
|
||||
|
||||
bool isPrimitive() {
|
||||
return (rawValue & 0x20) == 0;
|
||||
}
|
||||
|
||||
bool isConstructed() {
|
||||
return (rawValue & 0x20) != 0;
|
||||
}
|
||||
|
||||
ASN1IdentifierTagNumber tagNumber() {
|
||||
return ASN1IdentifierTagNumber.fromValue(rawValue & 0x1F) ?? ASN1IdentifierTagNumber.END_OF_CONTENT;
|
||||
}
|
||||
|
||||
ASN1IdentifierClass typeClass() {
|
||||
for (var tc in [ASN1IdentifierClass.APPLICATION, ASN1IdentifierClass.CONTEXT_SPECIFIC, ASN1IdentifierClass.PRIVATE]) {
|
||||
if ((rawValue & tc.toValue()) == tc.toValue()) {
|
||||
return tc;
|
||||
}
|
||||
}
|
||||
return ASN1IdentifierClass.UNIVERSAL;
|
||||
}
|
||||
|
||||
String get description {
|
||||
var tc = typeClass();
|
||||
var tn = tagNumber();
|
||||
if (tc == ASN1IdentifierClass.UNIVERSAL) {
|
||||
return tn.toString();
|
||||
} else {
|
||||
return "$tc(${tn.toValue()})";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
84
lib/src/X509Certificate/asn1_object.dart
Normal file
84
lib/src/X509Certificate/asn1_object.dart
Normal file
@ -0,0 +1,84 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'x509_certificate.dart';
|
||||
|
||||
import 'asn1_identifier.dart';
|
||||
import 'oid.dart';
|
||||
|
||||
class ASN1Object {
|
||||
/// This property contains the DER encoded object
|
||||
Uint8List encoded;
|
||||
|
||||
/// This property contains the decoded Swift object whenever is possible
|
||||
dynamic value;
|
||||
|
||||
ASN1Identifier identifier;
|
||||
|
||||
List<ASN1Object> sub;
|
||||
|
||||
ASN1Object parent;
|
||||
|
||||
ASN1Object subAtIndex(int index) {
|
||||
if (sub != null && index >= 0 && index < sub.length) {
|
||||
return sub[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int subCount() {
|
||||
return sub?.length ?? 0;
|
||||
}
|
||||
|
||||
ASN1Object findOid({OID oid, String oidValue}) {
|
||||
oidValue = oid != null ? oid.toValue() : oidValue;
|
||||
for (var child in (sub ?? <ASN1Object>[])) {
|
||||
if (child.identifier?.tagNumber() == ASN1IdentifierTagNumber.OBJECT_IDENTIFIER) {
|
||||
if (child.value == oidValue) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var result = child.findOid(oidValue: oidValue);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String get description {
|
||||
return printAsn1();
|
||||
}
|
||||
|
||||
String printAsn1({insets = ""}) {
|
||||
var output = insets;
|
||||
output += identifier?.description?.toUpperCase() ?? "";
|
||||
output += (value != null ? ": $value" : "");
|
||||
if (identifier?.typeClass() == ASN1IdentifierClass.UNIVERSAL && identifier?.tagNumber() == ASN1IdentifierTagNumber.OBJECT_IDENTIFIER) {
|
||||
var descr = OID.fromValue(value?.toString() ?? "")?.name();
|
||||
if (descr != null) {
|
||||
output += " ($descr)";
|
||||
}
|
||||
}
|
||||
output += sub != null && sub.length > 0 ? " {" : "";
|
||||
output += "\n";
|
||||
for (var item in (sub ?? <ASN1Object>[])) {
|
||||
output += item.printAsn1(insets: insets + " ");
|
||||
}
|
||||
output += sub != null && sub.length > 0 ? "}\n" : "";
|
||||
return output;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return description;
|
||||
}
|
||||
|
||||
ASN1Object atIndex(X509BlockPosition x509blockPosition) {
|
||||
if (sub != null && x509blockPosition.index < sub.length) {
|
||||
return sub[x509blockPosition.index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
8
lib/src/X509Certificate/main.dart
Normal file
8
lib/src/X509Certificate/main.dart
Normal file
@ -0,0 +1,8 @@
|
||||
export 'asn1_decoder.dart';
|
||||
export 'asn1_distinguished_names.dart';
|
||||
export 'asn1_identifier.dart';
|
||||
export 'asn1_object.dart';
|
||||
export 'oid.dart';
|
||||
export 'x509_certificate.dart';
|
||||
export 'x509_extension.dart';
|
||||
export 'x509_public_key.dart';
|
315
lib/src/X509Certificate/oid.dart
Normal file
315
lib/src/X509Certificate/oid.dart
Normal file
@ -0,0 +1,315 @@
|
||||
class OID {
|
||||
final String _value;
|
||||
|
||||
const OID._internal(this._value);
|
||||
|
||||
static List<OID> values = [
|
||||
OID.etsiQcsCompliance,
|
||||
OID.etsiQcsRetentionPeriod,
|
||||
OID.etsiQcsQcSSCD,
|
||||
OID.dsa,
|
||||
OID.ecPublicKey,
|
||||
OID.prime256v1,
|
||||
OID.ecdsaWithSHA256,
|
||||
OID.ecdsaWithSHA512,
|
||||
OID.rsaEncryption,
|
||||
OID.md2WithRSAEncryption,
|
||||
OID.md4WithRSAEncryption,
|
||||
OID.md5WithRSAEncryption,
|
||||
OID.sha1WithRSAEncryption,
|
||||
OID.RSAES_OAEP,
|
||||
OID.mgf1,
|
||||
OID.pSpecified,
|
||||
OID.RSASSA_PSS,
|
||||
OID.sha256WithRSAEncryption,
|
||||
OID.sha384WithRSAEncryption,
|
||||
OID.sha512WithRSAEncryption,
|
||||
OID.pkcs7data,
|
||||
OID.pkcs7signedData,
|
||||
OID.pkcs7envelopedData,
|
||||
OID.emailAddress,
|
||||
OID.signingCertificateV2,
|
||||
OID.contentType,
|
||||
OID.messageDigest,
|
||||
OID.signingTime,
|
||||
OID.dsaWithSha1,
|
||||
OID.certificateExtension,
|
||||
OID.jurisdictionOfIncorporationSP,
|
||||
OID.jurisdictionOfIncorporationC,
|
||||
OID.authorityInfoAccess,
|
||||
OID.qcStatements,
|
||||
OID.cps,
|
||||
OID.unotice,
|
||||
OID.serverAuth,
|
||||
OID.clientAuth,
|
||||
OID.ocsp,
|
||||
OID.caIssuers,
|
||||
OID.dateOfBirth,
|
||||
OID.desCBC,
|
||||
OID.sha1,
|
||||
OID.sha256,
|
||||
OID.sha384,
|
||||
OID.sha512,
|
||||
OID.md5,
|
||||
OID.VeriSignEVpolicy,
|
||||
OID.extendedValidation,
|
||||
OID.organizationValidated,
|
||||
OID.subjectKeyIdentifier,
|
||||
OID.keyUsage,
|
||||
OID.subjectAltName,
|
||||
OID.issuerAltName,
|
||||
OID.basicConstraints,
|
||||
OID.cRLDistributionPoints,
|
||||
OID.certificatePolicies,
|
||||
OID.authorityKeyIdentifier,
|
||||
OID.extKeyUsage,
|
||||
OID.subjectDirectoryAttributes,
|
||||
OID.organizationName,
|
||||
OID.organizationalUnitName,
|
||||
OID.businessCategory,
|
||||
OID.postalCode,
|
||||
OID.commonName,
|
||||
OID.surname,
|
||||
OID.givenName,
|
||||
OID.dnQualifier,
|
||||
OID.serialNumber,
|
||||
OID.countryName,
|
||||
OID.localityName,
|
||||
OID.stateOrProvinceName,
|
||||
OID.streetAddress,
|
||||
OID.desEDE3CBC,
|
||||
OID.aes128CBC,
|
||||
OID.aes192CBC,
|
||||
OID.aes256CBC,
|
||||
OID.nsCertType,
|
||||
OID.nsComment,
|
||||
OID.privateKeyUsagePeriod,
|
||||
OID.cRLNumber,
|
||||
OID.cRLReason,
|
||||
OID.expirationDate,
|
||||
OID.instructionCode,
|
||||
OID.invalidityDate,
|
||||
OID.deltaCRLIndicator,
|
||||
OID.issuingDistributionPoint,
|
||||
OID.certificateIssuer,
|
||||
OID.nameConstraints,
|
||||
OID.policyMappings,
|
||||
OID.policyConstraints,
|
||||
OID.freshestCRL,
|
||||
OID.inhibitAnyPolicy,
|
||||
OID.codeSigning,
|
||||
OID.emailProtection,
|
||||
OID.timeStamping,
|
||||
];
|
||||
|
||||
static OID fromValue(String value) {
|
||||
return OID.values.firstWhere((element) => element.toValue() == value, orElse: () => null);
|
||||
}
|
||||
|
||||
String toValue() => _value;
|
||||
|
||||
String name() => _oidMapName[this._value];
|
||||
|
||||
@override
|
||||
String toString() => "($_value, ${name()})";
|
||||
|
||||
static const etsiQcsCompliance = const OID._internal("0.4.0.1862.1.1");
|
||||
static const etsiQcsRetentionPeriod = const OID._internal("0.4.0.1862.1.3");
|
||||
static const etsiQcsQcSSCD = const OID._internal("0.4.0.1862.1.4");
|
||||
static const dsa = const OID._internal("1.2.840.10040.4.1");
|
||||
static const ecPublicKey = const OID._internal("1.2.840.10045.2.1");
|
||||
static const prime256v1 = const OID._internal("1.2.840.10045.3.1.7");
|
||||
static const ecdsaWithSHA256 = const OID._internal("1.2.840.10045.4.3.2");
|
||||
static const ecdsaWithSHA512 = const OID._internal("1.2.840.10045.4.3.4");
|
||||
static const rsaEncryption = const OID._internal("1.2.840.113549.1.1.1");
|
||||
static const md2WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.2");
|
||||
static const md4WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.3");
|
||||
static const md5WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.4");
|
||||
static const sha1WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.5");
|
||||
static const RSAES_OAEP = const OID._internal("1.2.840.113549.1.1.7");
|
||||
static const mgf1 = const OID._internal(".2.840.113549.1.1.8");
|
||||
static const pSpecified = const OID._internal(".2.840.113549.1.1.9");
|
||||
static const RSASSA_PSS = const OID._internal(".2.840.113549.1.1.10");
|
||||
static const sha256WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.11");
|
||||
static const sha384WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.12");
|
||||
static const sha512WithRSAEncryption = const OID._internal("1.2.840.113549.1.1.13");
|
||||
static const pkcs7data = const OID._internal("1.2.840.113549.1.7.1");
|
||||
static const pkcs7signedData = const OID._internal("1.2.840.113549.1.7.2");
|
||||
static const pkcs7envelopedData = const OID._internal("1.2.840.113549.1.7.3");
|
||||
static const emailAddress = const OID._internal("1.2.840.113549.1.9.1");
|
||||
static const signingCertificateV2 = const OID._internal("1.2.840.113549.1.9.16.2.47");
|
||||
static const contentType = const OID._internal("1.2.840.113549.1.9.3");
|
||||
static const messageDigest = const OID._internal("1.2.840.113549.1.9.4");
|
||||
static const signingTime = const OID._internal("1.2.840.113549.1.9.5");
|
||||
static const dsaWithSha1 = const OID._internal("1.2.840.10040.4.3");
|
||||
static const certificateExtension = const OID._internal("1.3.6.1.4.1.11129.2.4.2");
|
||||
static const jurisdictionOfIncorporationSP = const OID._internal("1.3.6.1.4.1.311.60.2.1.2");
|
||||
static const jurisdictionOfIncorporationC = const OID._internal("1.3.6.1.4.1.311.60.2.1.3");
|
||||
static const authorityInfoAccess = const OID._internal("1.3.6.1.5.5.7.1.1");
|
||||
static const qcStatements = const OID._internal("1.3.6.1.5.5.7.1.3");
|
||||
static const cps = const OID._internal("1.3.6.1.5.5.7.2.1");
|
||||
static const unotice = const OID._internal("1.3.6.1.5.5.7.2.2");
|
||||
static const serverAuth = const OID._internal("1.3.6.1.5.5.7.3.1");
|
||||
static const clientAuth = const OID._internal("1.3.6.1.5.5.7.3.2");
|
||||
static const ocsp = const OID._internal("1.3.6.1.5.5.7.48.1");
|
||||
static const caIssuers = const OID._internal("1.3.6.1.5.5.7.48.2");
|
||||
static const dateOfBirth = const OID._internal("1.3.6.1.5.5.7.9.1");
|
||||
static const desCBC = const OID._internal("1.3.14.3.2.7");
|
||||
static const sha1 = const OID._internal("1.3.14.3.2.26");
|
||||
static const sha256 = const OID._internal("2.16.840.1.101.3.4.2.1");
|
||||
static const sha384 = const OID._internal("2.16.840.1.101.3.4.2.2");
|
||||
static const sha512 = const OID._internal("2.16.840.1.101.3.4.2.3");
|
||||
static const md5 = const OID._internal("1.2.840.113549.2.5");
|
||||
static const VeriSignEVpolicy = const OID._internal("2.16.840.1.113733.1.7.23.6");
|
||||
static const extendedValidation = const OID._internal("2.23.140.1.1");
|
||||
static const organizationValidated = const OID._internal("2.23.140.1.2.2");
|
||||
static const subjectKeyIdentifier = const OID._internal("2.5.29.14");
|
||||
static const keyUsage = const OID._internal("2.5.29.15");
|
||||
static const subjectAltName = const OID._internal("2.5.29.17");
|
||||
static const issuerAltName = const OID._internal("2.5.29.18");
|
||||
static const basicConstraints = const OID._internal("2.5.29.19");
|
||||
static const cRLDistributionPoints = const OID._internal("2.5.29.31");
|
||||
static const certificatePolicies = const OID._internal("2.5.29.32");
|
||||
static const authorityKeyIdentifier = const OID._internal("2.5.29.35");
|
||||
static const extKeyUsage = const OID._internal("2.5.29.37");
|
||||
static const subjectDirectoryAttributes = const OID._internal("2.5.29.9");
|
||||
static const organizationName = const OID._internal("2.5.4.10");
|
||||
static const organizationalUnitName = const OID._internal("2.5.4.11");
|
||||
static const businessCategory = const OID._internal("2.5.4.15");
|
||||
static const postalCode = const OID._internal("2.5.4.17");
|
||||
static const commonName = const OID._internal("2.5.4.3");
|
||||
static const surname = const OID._internal("2.5.4.4");
|
||||
static const givenName = const OID._internal("2.5.4.42");
|
||||
static const dnQualifier = const OID._internal("2.5.4.46");
|
||||
static const serialNumber = const OID._internal("2.5.4.5");
|
||||
static const countryName = const OID._internal("2.5.4.6");
|
||||
static const localityName = const OID._internal("2.5.4.7");
|
||||
static const stateOrProvinceName = const OID._internal("2.5.4.8");
|
||||
static const streetAddress = const OID._internal("2.5.4.9");
|
||||
static const desEDE3CBC = const OID._internal("1.2.840.113549.3.7");
|
||||
static const aes128CBC = const OID._internal("2.16.840.1.101.3.4.1.2");
|
||||
static const aes192CBC = const OID._internal("2.16.840.1.101.3.4.1.22");
|
||||
static const aes256CBC = const OID._internal("2.16.840.1.101.3.4.1.42");
|
||||
static const nsCertType = const OID._internal("2.16.840.1.113730.1.1");
|
||||
static const nsComment = const OID._internal("2.16.840.1.113730.1.13");
|
||||
static const privateKeyUsagePeriod = const OID._internal("2.5.29.16");
|
||||
static const cRLNumber = const OID._internal("2.5.29.20");
|
||||
static const cRLReason = const OID._internal("2.5.29.21");
|
||||
static const expirationDate = const OID._internal("2.5.29.22");
|
||||
static const instructionCode = const OID._internal("2.5.29.23");
|
||||
static const invalidityDate = const OID._internal("2.5.29.24");
|
||||
static const deltaCRLIndicator = const OID._internal("2.5.29.27");
|
||||
static const issuingDistributionPoint = const OID._internal("2.5.29.28");
|
||||
static const certificateIssuer = const OID._internal("2.5.29.29");
|
||||
static const nameConstraints = const OID._internal("2.5.29.30");
|
||||
static const policyMappings = const OID._internal("2.5.29.33");
|
||||
static const policyConstraints = const OID._internal("2.5.29.36");
|
||||
static const freshestCRL = const OID._internal("2.5.29.46");
|
||||
static const inhibitAnyPolicy = const OID._internal("2.5.29.54");
|
||||
static const codeSigning = const OID._internal("1.3.6.1.5.5.7.3.3");
|
||||
static const emailProtection = const OID._internal("1.3.6.1.5.5.7.3.4");
|
||||
static const timeStamping = const OID._internal("1.3.6.1.5.5.7.3.8");
|
||||
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
|
||||
static const Map<String, String> _oidMapName = {
|
||||
"0.4.0.1862.1.1": "etsiQcsCompliance",
|
||||
"0.4.0.1862.1.3": "etsiQcsRetentionPeriod",
|
||||
"0.4.0.1862.1.4": "etsiQcsQcSSCD",
|
||||
"1.2.840.10040.4.1": "dsa",
|
||||
"1.2.840.10045.2.1": "ecPublicKey",
|
||||
"1.2.840.10045.3.1.7": "prime256v1",
|
||||
"1.2.840.10045.4.3.2": "ecdsaWithSHA256",
|
||||
"1.2.840.10045.4.3.4": "ecdsaWithSHA512",
|
||||
"1.2.840.113549.1.1.1": "rsaEncryption",
|
||||
"1.2.840.113549.1.1.2": "md2WithRSAEncryption",
|
||||
"1.2.840.113549.1.1.3": "md4WithRSAEncryption",
|
||||
"1.2.840.113549.1.1.4": "md5WithRSAEncryption",
|
||||
"1.2.840.113549.1.1.5": "sha1WithRSAEncryption",
|
||||
"1.2.840.113549.1.1.7": "RSAES-OAEP",
|
||||
".2.840.113549.1.1.8": "mgf1",
|
||||
".2.840.113549.1.1.9": "pSpecified",
|
||||
".2.840.113549.1.1.10": "RSASSA-PSS",
|
||||
"1.2.840.113549.1.1.11": "sha256WithRSAEncryption",
|
||||
"1.2.840.113549.1.1.12": "sha384WithRSAEncryption",
|
||||
"1.2.840.113549.1.1.13": "sha512WithRSAEncryption",
|
||||
"1.2.840.113549.1.7.1": "data",
|
||||
"1.2.840.113549.1.7.2": "signedData",
|
||||
"1.2.840.113549.1.9.1": "emailAddress",
|
||||
"1.2.840.113549.1.9.16.2.47": "signingCertificateV2",
|
||||
"1.2.840.113549.1.9.3": "contentType",
|
||||
"1.2.840.113549.1.9.4": "messageDigest",
|
||||
"1.2.840.113549.1.9.5": "signingTime",
|
||||
"1.2.840.10040.4.3": 'dsa-with-sha1',
|
||||
"1.3.6.1.4.1.11129.2.4.2": "certificateExtension",
|
||||
"1.3.6.1.4.1.311.60.2.1.2": "jurisdictionOfIncorporationSP",
|
||||
"1.3.6.1.4.1.311.60.2.1.3": "jurisdictionOfIncorporationC",
|
||||
"1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
|
||||
"1.3.6.1.5.5.7.1.3": "qcStatements",
|
||||
"1.3.6.1.5.5.7.2.1": "cps",
|
||||
"1.3.6.1.5.5.7.2.2": "unotice",
|
||||
"1.3.6.1.5.5.7.3.1": "serverAuth",
|
||||
"1.3.6.1.5.5.7.3.2": "clientAuth",
|
||||
"1.3.6.1.5.5.7.48.1": "ocsp",
|
||||
"1.3.6.1.5.5.7.48.2": "caIssuers",
|
||||
"1.3.6.1.5.5.7.9.1": "dateOfBirth",
|
||||
"1.3.14.3.2.7": "desCBC",
|
||||
"1.3.14.3.2.26": "sha1",
|
||||
"2.16.840.1.101.3.4.2.1": "sha256",
|
||||
"2.16.840.1.101.3.4.2.2": "sha384",
|
||||
"2.16.840.1.101.3.4.2.3": "sha512",
|
||||
"1.2.840.113549.2.5": "md5",
|
||||
"2.16.840.1.113733.1.7.23.6": "VeriSign EV policy",
|
||||
"2.23.140.1.1": "extendedValidation",
|
||||
"2.23.140.1.2.2": "extendedValidation",
|
||||
"2.5.29.14": "subjectKeyIdentifier",
|
||||
"2.5.29.15": "keyUsage",
|
||||
"2.5.29.17": "subjectAltName",
|
||||
"2.5.29.18": "issuerAltName",
|
||||
"2.5.29.19": "basicConstraints",
|
||||
"2.5.29.31": "cRLDistributionPoints",
|
||||
"2.5.29.32": "certificatePolicies",
|
||||
"2.5.29.35": "authorityKeyIdentifier",
|
||||
"2.5.29.37": "extKeyUsage",
|
||||
"2.5.29.9": "subjectDirectoryAttributes",
|
||||
"2.5.4.10": "organizationName",
|
||||
"2.5.4.11": "organizationalUnitName",
|
||||
"2.5.4.15": "businessCategory",
|
||||
"2.5.4.17": "postalCode",
|
||||
"2.5.4.3": "commonName",
|
||||
"2.5.4.4": "surname",
|
||||
"2.5.4.42": "givenName",
|
||||
"2.5.4.46": "dnQualifier",
|
||||
"2.5.4.5": "serialNumber",
|
||||
"2.5.4.6": "countryName",
|
||||
"2.5.4.7": "localityName",
|
||||
"2.5.4.8": "stateOrProvinceName",
|
||||
"2.5.4.9": "streetAddress",
|
||||
"1.2.840.113549.3.7": "des-EDE3-CBC",
|
||||
"2.16.840.1.101.3.4.1.2": "aes128-CBC",
|
||||
"2.16.840.1.101.3.4.1.22": "aes192-CBC",
|
||||
"2.16.840.1.101.3.4.1.42": "aes256-CBC",
|
||||
"2.16.840.1.113730.1.1": "nsCertType",
|
||||
"2.16.840.1.113730.1.13": "nsComment",
|
||||
"2.5.29.16": "privateKeyUsagePeriod",
|
||||
"2.5.29.20": "cRLNumber",
|
||||
"2.5.29.21": "cRLReason",
|
||||
"2.5.29.22": "expirationDate",
|
||||
"2.5.29.23": "instructionCode",
|
||||
"2.5.29.24": "invalidityDate",
|
||||
"2.5.29.27": "deltaCRLIndicator",
|
||||
"2.5.29.28": "issuingDistributionPoint",
|
||||
"2.5.29.29": "certificateIssuer",
|
||||
"2.5.29.30": "nameConstraints",
|
||||
"2.5.29.33": "policyMappings",
|
||||
"2.5.29.36": "policyConstraints",
|
||||
"2.5.29.46": "freshestCRL",
|
||||
"2.5.29.54": "inhibitAnyPolicy",
|
||||
"1.3.6.1.5.5.7.3.3": "codeSigning",
|
||||
"1.3.6.1.5.5.7.3.4": "emailProtection",
|
||||
"1.3.6.1.5.5.7.3.8": "timeStamping",
|
||||
};
|
||||
}
|
450
lib/src/X509Certificate/x509_certificate.dart
Normal file
450
lib/src/X509Certificate/x509_certificate.dart
Normal file
@ -0,0 +1,450 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'asn1_decoder.dart';
|
||||
import 'asn1_object.dart';
|
||||
import 'oid.dart';
|
||||
import 'x509_public_key.dart';
|
||||
|
||||
import 'x509_extension.dart';
|
||||
import 'asn1_distinguished_names.dart';
|
||||
|
||||
///Class that represents a X.509 certificate.
|
||||
///This provides a standard way to access all the attributes of an X.509 certificate.
|
||||
class X509Certificate {
|
||||
List<ASN1Object> asn1;
|
||||
ASN1Object block1;
|
||||
|
||||
///Returns the encoded form of this certificate. It is
|
||||
///assumed that each certificate type would have only a single
|
||||
///form of encoding; for example, X.509 certificates would
|
||||
///be encoded as ASN.1 DER.
|
||||
Uint8List encoded;
|
||||
|
||||
static const beginPemBlock = "-----BEGIN CERTIFICATE-----";
|
||||
static const endPemBlock = "-----END CERTIFICATE-----";
|
||||
|
||||
X509Certificate({ASN1Object asn1}) {
|
||||
if (asn1 != null) {
|
||||
var block1 = asn1.subAtIndex(0);
|
||||
if (block1 == null) {
|
||||
throw ASN1ParseError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static X509Certificate fromData({@required Uint8List data}) {
|
||||
var decoded = utf8.decode(data, allowMalformed: true);
|
||||
if (decoded.contains(X509Certificate.beginPemBlock)) {
|
||||
return X509Certificate.fromPemData(pem: data);
|
||||
} else {
|
||||
return X509Certificate.fromDerData(der: data);
|
||||
}
|
||||
}
|
||||
|
||||
static X509Certificate fromDerData({@required Uint8List der}) {
|
||||
var asn1 = ASN1DERDecoder.decode(data: der.toList(growable: true));
|
||||
if (asn1.length > 0) {
|
||||
var block1 = asn1.first?.subAtIndex(0);
|
||||
if (block1 != null) {
|
||||
var certificate = X509Certificate();
|
||||
certificate.asn1 = asn1;
|
||||
certificate.block1 = block1;
|
||||
certificate.encoded = der;
|
||||
return certificate;
|
||||
}
|
||||
}
|
||||
throw ASN1ParseError();
|
||||
}
|
||||
|
||||
static X509Certificate fromPemData({@required Uint8List pem}) {
|
||||
var derData = X509Certificate.decodeToDER(pemData: pem);
|
||||
if (derData == null) {
|
||||
throw ASN1ParseError();
|
||||
}
|
||||
return X509Certificate.fromDerData(der: derData);
|
||||
}
|
||||
|
||||
///Read possible PEM encoding
|
||||
static Uint8List decodeToDER({@required pemData}) {
|
||||
var pem = String.fromCharCodes(pemData);
|
||||
if (pem != null && pem.contains(X509Certificate.beginPemBlock)) {
|
||||
var lines = pem.split("\n");
|
||||
var base64buffer = "";
|
||||
var certLine = false;
|
||||
for (var line in lines) {
|
||||
if (line == X509Certificate.endPemBlock) {
|
||||
certLine = false;
|
||||
}
|
||||
if (certLine) {
|
||||
base64buffer += line;
|
||||
}
|
||||
if (line == X509Certificate.beginPemBlock) {
|
||||
certLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List derDataDecoded;
|
||||
try {
|
||||
derDataDecoded = Uint8List.fromList(utf8.encode(base64buffer));
|
||||
} catch (e) {}
|
||||
if (derDataDecoded != null) {
|
||||
return derDataDecoded;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String get description =>
|
||||
asn1.fold("", (value, element) => value + element.description + "\n");
|
||||
|
||||
///Checks that the given date is within the certificate's validity period.
|
||||
bool checkValidity({DateTime date}) {
|
||||
if (date == null) {
|
||||
date = DateTime.now();
|
||||
}
|
||||
if (notBefore != null && notAfter != null) {
|
||||
return date.isAfter(notBefore) && date.isBefore(notAfter);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///Gets the version (version number) value from the certificate.
|
||||
int get version {
|
||||
var v = firstLeafValue(block: block1) as List<int>;
|
||||
if (v != null) {
|
||||
var index = toIntValue(v);
|
||||
if (index != null) {
|
||||
return index.toInt() + 1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets the serialNumber value from the certificate.
|
||||
List<int> get serialNumber =>
|
||||
block1.atIndex(X509BlockPosition.serialNumber)?.value as List<int>;
|
||||
|
||||
///Returns the issuer (issuer distinguished name) value from the certificate as a String.
|
||||
String get issuerDistinguishedName {
|
||||
var issuerBlock = block1.atIndex(X509BlockPosition.issuer);
|
||||
if (issuerBlock != null) {
|
||||
return blockDistinguishedName(block: issuerBlock);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> get issuerOIDs {
|
||||
var result = <String>[];
|
||||
var issuerBlock = block1.atIndex(X509BlockPosition.issuer);
|
||||
if (issuerBlock != null) {
|
||||
for (var sub in (issuerBlock.sub ?? <ASN1Object>[])) {
|
||||
var value = firstLeafValue(block: sub) as String;
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String issuer({String oid, ASN1DistinguishedNames dn}) {
|
||||
if (oid == null && dn != null) {
|
||||
oid = dn.oid();
|
||||
}
|
||||
if (oid != null) {
|
||||
var issuerBlock = block1.atIndex(X509BlockPosition.issuer);
|
||||
if (issuerBlock != null) {
|
||||
var oidBlock = issuerBlock.findOid(oidValue: oid);
|
||||
if (oidBlock != null) {
|
||||
var sub = oidBlock.parent?.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
return sub.last.value as String;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Returns the subject (subject distinguished name) value from the certificate as a String.
|
||||
String get subjectDistinguishedName {
|
||||
var subjectBlock = block1.atIndex(X509BlockPosition.subject);
|
||||
if (subjectBlock != null) {
|
||||
return blockDistinguishedName(block: subjectBlock);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> get subjectOIDs {
|
||||
var result = <String>[];
|
||||
var subjectBlock = block1.atIndex(X509BlockPosition.subject);
|
||||
if (subjectBlock != null) {
|
||||
for (var sub in (subjectBlock.sub ?? <ASN1Object>[])) {
|
||||
var value = firstLeafValue(block: sub) as String;
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String subject({String oid, ASN1DistinguishedNames dn}) {
|
||||
if (oid == null && dn != null) {
|
||||
oid = dn.oid();
|
||||
}
|
||||
if (oid != null) {
|
||||
var subjectBlock = block1.atIndex(X509BlockPosition.subject);
|
||||
if (subjectBlock != null) {
|
||||
var oidBlock = subjectBlock.findOid(oidValue: oid);
|
||||
if (oidBlock != null) {
|
||||
var sub = oidBlock.parent?.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
return sub.last.value as String;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets the notBefore date from the validity period of the certificate.
|
||||
DateTime get notBefore =>
|
||||
block1.atIndex(X509BlockPosition.dateValidity)?.subAtIndex(0)?.value
|
||||
as DateTime;
|
||||
|
||||
///Gets the notAfter date from the validity period of the certificate.
|
||||
DateTime get notAfter {
|
||||
var value = block1
|
||||
.atIndex(X509BlockPosition.dateValidity)
|
||||
?.subAtIndex(1)
|
||||
?.value as DateTime;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Gets the signature value (the raw signature bits) from the certificate.
|
||||
List<int> get signature => asn1[0].subAtIndex(2)?.value as List<int>;
|
||||
|
||||
/// Gets the signature algorithm name for the certificate signature algorithm.
|
||||
String get sigAlgName => OID.fromValue(sigAlgOID ?? "")?.name();
|
||||
|
||||
/// Gets the signature algorithm OID string from the certificate.
|
||||
String get sigAlgOID => block1.subAtIndex(2)?.subAtIndex(0)?.value as String;
|
||||
|
||||
/// Gets the DER-encoded signature algorithm parameters from this certificate's signature algorithm.
|
||||
List<int> get sigAlgParams => null;
|
||||
|
||||
///Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15).
|
||||
///```
|
||||
///KeyUsage ::= BIT STRING {
|
||||
///digitalSignature (0),
|
||||
///nonRepudiation (1),
|
||||
///keyEncipherment (2),
|
||||
///dataEncipherment (3),
|
||||
///keyAgreement (4),
|
||||
///keyCertSign (5),
|
||||
///cRLSign (6),
|
||||
///encipherOnly (7),
|
||||
///decipherOnly (8)
|
||||
///}
|
||||
///```
|
||||
List<bool> get keyUsage {
|
||||
var result = <bool>[];
|
||||
var oidBlock = block1.findOid(oid: OID.keyUsage);
|
||||
if (oidBlock != null) {
|
||||
var sub = oidBlock.parent?.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
var data = sub.last.subAtIndex(0)?.value as List<int>;
|
||||
int bits = (data != null && data.length > 0) ? data.first ?? 0 : 0;
|
||||
for (var index = 0; index < 7; index++) {
|
||||
var value = bits & (1 << index).toUnsigned(8) != 0;
|
||||
result.insert(0, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
///Gets a list of Strings representing the OBJECT IDENTIFIERs of the ExtKeyUsageSyntax field of
|
||||
///the extended key usage extension, (OID = 2.5.29.37).
|
||||
List<String> get extendedKeyUsage =>
|
||||
extensionObject(oid: OID.extKeyUsage)?.valueAsStrings ?? <String>[];
|
||||
|
||||
///Gets a collection of subject alternative names from the SubjectAltName extension, (OID = 2.5.29.17).
|
||||
List<String> get subjectAlternativeNames =>
|
||||
extensionObject(oid: OID.subjectAltName)?.valueAsStrings ?? <String>[];
|
||||
|
||||
///Gets a collection of issuer alternative names from the IssuerAltName extension, (OID = 2.5.29.18).
|
||||
List<String> get issuerAlternativeNames =>
|
||||
extensionObject(oid: OID.issuerAltName)?.valueAsStrings ?? <String>[];
|
||||
|
||||
///Gets the informations of the public key from this certificate.
|
||||
X509PublicKey get publicKey {
|
||||
var pkBlock = block1.atIndex(X509BlockPosition.publicKey);
|
||||
if (pkBlock != null) {
|
||||
return X509PublicKey(pkBlock: pkBlock);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Get a list of critical extension OID codes
|
||||
List<String> get criticalExtensionOIDs {
|
||||
var extensionBlocks = this.extensionBlocks;
|
||||
if (extensionBlocks == null) {
|
||||
return <String>[];
|
||||
}
|
||||
return extensionBlocks
|
||||
.map((block) => X509Extension(block: block))
|
||||
.where((extension) => extension.isCritical)
|
||||
.map((extension) => extension.oid)
|
||||
.toList();
|
||||
}
|
||||
|
||||
///Get a list of non critical extension OID codes
|
||||
List<String> get nonCriticalExtensionOIDs {
|
||||
var extensionBlocks = this.extensionBlocks;
|
||||
if (extensionBlocks == null) {
|
||||
return <String>[];
|
||||
}
|
||||
return extensionBlocks
|
||||
.map((block) => X509Extension(block: block))
|
||||
.where((extension) => !extension.isCritical)
|
||||
.map((extension) => extension.oid)
|
||||
.toList();
|
||||
}
|
||||
|
||||
///Gets the certificate constraints path length from the
|
||||
///critical BasicConstraints extension, (OID = 2.5.29.19).
|
||||
int get basicConstraints => extensionObject(oid: OID.basicConstraints)?.value as int ?? -1;
|
||||
|
||||
List<ASN1Object> get extensionBlocks =>
|
||||
block1.atIndex(X509BlockPosition.extensions)?.subAtIndex(0)?.sub;
|
||||
|
||||
///Gets the extension information of the given OID code or enum.
|
||||
X509Extension extensionObject({String oidValue, OID oid}) {
|
||||
if (oidValue == null && oid != null) {
|
||||
oidValue = oid.toValue();
|
||||
}
|
||||
if (oidValue != null) {
|
||||
var block = block1
|
||||
.atIndex(X509BlockPosition.extensions)
|
||||
?.findOid(oidValue: oidValue)
|
||||
?.parent;
|
||||
if (block != null) {
|
||||
return X509Extension(block: block);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Format subject/issuer information in RFC1779
|
||||
String blockDistinguishedName({@required ASN1Object block}) {
|
||||
var result = "";
|
||||
List<ASN1DistinguishedNames> oidNames = [
|
||||
ASN1DistinguishedNames.COMMON_NAME,
|
||||
ASN1DistinguishedNames.DN_QUALIFIER,
|
||||
ASN1DistinguishedNames.SERIAL_NUMBER,
|
||||
ASN1DistinguishedNames.GIVEN_NAME,
|
||||
ASN1DistinguishedNames.SURNAME,
|
||||
ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME,
|
||||
ASN1DistinguishedNames.ORGANIZATION_NAME,
|
||||
ASN1DistinguishedNames.STREET_ADDRESS,
|
||||
ASN1DistinguishedNames.LOCALITY_NAME,
|
||||
ASN1DistinguishedNames.STATE_OR_PROVINCE_NAME,
|
||||
ASN1DistinguishedNames.COUNTRY_NAME,
|
||||
ASN1DistinguishedNames.EMAIL
|
||||
];
|
||||
for (var oidName in oidNames) {
|
||||
var oidBlock = block.findOid(oidValue: oidName.oid());
|
||||
if (oidBlock != null) {
|
||||
if (result.isNotEmpty) {
|
||||
result += ", ";
|
||||
}
|
||||
result += oidName.representation();
|
||||
result += "=";
|
||||
|
||||
var sub = oidBlock.parent?.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
var value = sub.last.value as String;
|
||||
if (value != null) {
|
||||
var specialChar = ",+=\n<>#;\\";
|
||||
var quote = "";
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var char = value[i];
|
||||
if (specialChar.contains(char)) {
|
||||
quote = "\"";
|
||||
}
|
||||
}
|
||||
result += quote;
|
||||
result += value;
|
||||
result += quote;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return description;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"basicConstraints": basicConstraints,
|
||||
"subjectAlternativeNames": subjectAlternativeNames,
|
||||
"issuerAlternativeNames": issuerAlternativeNames,
|
||||
"extendedKeyUsage": extendedKeyUsage,
|
||||
"issuerDistinguishedName": issuerDistinguishedName,
|
||||
"keyUsage": keyUsage,
|
||||
"notAfter": notAfter,
|
||||
"notBefore": notBefore,
|
||||
"serialNumber": serialNumber,
|
||||
"sigAlgName": sigAlgName,
|
||||
"sigAlgOID": sigAlgOID,
|
||||
"sigAlgParams": sigAlgParams,
|
||||
"signature": signature,
|
||||
"subjectDistinguishedName": subjectDistinguishedName,
|
||||
"version": version,
|
||||
"criticalExtensionOIDs": criticalExtensionOIDs,
|
||||
"nonCriticalExtensionOIDs": nonCriticalExtensionOIDs,
|
||||
"encoded": encoded,
|
||||
"publicKey": publicKey?.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
}
|
||||
|
||||
dynamic firstLeafValue({@required ASN1Object block}) {
|
||||
var sub = block.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
var subFirst = sub.first;
|
||||
if (subFirst != null) {
|
||||
return firstLeafValue(block: subFirst);
|
||||
}
|
||||
}
|
||||
return block.value;
|
||||
}
|
||||
|
||||
enum X509BlockPosition {
|
||||
version,
|
||||
serialNumber,
|
||||
signatureAlg,
|
||||
issuer,
|
||||
dateValidity,
|
||||
subject,
|
||||
publicKey,
|
||||
extensions
|
||||
}
|
55
lib/src/X509Certificate/x509_extension.dart
Normal file
55
lib/src/X509Certificate/x509_extension.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'x509_certificate.dart';
|
||||
import 'asn1_object.dart';
|
||||
import 'oid.dart';
|
||||
|
||||
class X509Extension {
|
||||
ASN1Object block;
|
||||
|
||||
X509Extension({this.block});
|
||||
|
||||
String get oid => block.subAtIndex(0)?.value;
|
||||
|
||||
String get name => OID.fromValue(oid ?? "")?.name();
|
||||
|
||||
bool get isCritical {
|
||||
if ((block.sub?.length ?? 0) > 2) {
|
||||
return block.subAtIndex(1)?.value ?? false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
dynamic get value {
|
||||
var sub = block.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
var valueBlock = sub.last;
|
||||
if (valueBlock != null) {
|
||||
return firstLeafValue(block: valueBlock);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ASN1Object get valueAsBlock {
|
||||
var sub = block.sub;
|
||||
if (sub != null && sub.length > 0) {
|
||||
return sub.last;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> get valueAsStrings {
|
||||
var result = <String>[];
|
||||
var sub = <ASN1Object>[];
|
||||
try {
|
||||
sub = block.sub?.last?.sub?.last?.sub;
|
||||
} catch (e) {}
|
||||
|
||||
for (var item in sub) {
|
||||
var name = item.value;
|
||||
if (name != null) {
|
||||
result.add(name);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
54
lib/src/X509Certificate/x509_public_key.dart
Normal file
54
lib/src/X509Certificate/x509_public_key.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'asn1_decoder.dart';
|
||||
import 'asn1_object.dart';
|
||||
import 'oid.dart';
|
||||
|
||||
class X509PublicKey {
|
||||
ASN1Object pkBlock;
|
||||
|
||||
X509PublicKey({this.pkBlock});
|
||||
|
||||
String get algOid => pkBlock?.subAtIndex(0)?.subAtIndex(0)?.value;
|
||||
|
||||
String get algName => OID.fromValue(algOid ?? "")?.name();
|
||||
|
||||
String get algParams => pkBlock?.subAtIndex(0)?.subAtIndex(1)?.value;
|
||||
|
||||
Uint8List get encoded {
|
||||
var oid = OID.fromValue(algOid);
|
||||
var keyData = pkBlock?.subAtIndex(1)?.value ?? null;
|
||||
|
||||
if (oid != null && algOid != null && keyData != null) {
|
||||
if (oid == OID.ecPublicKey) {
|
||||
return Uint8List.fromList(keyData);
|
||||
} else if (oid == OID.rsaEncryption) {
|
||||
List<ASN1Object> publicKeyAsn1Objects;
|
||||
try {
|
||||
publicKeyAsn1Objects = ASN1DERDecoder.decode(data: keyData.toList(growable: true));
|
||||
} catch(e) {}
|
||||
|
||||
if (publicKeyAsn1Objects != null && publicKeyAsn1Objects.length > 0) {
|
||||
var publicKeyModulus = publicKeyAsn1Objects.first?.subAtIndex(0)?.value;
|
||||
if (publicKeyModulus != null) {
|
||||
return Uint8List.fromList(publicKeyModulus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"algOid": algOid,
|
||||
"algName": algName,
|
||||
"algParams": algParams,
|
||||
"encoded": encoded,
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
@ -9,6 +10,8 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'X509Certificate/asn1_distinguished_names.dart';
|
||||
import 'X509Certificate/x509_certificate.dart';
|
||||
|
||||
import 'package:html/parser.dart' show parse;
|
||||
|
||||
@ -369,7 +372,37 @@ class InAppWebViewController {
|
||||
int androidError = call.arguments["androidError"];
|
||||
int iosError = call.arguments["iosError"];
|
||||
String message = call.arguments["message"];
|
||||
Uint8List serverCertificate = call.arguments["serverCertificate"];
|
||||
Map<String, dynamic> sslCertificateMap = call.arguments["sslCertificate"]?.cast<String, dynamic>();
|
||||
|
||||
SslCertificate sslCertificate;
|
||||
if (sslCertificateMap != null) {
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
X509Certificate x509certificate = X509Certificate.fromData(data: sslCertificateMap["x509Certificate"]);
|
||||
sslCertificate = SslCertificate(
|
||||
issuedBy: SslCertificateDName(
|
||||
CName: x509certificate.issuer(dn: ASN1DistinguishedNames.COMMON_NAME) ?? "",
|
||||
DName: x509certificate.issuerDistinguishedName ?? "",
|
||||
OName: x509certificate.issuer(dn: ASN1DistinguishedNames.ORGANIZATION_NAME) ?? "",
|
||||
UName: x509certificate.issuer(dn: ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME) ?? ""),
|
||||
issuedTo: SslCertificateDName(
|
||||
CName: x509certificate.subject(dn: ASN1DistinguishedNames.COMMON_NAME) ?? "",
|
||||
DName: x509certificate.subjectDistinguishedName ?? "",
|
||||
OName: x509certificate.subject(dn: ASN1DistinguishedNames.ORGANIZATION_NAME) ?? "",
|
||||
UName: x509certificate.subject(dn: ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME) ?? ""),
|
||||
validNotAfterDate: x509certificate.notAfter,
|
||||
validNotBeforeDate: x509certificate.notBefore,
|
||||
x509Certificate: x509certificate,
|
||||
);
|
||||
} catch(e, stacktrace) {
|
||||
print(e);
|
||||
print(stacktrace);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
sslCertificate = SslCertificate.fromMap(sslCertificateMap);
|
||||
}
|
||||
}
|
||||
|
||||
AndroidSslError androidSslError = androidError != null ? AndroidSslError.fromValue(androidError) : null;
|
||||
IOSSslError iosSslError = iosError != null ? IOSSslError.fromValue(iosError) : null;
|
||||
@ -381,7 +414,7 @@ class InAppWebViewController {
|
||||
androidError: androidSslError,
|
||||
iosError: iosSslError,
|
||||
message: message,
|
||||
serverCertificate: serverCertificate);
|
||||
sslCertificate: sslCertificate);
|
||||
if (_webview != null &&
|
||||
_webview.onReceivedServerTrustAuthRequest != null)
|
||||
return (await _webview.onReceivedServerTrustAuthRequest(
|
||||
@ -1655,6 +1688,46 @@ class InAppWebViewController {
|
||||
return await _channel.invokeMethod('getScrollY', args);
|
||||
}
|
||||
|
||||
///Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getCertificate()
|
||||
Future<SslCertificate> getCertificate() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
|
||||
Map<String, dynamic> sslCertificateMap = (await _channel.invokeMethod('getCertificate', args))?.cast<String, dynamic>();
|
||||
|
||||
if (sslCertificateMap != null) {
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
X509Certificate x509certificate = X509Certificate.fromData(data: sslCertificateMap["x509Certificate"]);
|
||||
return SslCertificate(
|
||||
issuedBy: SslCertificateDName(
|
||||
CName: x509certificate.issuer(dn: ASN1DistinguishedNames.COMMON_NAME) ?? "",
|
||||
DName: x509certificate.issuerDistinguishedName ?? "",
|
||||
OName: x509certificate.issuer(dn: ASN1DistinguishedNames.ORGANIZATION_NAME) ?? "",
|
||||
UName: x509certificate.issuer(dn: ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME) ?? ""),
|
||||
issuedTo: SslCertificateDName(
|
||||
CName: x509certificate.subject(dn: ASN1DistinguishedNames.COMMON_NAME) ?? "",
|
||||
DName: x509certificate.subjectDistinguishedName ?? "",
|
||||
OName: x509certificate.subject(dn: ASN1DistinguishedNames.ORGANIZATION_NAME) ?? "",
|
||||
UName: x509certificate.subject(dn: ASN1DistinguishedNames.ORGANIZATIONAL_UNIT_NAME) ?? ""),
|
||||
validNotAfterDate: x509certificate.notAfter,
|
||||
validNotBeforeDate: x509certificate.notBefore,
|
||||
x509Certificate: x509certificate,
|
||||
);
|
||||
} catch(e, stacktrace) {
|
||||
print(e);
|
||||
print(stacktrace);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return SslCertificate.fromMap(sslCertificateMap);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets the default user agent.
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
|
||||
@ -1794,16 +1867,6 @@ class AndroidInAppWebViewController {
|
||||
return await _controller._channel.invokeMethod('clearHistory', args);
|
||||
}
|
||||
|
||||
///Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getCertificate()
|
||||
Future<AndroidSslCertificate> getCertificate() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
var sslCertificateMap = (await _controller._channel.invokeMethod('getCertificate', args))?.cast<String, dynamic>();
|
||||
|
||||
return sslCertificateMap != null ? AndroidSslCertificate.fromMap(sslCertificateMap) : null;
|
||||
}
|
||||
|
||||
///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests.
|
||||
///Note that WebView automatically clears these preferences when the system keychain is updated.
|
||||
///The preferences are shared by all the WebViews that are created by the embedder application.
|
||||
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'X509Certificate/x509_certificate.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -924,15 +925,15 @@ class ServerTrustChallenge {
|
||||
///**NOTE**: on iOS this value is always an empty string.
|
||||
String message;
|
||||
|
||||
///The `X509Certificate` used to create the server SSL certificate.
|
||||
Uint8List serverCertificate;
|
||||
///The SSL certificate used for this challenge.
|
||||
SslCertificate sslCertificate;
|
||||
|
||||
ServerTrustChallenge(
|
||||
{@required this.protectionSpace,
|
||||
this.androidError,
|
||||
this.iosError,
|
||||
this.message,
|
||||
this.serverCertificate})
|
||||
this.sslCertificate})
|
||||
: assert(protectionSpace != null);
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
@ -941,7 +942,7 @@ class ServerTrustChallenge {
|
||||
"androidError": androidError?.toValue(),
|
||||
"iosError": iosError?.toValue(),
|
||||
"message": message,
|
||||
"serverCertificate": serverCertificate
|
||||
"sslCertificate": sslCertificate?.toMap()
|
||||
};
|
||||
}
|
||||
|
||||
@ -3741,14 +3742,25 @@ class IOSUIScrollViewContentInsetAdjustmentBehavior {
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
||||
|
||||
class AndroidSslCertificate {
|
||||
AndroidSslCertificateDName issuedBy;
|
||||
AndroidSslCertificateDName issuedTo;
|
||||
int validNotAfterDate;
|
||||
int validNotBeforeDate;
|
||||
AndroidX509Certificate x509Certificate;
|
||||
///SSL certificate info (certificate details) class.
|
||||
class SslCertificate {
|
||||
|
||||
AndroidSslCertificate({
|
||||
///Name of the entity this certificate is issued by
|
||||
SslCertificateDName issuedBy;
|
||||
|
||||
///Name of the entity this certificate is issued to
|
||||
SslCertificateDName issuedTo;
|
||||
|
||||
///Not-after date from the validity period
|
||||
DateTime validNotAfterDate;
|
||||
|
||||
///Not-before date from the validity period
|
||||
DateTime validNotBeforeDate;
|
||||
|
||||
///The original source certificate, if available.
|
||||
X509Certificate x509Certificate;
|
||||
|
||||
SslCertificate({
|
||||
this.issuedBy,
|
||||
this.issuedTo,
|
||||
this.validNotAfterDate,
|
||||
@ -3756,13 +3768,21 @@ class AndroidSslCertificate {
|
||||
this.x509Certificate
|
||||
});
|
||||
|
||||
static AndroidSslCertificate fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? AndroidSslCertificate(
|
||||
issuedBy: AndroidSslCertificateDName.fromMap(map["issuedBy"]?.cast<String, dynamic>()),
|
||||
issuedTo: AndroidSslCertificateDName.fromMap(map["issuedTo"]?.cast<String, dynamic>()),
|
||||
validNotAfterDate: map["validNotAfterDate"],
|
||||
validNotBeforeDate: map["validNotBeforeDate"],
|
||||
x509Certificate: AndroidX509Certificate.fromMap(map["x509Certificate"]?.cast<String, dynamic>()),
|
||||
static SslCertificate fromMap(Map<String, dynamic> map) {
|
||||
X509Certificate x509Certificate;
|
||||
try {
|
||||
x509Certificate = X509Certificate.fromData(data: map["x509Certificate"]);
|
||||
} catch (e, stacktrace) {
|
||||
print(e);
|
||||
print(stacktrace);
|
||||
}
|
||||
|
||||
return map != null ? SslCertificate(
|
||||
issuedBy: SslCertificateDName.fromMap(map["issuedBy"]?.cast<String, dynamic>()),
|
||||
issuedTo: SslCertificateDName.fromMap(map["issuedTo"]?.cast<String, dynamic>()),
|
||||
validNotAfterDate: DateTime.fromMillisecondsSinceEpoch(map["validNotAfterDate"]),
|
||||
validNotBeforeDate: DateTime.fromMillisecondsSinceEpoch(map["validNotBeforeDate"]),
|
||||
x509Certificate: x509Certificate,
|
||||
) : null;
|
||||
}
|
||||
|
||||
@ -3770,8 +3790,8 @@ class AndroidSslCertificate {
|
||||
return {
|
||||
"issuedBy": issuedBy?.toMap(),
|
||||
"issuedTo": issuedTo?.toMap(),
|
||||
"validNotAfterDate": validNotAfterDate,
|
||||
"validNotBeforeDate": validNotBeforeDate,
|
||||
"validNotAfterDate": validNotAfterDate.millisecondsSinceEpoch,
|
||||
"validNotBeforeDate": validNotBeforeDate.millisecondsSinceEpoch,
|
||||
"x509Certificate": x509Certificate?.toMap(),
|
||||
};
|
||||
}
|
||||
@ -3786,25 +3806,30 @@ class AndroidSslCertificate {
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidSslCertificateDName {
|
||||
///Distinguished name helper class. Used by [SslCertificate].
|
||||
class SslCertificateDName {
|
||||
///Common-name (CN) component of the name
|
||||
// ignore: non_constant_identifier_names
|
||||
String CName;
|
||||
///Distinguished name (normally includes CN, O, and OU names)
|
||||
// ignore: non_constant_identifier_names
|
||||
String DName;
|
||||
///Organization (O) component of the name
|
||||
// ignore: non_constant_identifier_names
|
||||
String OName;
|
||||
///Organizational Unit (OU) component of the name
|
||||
// ignore: non_constant_identifier_names
|
||||
String UName;
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
AndroidSslCertificateDName({this.CName, this.DName, this.OName, this.UName});
|
||||
SslCertificateDName({this.CName = "", this.DName = "", this.OName = "", this.UName = ""});
|
||||
|
||||
static AndroidSslCertificateDName fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? AndroidSslCertificateDName(
|
||||
CName: map["CName"],
|
||||
DName: map["DName"],
|
||||
OName: map["OName"],
|
||||
UName: map["UName"],
|
||||
static SslCertificateDName fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? SslCertificateDName(
|
||||
CName: map["CName"] ?? "",
|
||||
DName: map["DName"] ?? "",
|
||||
OName: map["OName"] ?? "",
|
||||
UName: map["UName"] ?? "",
|
||||
) : null;
|
||||
}
|
||||
|
||||
@ -3826,229 +3851,3 @@ class AndroidSslCertificateDName {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidX509Certificate {
|
||||
int basicConstraints;
|
||||
List<String> extendedKeyUsage;
|
||||
AndroidX509CertificatePrincipal issuerDN;
|
||||
List<bool> issuerUniqueID;
|
||||
AndroidX500Principal issuerX500Principal;
|
||||
List<bool> keyUsage;
|
||||
int notAfter;
|
||||
int notBefore;
|
||||
int serialNumber;
|
||||
String sigAlgName;
|
||||
String sigAlgOID;
|
||||
Uint8List sigAlgParams;
|
||||
Uint8List signature;
|
||||
AndroidX509CertificatePrincipal subjectDN;
|
||||
List<bool> subjectUniqueID;
|
||||
AndroidX500Principal subjectX500Principal;
|
||||
// ignore: non_constant_identifier_names
|
||||
Uint8List TBSCertificate;
|
||||
int version;
|
||||
Set<String> criticalExtensionOIDs;
|
||||
Set<String> nonCriticalExtensionOIDs;
|
||||
Uint8List encoded;
|
||||
AndroidX509CertificatePublicKey publicKey;
|
||||
String type;
|
||||
bool hasUnsupportedCriticalExtension;
|
||||
bool valid;
|
||||
|
||||
AndroidX509Certificate({
|
||||
this.basicConstraints,
|
||||
this.extendedKeyUsage,
|
||||
this.issuerDN,
|
||||
this.issuerUniqueID,
|
||||
this.issuerX500Principal,
|
||||
this.keyUsage,
|
||||
this.notAfter,
|
||||
this.notBefore,
|
||||
this.serialNumber,
|
||||
this.sigAlgName,
|
||||
this.sigAlgOID,
|
||||
this.sigAlgParams,
|
||||
this.signature,
|
||||
this.subjectDN,
|
||||
this.subjectUniqueID,
|
||||
this.subjectX500Principal,
|
||||
// ignore: non_constant_identifier_names
|
||||
this.TBSCertificate,
|
||||
this.version,
|
||||
this.criticalExtensionOIDs,
|
||||
this.nonCriticalExtensionOIDs,
|
||||
this.encoded,
|
||||
this.publicKey,
|
||||
this.type,
|
||||
this.hasUnsupportedCriticalExtension,
|
||||
this.valid
|
||||
});
|
||||
|
||||
static AndroidX509Certificate fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? AndroidX509Certificate(
|
||||
basicConstraints: map["basicConstraints"],
|
||||
extendedKeyUsage: map["extendedKeyUsage"],
|
||||
issuerDN: AndroidX509CertificatePrincipal.fromMap(map["issuerDN"]),
|
||||
issuerUniqueID: map["issuerUniqueID"],
|
||||
issuerX500Principal: AndroidX500Principal.fromMap(map["issuerX500Principal"]),
|
||||
keyUsage: map["keyUsage"],
|
||||
notAfter: map["notAfter"],
|
||||
notBefore: map["notBefore"],
|
||||
serialNumber: map["serialNumber"],
|
||||
sigAlgName: map["sigAlgName"],
|
||||
sigAlgOID: map["sigAlgOID"],
|
||||
sigAlgParams: map["sigAlgParams"],
|
||||
signature: map["signature"],
|
||||
subjectDN: AndroidX509CertificatePrincipal.fromMap(map["subjectDN"]),
|
||||
subjectUniqueID: map["subjectUniqueID"],
|
||||
subjectX500Principal: AndroidX500Principal.fromMap(map["subjectX500Principal"]),
|
||||
TBSCertificate: map["TBSCertificate"],
|
||||
version: map["version"],
|
||||
criticalExtensionOIDs: map["criticalExtensionOIDs"],
|
||||
nonCriticalExtensionOIDs: map["nonCriticalExtensionOIDs"],
|
||||
encoded: map["encoded"],
|
||||
publicKey: AndroidX509CertificatePublicKey.fromMap(map["publicKey"]),
|
||||
type: map["type"],
|
||||
hasUnsupportedCriticalExtension: map["hasUnsupportedCriticalExtension"],
|
||||
valid: map["valid"]
|
||||
) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"basicConstraints": basicConstraints,
|
||||
"extendedKeyUsage": extendedKeyUsage,
|
||||
"issuerDN": issuerDN?.toMap(),
|
||||
"issuerUniqueID": issuerUniqueID,
|
||||
"issuerX500Principal": issuerX500Principal?.toMap(),
|
||||
"keyUsage": keyUsage,
|
||||
"notAfter": notAfter,
|
||||
"notBefore": notBefore,
|
||||
"serialNumber": serialNumber,
|
||||
"sigAlgName": sigAlgName,
|
||||
"sigAlgOID": sigAlgOID,
|
||||
"sigAlgParams": sigAlgParams,
|
||||
"signature": signature,
|
||||
"subjectDN": subjectDN?.toMap(),
|
||||
"subjectUniqueID": subjectUniqueID,
|
||||
"subjectX500Principal": subjectX500Principal?.toMap(),
|
||||
"TBSCertificate": TBSCertificate,
|
||||
"version": version,
|
||||
"criticalExtensionOIDs": criticalExtensionOIDs,
|
||||
"nonCriticalExtensionOIDs": nonCriticalExtensionOIDs,
|
||||
"encoded": encoded,
|
||||
"publicKey": publicKey?.toMap(),
|
||||
"type": type,
|
||||
"hasUnsupportedCriticalExtension": hasUnsupportedCriticalExtension,
|
||||
"valid": valid,
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidX509CertificatePrincipal {
|
||||
String name;
|
||||
|
||||
AndroidX509CertificatePrincipal({
|
||||
this.name
|
||||
});
|
||||
|
||||
static AndroidX509CertificatePrincipal fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? AndroidX509CertificatePrincipal(
|
||||
name: map["name"],
|
||||
) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"name": name,
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidX500Principal {
|
||||
String name;
|
||||
Uint8List encoded;
|
||||
|
||||
AndroidX500Principal({
|
||||
this.name,
|
||||
this.encoded
|
||||
});
|
||||
|
||||
static AndroidX500Principal fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? AndroidX500Principal(
|
||||
name: map["name"],
|
||||
encoded: map["encoded"],
|
||||
) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"name": name,
|
||||
"encoded": encoded,
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidX509CertificatePublicKey {
|
||||
String algorithm;
|
||||
Uint8List encoded;
|
||||
String format;
|
||||
|
||||
AndroidX509CertificatePublicKey({
|
||||
this.algorithm,
|
||||
this.encoded,
|
||||
this.format
|
||||
});
|
||||
|
||||
static AndroidX509CertificatePublicKey fromMap(Map<String, dynamic> map) {
|
||||
return map != null ? AndroidX509CertificatePublicKey(
|
||||
algorithm: map["algorithm"],
|
||||
encoded: map["encoded"],
|
||||
format: map["format"],
|
||||
) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"algorithm": algorithm,
|
||||
"encoded": encoded,
|
||||
"format": format,
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user