- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
在下面的 iOS UIViewController 代码中,我连接到使用自签名证书的服务器。我可以通过两种方式验证此自签名证书:使用信任 API 手动验证,或通过将自签名证书添加到我的应用程序的钥匙串(keychain)中来自动验证。
不幸的是,在我创建一个 CFReadStream 并将 kCFStreamSSLValidatesCertificateChain 设置为 kBooleanFalse 之后,我之后创建的每个 CFReadStream 都不会验证其证书链。我是否未能在某处清理代码?如果是这样,我会很乐意将这个问题重新表述为有关 API 清理的具体内容。
#import <UIKit/UIKit.h>
#import <Security/Security.h>
@interface SecureViewController : UIViewController<NSStreamDelegate> {
}
- (id) initWithCertificate: (SecCertificateRef) certificate;
@end
#import "SecureViewController.h"
@interface SecureViewController()
@property (nonatomic) SecCertificateRef certificate;
@property (nonatomic, retain) NSInputStream *inputStream;
@property (nonatomic, retain) NSOutputStream *outputStream;
@property (nonatomic) BOOL verifyOnHasSpaceAvailable;
- (void) verifyManually;
- (void) verifyWithKeychain;
@end
@implementation SecureViewController
@synthesize certificate = _certificate;
@synthesize inputStream = _inputStream;
@synthesize outputStream = _outputStream;
@synthesize verifyOnHasSpaceAvailable = _verifyOnHasSpaceAvailable;
#pragma mark -
#pragma mark init/dealloc methods
- (id) initWithCertificate: (SecCertificateRef) certificate {
if (self = [super initWithNibName:nil bundle:nil]) {
self.certificate = certificate;
}
return self;
}
- (void)dealloc {
self.certificate = NULL;
self.inputStream = nil;
self.outputStream = nil;
[super dealloc];
}
#pragma mark -
#pragma mark UIViewController
- (void)loadView {
[super loadView];
UIButton *manualVerificationButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[manualVerificationButton addTarget:self
action:@selector(verifyManually)
forControlEvents:UIControlEventTouchUpInside];
manualVerificationButton.frame = CGRectMake(0,
0,
self.view.bounds.size.width,
self.view.bounds.size.height / 2);
[manualVerificationButton setTitle:@"Manual Verification"
forState:UIControlStateNormal];
[self.view addSubview:manualVerificationButton];
UIButton *keychainVerificationButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[keychainVerificationButton addTarget:self
action:@selector(verifyWithKeychain)
forControlEvents:UIControlEventTouchUpInside];
keychainVerificationButton.frame = CGRectMake(0,
self.view.bounds.size.height / 2,
self.view.bounds.size.width,
self.view.bounds.size.height / 2);
[keychainVerificationButton setTitle:
@"Keychain Verification\n"
@"(Doesn't work after Manual Verification)\n"
@"((Don't know why yet.))"
forState:UIControlStateNormal];
keychainVerificationButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
keychainVerificationButton.titleLabel.numberOfLines = 0;
[self.view addSubview:keychainVerificationButton];
}
#pragma mark -
#pragma mark private api
- (void) verifyWithKeychain {
self.inputStream = nil;
self.outputStream = nil;
self.verifyOnHasSpaceAvailable = NO;
OSStatus err = SecItemAdd((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
self.certificate, kSecValueRef,
nil],
NULL);
assert(err == noErr || err == errSecDuplicateItem);
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
self.inputStream = (NSInputStream *)readStream;
self.outputStream = (NSOutputStream *)writeStream;
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
- (void) verifyManually {
self.inputStream = nil;
self.outputStream = nil;
// we don't want the keychain to accidentally accept our self-signed cert.
SecItemDelete((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
self.certificate, kSecValueRef,
nil]);
self.verifyOnHasSpaceAvailable = YES;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
NSDictionary *sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
nil];
CFReadStreamSetProperty(readStream,
kCFStreamPropertySSLSettings,
sslSettings);
// Don't set this property. The only settings that work are:
// kCFStreamSocketSecurityLevelNone or leaving it unset.
// Leaving it appears to be equivalent to setting it to:
// kCFStreamSocketSecurityLevelTLSv1 or kCFStreamSocketSecurityLevelSSLv3
//
// CFReadStreamSetProperty(readStream,
// kCFStreamPropertySocketSecurityLevel,
// kCFStreamSocketSecurityLevelTLSv1);
self.inputStream = (NSInputStream *)readStream;
self.outputStream = (NSOutputStream *)writeStream;
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
#pragma mark -
#pragma mark private properties
- (void) setCertificate:(SecCertificateRef) certificate {
if (_certificate != certificate) {
if (_certificate) {
CFRelease(_certificate);
}
_certificate = certificate;
if (_certificate) {
CFRetain(_certificate);
}
}
}
- (void) setInputStream:(NSInputStream *) inputStream {
if (_inputStream != inputStream) {
[_inputStream setDelegate:nil];
[_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[_inputStream close];
[_inputStream release];
_inputStream = inputStream;
[_inputStream retain];
[_inputStream setDelegate:self];
[_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
}
- (void) setOutputStream:(NSOutputStream *) outputStream {
if (_outputStream != outputStream) {
[_outputStream setDelegate:nil];
[_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[_outputStream close];
[_outputStream release];
_outputStream = outputStream;
[_outputStream retain];
[_outputStream setDelegate:self];
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
}
#pragma mark -
#pragma mark NSStreamDelegate
- (void)stream:(NSStream *)aStream
handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
break;
case NSStreamEventHasSpaceAvailable:
NSLog(@"Socket Security Level: %@", [aStream propertyForKey:(NSString *) kCFStreamPropertySocketSecurityLevel]);
NSLog(@"SSL settings: %@", [aStream propertyForKey:(NSString *) kCFStreamPropertySSLSettings]);
if (self.verifyOnHasSpaceAvailable) {
SecPolicyRef policy = SecPolicyCreateSSL(NO, CFSTR("localhost"));
SecTrustRef trust = NULL;
SecTrustCreateWithCertificates([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates],
policy,
&trust);
SecTrustSetAnchorCertificates(trust,
(CFArrayRef) [NSArray arrayWithObject:(id) self.certificate]);
SecTrustResultType trustResultType = kSecTrustResultInvalid;
OSStatus status = SecTrustEvaluate(trust, &trustResultType);
if (status == errSecSuccess) {
// expect trustResultType == kSecTrustResultUnspecified until my cert exists in the keychain
// see technote for more detail: http://developer.apple.com/library/mac/#qa/qa2007/qa1360.html
if (trustResultType == kSecTrustResultUnspecified) {
NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType);
} else {
NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType);
}
} else {
NSLog(@"Creating trust failed: %d", status);
[aStream close];
}
if (trust) {
CFRelease(trust);
}
if (policy) {
CFRelease(policy);
}
} else {
NSLog(@"We can trust this server!");
}
break;
case NSStreamEventErrorOccurred:
if ([[aStream streamError] code] == -9807) { // header file with error code symbol isn't present in ios.
NSLog(@"We cannot trust this certificate.");
} else {
NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]);
}
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
}
@end
最佳答案
显然,CFReadStream
setter 调用的顺序很重要。以下 verifyManually
方法有效:
- (void) verifyManually {
self.inputStream = nil;
self.outputStream = nil;
// we don't want the keychain to accidentally accept our self-signed cert.
SecItemDelete((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
self.certificate, kSecValueRef,
nil]);
self.verifyOnHasSpaceAvailable = YES;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
// Set this kCFStreamPropertySocketSecurityLevel before
// setting kCFStreamPropertySSLSettings.
// Setting kCFStreamPropertySocketSecurityLevel
// appears to override previous settings in kCFStreamPropertySSLSettings
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
NSDictionary *sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
nil];
CFReadStreamSetProperty(readStream,
kCFStreamPropertySSLSettings,
sslSettings);
self.inputStream = (NSInputStream *)readStream;
self.outputStream = (NSOutputStream *)writeStream;
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
关于cocoa - 在一个 CFReadStream 中设置 kCFStreamSSLValidatesCertificateChain 会导致其他 CFReadStreams 不验证证书链,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4779274/
尝试使用集成到 QTCreator 的表单编辑器,但即使我将插件放入 QtCreator.app/Contents/MacOS/designer 也不会显示。不过,相同的 dylib 文件确实适用于独
在此代码示例中。 “this.method2();”之后会读到什么?在返回returnedValue之前会跳转到method2()吗? public int method1(int returnedV
我的项目有通过gradle配置的依赖项。我想添加以下依赖项: compile group: 'org.restlet.jse', name: 'org.restlet.ext.apispark', v
我将把我们基于 Windows 的客户管理软件移植到基于 Web 的软件。我发现 polymer 可能是一种选择。 但是,对于我们的使用,我们找不到 polymer 组件具有表格 View 、下拉菜单
我的项目文件夹 Project 中有一个文件夹,比如 ED 文件夹,当我在 Eclipse 中指定在哪里查找我写入的文件时 File file = new File("ED/text.txt"); e
这是奇怪的事情,这个有效: $('#box').css({"backgroundPosition": "0px 250px"}); 但这不起作用,它只是不改变位置: $('#box').animate
这个问题在这里已经有了答案: Why does OR 0 round numbers in Javascript? (3 个答案) 关闭 5 年前。 Mozilla JavaScript Guide
这个问题在这里已经有了答案: Is the function strcmpi in the C standard libary of ISO? (3 个答案) 关闭 8 年前。 我有一个问题,为什么
我目前使用的是共享主机方案,我不确定它使用的是哪个版本的 MySQL,但它似乎不支持 DATETIMEOFFSET 类型。 是否存在支持 DATETIMEOFFSET 的 MySQL 版本?或者有计划
研究 Seam 3,我发现 Seam Solder 允许将 @Named 注释应用于包 - 在这种情况下,该包中的所有 bean 都将自动命名,就好像它们符合条件一样@Named 他们自己。我没有看到
我知道 .append 偶尔会增加数组的容量并形成数组的新副本,但 .removeLast 会逆转这种情况并减少容量通过复制到一个新的更小的数组来改变数组? 最佳答案 否(或者至少如果是,则它是一个错
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
noexcept 函数说明符是否旨在 boost 性能,因为生成的对象中可能没有记录异常的代码,因此应尽可能将其添加到函数声明和定义中?我首先想到了可调用对象的包装器,其中 noexcept 可能会产
我正在使用 Angularjs 1.3.7,刚刚发现 Promise.all 在成功响应后不会更新 angularjs View ,而 $q.all 会。由于 Promises 包含在 native
我最近发现了这段JavaScript代码: Math.random() * 0x1000000 10.12345 10.12345 >> 0 10 > 10.12345 >>> 0 10 我使用
我正在编写一个玩具(物理)矢量库,并且遇到了 GHC 坚持认为函数应该具有 Integer 的问题。是他们的类型。我希望向量乘以向量以及标量(仅使用 * ),虽然这可以通过仅使用 Vector 来实现
PHP 的 mail() 函数发送邮件正常,但 Swiftmailer 的 Swift_MailTransport 不起作用! 这有效: mail('user@example.com', 'test
我尝试通过 php 脚本转储我的数据,但没有命令行。所以我用 this script 创建了我的 .sql 文件然后我尝试使用我的脚本: $link = mysql_connect($host, $u
使用 python 2.6.4 中的 sqlite3 标准库,以下查询在 sqlite3 命令行上运行良好: select segmentid, node_t, start, number,title
我最近发现了这段JavaScript代码: Math.random() * 0x1000000 10.12345 10.12345 >> 0 10 > 10.12345 >>> 0 10 我使用
我是一名优秀的程序员,十分优秀!