当前位置: 首页 > 知识库问答 >
问题:

支持SSL/TLS的Objective-C TCP服务器

海雪松
2023-03-14

我有一个简单的TCP服务器/客户端设置。这种连接实际上效果很好。

现在,我想为套接字连接实现SSL/TLS加密。我使用密钥链访问创建了PKCS12证书。在我的服务器中,accept回调中包含以下代码:

NSString *certificatePath = [[NSBundle mainBundle] pathForResource:@"TCPServerCertificate" ofType:@"p12"];
NSData *certificateData = [NSData dataWithContentsOfFile:certificatePath];

CFArrayRef keyRef;
OSStatus status = SecPKCS12Import((__bridge CFDataRef)certificateData, (__bridge CFDictionaryRef)@{(__bridge NSString *)kSecImportExportPassphrase: @"1234"}, &keyRef);

if (status != noErr) {
    NSLog(@"PKCS12 import error %i", status);
    return;
}

CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyRef, 0);
SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);

SecCertificateRef certificate;
status = SecIdentityCopyCertificate(identityRef, &certificate);

if (status != noErr) {
    NSLog(@"sec identity copy failed: %i", status);
    return;
}

NSArray *certificates = [NSArray arrayWithObjects:(__bridge id)identityRef, (__bridge id)certificate, nil];

NSDictionary *settings = @{(NSString *)kCFStreamPropertyShouldCloseNativeSocket:    [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLValidatesCertificateChain:       [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLAllowsExpiredCertificates:       [NSNumber numberWithBool:NO],
                           (NSString *)kCFStreamSSLAllowsExpiredRoots:              [NSNumber numberWithBool:NO],
                           (NSString *)kCFStreamSSLAllowsAnyRoot:                   [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLCertificates:                    certificates,
                           (NSString *)kCFStreamSSLIsServer:                        [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLLevel:                           (NSString *)kCFStreamSocketSecurityLevelTLSv1};

CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

然后我创建流的实例,并在另一个类中处理它们。

当我运行服务器并连接客户端时,我会在委托中获得常规的nsstreamventopencompleted。如果我只是关闭连接,那么当我尝试写入流或事件时,会出现以下错误:

2013-10-25 13:27:08.584 TCPServer[6435:303] CFNetwork SSLHandshake failed (-9800)
2013-10-25 13:27:08.584 TCPServer[6435:303] NSStreamEventOpenCompleted
2013-10-25 13:27:08.585 TCPServer[6435:303] NSStreamEventErrorOccurred

我想知道我必须在客户端实现什么。此外,我想知道为什么在发送数据或断开与客户端的连接时会出现握手失败。每当发生此错误时,客户端都认为它仍处于连接状态。

是否有任何好的TCP SSL/TLS教程或其他涵盖客户端和服务器端的材料?

共有2个答案

应和光
2023-03-14

我刚刚创建了一个包,它使TLS/TCP在新的iOS 13限制下变得更容易。希望它能帮助别人!请随意贡献:

https://github.com/eamonwhiter73/IOSObjCWebSockets

葛威
2023-03-14

此代码存在一些问题:您不能使用kCFStreamProperty tyShouldCloseNativeSocket设置kCFStreamProperty tySSLSetings,并且不应混合服务器和客户端代码。

对于服务器,您应该只设置一个证书

CFReadStreamSetProperty(read, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(write, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
//kCFStreamPropertySocketSecurityLevel
//Note: If you set this key, you must do so before setting any other SSL options, such as kCFStreamPropertySSLSettings.
CFReadStreamSetProperty(read, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
CFWriteStreamSetProperty(write, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
//Creating server dictionnary

//kCFStreamSSLIsServer
//If the value of this key is kCFBooleanTrue, the kCFStreamSSLCertificates key must contain a valid value

//kCFStreamSSLCertificates
//Security property key whose value is a CFArray of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef.
//For more information, see SSLSetCertificate() in Security/SecureTransport.h.

NSDictionary *settings = @{(id)kCFStreamSSLCertificates:                    certificates,
                           (id)kCFStreamSSLIsServer:                        @YES};


//Apply settings
CFReadStreamSetProperty(read, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));
CFWriteStreamSetProperty(write, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));

对于客户端,如果您想覆盖验证链(请参阅Apple doc on overriding chain validation),您应该执行以下操作:

CFReadStreamSetProperty(read, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(write, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
//kCFStreamPropertySocketSecurityLevel
//Note: If you set this key, you must do so before setting any other SSL options, such as kCFStreamPropertySSLSettings.

CFReadStreamSetProperty(read, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
CFWriteStreamSetProperty(write, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);

//DO NOT USE kCFStreamPropertySSLContext as it overrides the following configuration
//create dictionnary for kCFStreamPropertySSLSettings

//keys for dictionnary we want to change:kCFStreamSSLAllowsExpiredCertificates;kCFStreamSSLAllowsExpiredRoots;kCFStreamSSLAllowsAnyRoot;
//kCFStreamSSLValidatesCertificateChain => no need to worry about the root
//kCFStreamSSLPeerName kCFNull prevents name verification


settings = @{(id)kCFStreamSSLValidatesCertificateChain: @NO,//The delegate will verify this
             (id)kCFStreamSSLPeerName: (id)kCFNull};//prevents name verification if server is not fixed (eg. IP)

//Apply settings
CFReadStreamSetProperty(read, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));
CFWriteStreamSetProperty(write, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));
 类似资料:
  • 我在演戏!(2.2.1,Java)应用程序,我希望允许: 客户端证书-只有使用客户端证书的客户端身份验证才能使用应用程序API 服务器端证书–客户端必须能够通过其受信任的CA列表对服务器提供的证书进行身份验证 你在玩什么!这样做的最佳实践? 另外,你知道Play!使用什么加密算法吗?(TLS1.2, 1.1, 1.0、SSL 3.0、SSL 2.0等),如果它不使用TLS 1.2,我可以强制Pla

  • 我有一个关于SSLConnection的问题,在java中,我为客户端编写了下面的代码(这个应用程序必须连接到服务器,我有支持ssl的服务器),但我得到了这样的错误“javax.net.ssl.sslException:Connection Hays Shutdown:javax.net.ssl.sslhandShakeException:sun.security.validator.valida

  • 问题内容: 根据Java 7文档以及第三方供应商的说法,似乎Java 7应该支持AES-GCM套件: IBM Java 7 Java 7 SSL文档 在客户端和服务器之间的协商中遇到一些错误,由于仅将其限制为AES- GCM密码而无法协商密码。经过调查,我发现客户端或服务器(tomcat实例)均不支持密码套件。在客户端上运行一些示例代码以获取输出: 不知道是否有人遇到过这样的问题。 Java 7是

  • 问题内容: 我需要检查服务器是否支持SSL和来自Web服务器的密码。 我查看了Java中的SSLSocket,但无法正常工作。 我使用的方法始终为每个网址提供相同的协议。此外,我没有从服务器获得密码。我猜是正确的方法 如何检查服务器使用SSL?服务器返回了什么密码? 问题答案: 分别返回 您一方 可以支持的协议和密码列表。返回您启用的这些列表的子集。 这不会告诉您有关服务器支持什么的太多信息。 根

  • 本文向大家介绍在Apache服务器上安装SSL支持的教程,包括了在Apache服务器上安装SSL支持的教程的使用技巧和注意事项,需要的朋友参考一下 今天我会讲述如何为你的个人网站或者博客安装SSL 证书,来保护你的访问者和网站之间通信的安全。 安全套接字层或称SSL,是一种加密网站和浏览器之间连接的标准安全技术。这确保服务器和浏览器之间传输的数据保持隐私和安全。它被成千上万的人使用来保护他们与客户

  • 在我的Android应用程序中,我必须与https web服务通信并读取响应。 我已经通知服务器使用TLS 1.2配置SSL。 我正在使用以下示例代码与服务连接(https get Request),但只有运行Android 5.0或更高版本的设备才能成功通信和读取响应...... 该版本(Android 5.0)下的所有其他设备无法通信,在尝试建立连接时引发IOException。。。 或 问题