Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/more node tls compliant ios #208

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ static SSLServerSocketFactory createServerSocketFactory(Context context, @NonNul
return sslContext.getServerSocketFactory();
}

static boolean hasIdentity(ReadableMap options) {
try {
final String keystoreName = options.hasKey("androidKeyStore") ?
options.getString("androidKeyStore") : KeyStore.getDefaultType();
final String keyAlias = options.hasKey("keyAlias") ?
options.getString("keyAlias") : "";

if (keyAlias.isEmpty()) {
return false;
}

KeyStore keyStore = KeyStore.getInstance(keystoreName);
keyStore.load(null, null);

// Check if key entry exists with its certificate chain
return keyStore.isKeyEntry(keyAlias);
} catch (Exception e) {
return false;
}
}

public static PrivateKey getPrivateKeyFromPEM(InputStream keyStream) {
try (PemReader pemReader = new PemReader(new InputStreamReader(keyStream))) {
PemObject pemObject = pemReader.readPemObject();
Expand Down Expand Up @@ -127,37 +148,45 @@ static SSLSocketFactory createCustomTrustedSocketFactory(
final KeystoreInfo keystoreInfo) throws IOException, GeneralSecurityException {

SSLSocketFactory ssf = null;
if (optionResCert != null && optionResKey != null) {
final String keyStoreName = keystoreInfo.getKeystoreName().isEmpty() ?

KeyStore keyStore = null;
final String keyStoreName = keystoreInfo.getKeystoreName().isEmpty() ?
KeyStore.getDefaultType() :
keystoreInfo.getKeystoreName();
KeyStore keyStore = KeyStore.getInstance(keyStoreName);
keyStore.load(null, null);
String keyAlias = keystoreInfo.getKeyAlias();

// Check if cert and key if already registered inside our keystore
// If one is missing we insert again
boolean hasCertInStore = keyStore.isCertificateEntry(keystoreInfo.getCertAlias());
boolean hasKeyInStore = keyStore.isKeyEntry(keystoreInfo.getKeyAlias());
if (!hasCertInStore || !hasKeyInStore) {
InputStream certInput = getResolvableinputStream(context, optionResCert);
Certificate cert = CertificateFactory.getInstance("X.509").generateCertificate(certInput);
keyStore.setCertificateEntry(keystoreInfo.getCertAlias(), cert);

InputStream keyInput = getResolvableinputStream(context, optionResKey);
PrivateKey privateKey = getPrivateKeyFromPEM(keyInput);
keyStore.setKeyEntry(keystoreInfo.getKeyAlias(), privateKey, null, new Certificate[]{cert});
// if user provides keyAlias without key it means an identity(cert+key) has already been
// inserted in keychain.
if (keyAlias != null && !keyAlias.isEmpty() && optionResKey == null) {
keyStore = KeyStore.getInstance(keyStoreName);
keyStore.load(null, null);
if (!keyStore.isKeyEntry(keyAlias)) {
keyStore = null;
}
} else if (optionResCert != null && optionResKey != null) {

keyStore = KeyStore.getInstance(keyStoreName);
keyStore.load(null, null);

boolean hasCaInStore = keyStore.isCertificateEntry(keystoreInfo.getCaAlias());
if (optionResCa != null && !hasCaInStore) {
InputStream certInput = getResolvableinputStream(context, optionResCert);
Certificate cert = CertificateFactory.getInstance("X.509").generateCertificate(certInput);
keyStore.setCertificateEntry(keystoreInfo.getCertAlias(), cert);

InputStream keyInput = getResolvableinputStream(context, optionResKey);
PrivateKey privateKey = getPrivateKeyFromPEM(keyInput);
keyStore.setKeyEntry(keystoreInfo.getKeyAlias(), privateKey, null, new Certificate[]{cert});

if (optionResCa != null) {
InputStream caInput = getResolvableinputStream(context, optionResCa);
// Generate the CA Certificate from the raw resource file
Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(caInput);
caInput.close();
// Load the key store using the CA
keyStore.setCertificateEntry(keystoreInfo.getCaAlias(), ca);
}

}

if (keyStore != null) {
// Initialize the KeyManagerFactory with this cert
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, new char[0]);
Expand All @@ -166,15 +195,14 @@ static SSLSocketFactory createCustomTrustedSocketFactory(
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[]{new BlindTrustManager()}, null);
return sslContext.getSocketFactory();

} else {
// Keep old behavior
InputStream caInput = getResolvableinputStream(context, optionResCa);
// Generate the CA Certificate from the raw resource file
Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(caInput);
caInput.close();
// Load the key store using the CA
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ private boolean containsKey(ReadableArray array, String key) {
}
return false;
}

private ResolvableOption getResolvableOption(ReadableMap tlsOptions, String key) {
if (tlsOptions.hasKey(key)) {
String value = tlsOptions.getString(key);
if (value == null || value.isEmpty()) {
return null;
}
ReadableArray resolvedKeys = tlsOptions.hasKey("resolvedKeys") ? tlsOptions.getArray("resolvedKeys") : null;
boolean needsResolution = resolvedKeys != null && containsKey(resolvedKeys, key);
return new ResolvableOption(value, needsResolution);
Expand All @@ -110,7 +114,8 @@ private SSLSocketFactory getSSLSocketFactory(Context context, ReadableMap tlsOpt
final KeystoreInfo keystoreInfo = new KeystoreInfo(keystoreName, caAlias, certAlias, keyAlias);

if (tlsOptions.hasKey("rejectUnauthorized") && !tlsOptions.getBoolean("rejectUnauthorized")) {
if (customTlsKey != null && customTlsCert != null ) {
if ((customTlsKey != null && customTlsCert != null) ||
(keyAlias != null && !keyAlias.isEmpty() && customTlsKey == null) ) {
ssf = SSLCertificateHelper.createCustomTrustedSocketFactory(
context,
customTlsCa,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,16 @@ private TcpSocketServer getTcpServer(final int id) {
return (TcpSocketServer) socket;
}

@SuppressWarnings("unused")
@ReactMethod
public void hasIdentity(@NonNull final ReadableMap options, Promise promise) {
try {
promise.resolve(SSLCertificateHelper.hasIdentity(options));
} catch (Exception e) {
promise.reject(e);
}
}

@SuppressWarnings("unused")
@ReactMethod
public void getPeerCertificate(final int cId, Promise promise) {
Expand Down
26 changes: 26 additions & 0 deletions ios/TcpSocketClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ typedef enum RCTTCPError RCTTCPError;

@class TcpSocketClient;

// Add ResolvableOption interface here
@interface ResolvableOption : NSObject

@property (nonatomic, strong, readonly) NSString *value;
@property (nonatomic, readonly) BOOL needsResolution;

- (instancetype)initWithValue:(NSString *)value needsResolution:(BOOL)needsResolution;
+ (instancetype)optionWithValue:(NSString *)value needsResolution:(BOOL)needsResolution;
- (NSString *)resolve;

@end

@protocol SocketClientDelegate <NSObject>

- (void)addClient:(TcpSocketClient *)client;
Expand Down Expand Up @@ -115,4 +127,18 @@ typedef enum RCTTCPError RCTTCPError;

- (void)resume;

+ (BOOL)hasIdentity:(NSDictionary *)aliases;

/**
* Get peer certificate information
* @return NSDictionary with certificate information or nil if not available
*/
- (NSDictionary *)getPeerCertificate;

/**
* Get local certificate information
* @return NSDictionary with certificate information or nil if not available
*/
- (NSDictionary *)getCertificate;

@end
Loading