aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m
diff options
context:
space:
mode:
Diffstat (limited to 'src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m')
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m b/src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m
new file mode 100644
index 0000000000..9699889536
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m
@@ -0,0 +1,135 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import "GRPCSecureChannelFactory.h"
+
+#include <grpc/grpc_security.h>
+
+#import "ChannelArgsUtil.h"
+#import "GRPCChannel.h"
+
+@implementation GRPCSecureChannelFactory {
+ grpc_channel_credentials *_channelCreds;
+}
+
++ (instancetype)factoryWithPEMRootCertificates:(NSString *)rootCerts
+ privateKey:(NSString *)privateKey
+ certChain:(NSString *)certChain
+ error:(NSError **)errorPtr {
+ return [[self alloc] initWithPEMRootCerts:rootCerts
+ privateKey:privateKey
+ certChain:certChain
+ error:errorPtr];
+}
+
+- (NSData *)nullTerminatedDataWithString:(NSString *)string {
+ // dataUsingEncoding: does not return a null-terminated string.
+ NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
+ if (data == nil) {
+ return nil;
+ }
+ NSMutableData *nullTerminated = [NSMutableData dataWithData:data];
+ [nullTerminated appendBytes:"\0" length:1];
+ return nullTerminated;
+}
+
+- (instancetype)initWithPEMRootCerts:(NSString *)rootCerts
+ privateKey:(NSString *)privateKey
+ certChain:(NSString *)certChain
+ error:(NSError **)errorPtr {
+ static NSData *defaultRootsASCII;
+ static NSError *defaultRootsError;
+ static dispatch_once_t loading;
+ dispatch_once(&loading, ^{
+ NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem
+ // Do not use NSBundle.mainBundle, as it's nil for tests of library projects.
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+ NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"];
+ NSError *error;
+ // Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the
+ // issuer). Load them as UTF8 and produce an ASCII equivalent.
+ NSString *contentInUTF8 =
+ [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
+ if (contentInUTF8 == nil) {
+ defaultRootsError = error;
+ return;
+ }
+ defaultRootsASCII = [self nullTerminatedDataWithString:contentInUTF8];
+ });
+
+ NSData *rootsASCII;
+ if (rootCerts != nil) {
+ rootsASCII = [self nullTerminatedDataWithString:rootCerts];
+ } else {
+ if (defaultRootsASCII == nil) {
+ if (errorPtr) {
+ *errorPtr = defaultRootsError;
+ }
+ NSAssert(
+ defaultRootsASCII, NSObjectNotAvailableException,
+ @"Could not read gRPCCertificates.bundle/roots.pem. This file, "
+ "with the root certificates, is needed to establish secure (TLS) connections. "
+ "Because the file is distributed with the gRPC library, this error is usually a sign "
+ "that the library wasn't configured correctly for your project. Error: %@",
+ defaultRootsError);
+ return nil;
+ }
+ rootsASCII = defaultRootsASCII;
+ }
+
+ grpc_channel_credentials *creds = NULL;
+ if (privateKey.length == 0 && certChain.length == 0) {
+ creds = grpc_ssl_credentials_create(rootsASCII.bytes, NULL, NULL, NULL);
+ } else {
+ grpc_ssl_pem_key_cert_pair key_cert_pair;
+ NSData *privateKeyASCII = [self nullTerminatedDataWithString:privateKey];
+ NSData *certChainASCII = [self nullTerminatedDataWithString:certChain];
+ key_cert_pair.private_key = privateKeyASCII.bytes;
+ key_cert_pair.cert_chain = certChainASCII.bytes;
+ if (key_cert_pair.private_key == NULL || key_cert_pair.cert_chain == NULL) {
+ creds = grpc_ssl_credentials_create(rootsASCII.bytes, NULL, NULL, NULL);
+ } else {
+ creds = grpc_ssl_credentials_create(rootsASCII.bytes, &key_cert_pair, NULL, NULL);
+ }
+ }
+
+ if ((self = [super init])) {
+ _channelCreds = creds;
+ }
+ return self;
+}
+
+- (grpc_channel *)createChannelWithHost:(NSString *)host channelArgs:(NSDictionary *)args {
+ NSAssert(host.length != 0, @"host cannot be empty");
+ if (host.length == 0) {
+ return NULL;
+ }
+ grpc_channel_args *coreChannelArgs = GRPCBuildChannelArgs(args);
+ grpc_channel *unmanagedChannel =
+ grpc_secure_channel_create(_channelCreds, host.UTF8String, coreChannelArgs, NULL);
+ GRPCFreeChannelArgs(coreChannelArgs);
+ return unmanagedChannel;
+}
+
+- (void)dealloc {
+ if (_channelCreds != NULL) {
+ grpc_channel_credentials_release(_channelCreds);
+ }
+}
+
+@end