1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        WaxSealCoreOS X Keychain 的面向?qū)ο蠓庋b

        聯(lián)合創(chuàng)作 · 2023-09-30 23:12

        什么是 WaxSealCore

        WaxSealCore 是一個(gè)受 Cocoa 設(shè)計(jì)影響的代碼庫,由 @開源中國真理部部長(zhǎng) 用 Objective-C 編寫。其對(duì) OS X Keychain Services API 進(jìn)行了面向?qū)ο蠓庋b,使得 Mac 開發(fā)者更容易地將 Keychain 機(jī)制融入到自己的 app 中。相對(duì)于 Apple 官方的 Keychain Services API 來說:

        1. 完全面向?qū)ο?

        2. API 風(fēng)格和 Cocoa 非常接近,熟練的 Mac 開發(fā)者可以迅速上手

        3. 支持基于 Unicode 字符搜索密碼項(xiàng)

        4. 詳盡的文檔支持

        什么是 Keychain Services

        OS X 和 iOS 開發(fā)者對(duì) “鑰匙串 API”Keychain Services API,為消歧義,下文都使用英文名稱)應(yīng)該都有所耳聞,計(jì)算機(jī)用戶總是必須管理許多用戶 ID 和密碼,比如在瀏覽器中的 Twitter,F(xiàn)acebook,OSChina.net 等網(wǎng)站,以及 Evernote,Skype,Telegram 等桌面和移動(dòng) app 的登錄密碼。這些服務(wù)在你能夠使用之前都需要通過密碼來驗(yàn)證使用者的身份。因?yàn)槊艽a繁雜,所以很多用戶總是通過起一個(gè)非常簡(jiǎn)單非常容易記住的密碼,并且為多個(gè)服務(wù)使用相同的密碼來應(yīng)付這件事(甚至將密碼寫在隨手能夠找到的小紙條上的也大有人在)。這些做法都大大削弱了密碼的安全性。

        所以在 OS X 和 iOS 中有一個(gè)被稱為 Keychain 的機(jī)制(平時(shí)你可以通過 OS X 自帶的 Keychain Access 應(yīng)用訪問系統(tǒng)中的 Keychain),Keychain 是一種具有特殊格式的文件類型(.keychain 文件),其是一個(gè)安全的加密容器,其本身可以使用一個(gè)主密碼(master password)進(jìn)行鎖定,除了密碼的擁有人,沒有人能夠訪問這個(gè)加密容器中的任何內(nèi)容。OS X 和 iOS 用戶在訪問一個(gè)新的網(wǎng)站時(shí),就會(huì)被詢問是否要保存網(wǎng)站的密碼,以便下次自動(dòng)登錄,當(dāng)用戶點(diǎn)擊“保存”時(shí),用戶的密碼就是被保存到這個(gè)加密容器內(nèi),Keychain 會(huì)對(duì)你輸入的密碼進(jìn)行高強(qiáng)度的加密,然后存儲(chǔ)在其中,下次訪問時(shí)通過解密密碼既可以實(shí)現(xiàn)自動(dòng)登錄。

        Keychain Services 是 Keychain 機(jī)制的編程接口。OS X/iOS 開發(fā)者在開發(fā)應(yīng)用時(shí),只需要調(diào)用這套中的函數(shù),就可以將自己的應(yīng)用中用到的密碼存儲(chǔ)到 OS X/iOS 的 Keychain 中,下次需要使用密碼時(shí)可以直接從 Keychain 中進(jìn)行獲取而不必每次都讓用戶重新輸入。除此之外,對(duì)于 Mac 開發(fā)者來說,你的應(yīng)用還可以和其他應(yīng)用共享同一個(gè)服務(wù)的密碼。Keychain Services 是一個(gè)很方便的 API,它無需開發(fā)者自己實(shí)現(xiàn)一套密碼管理機(jī)制。

        事實(shí)上,OS X 版的 Firefox 和 Thunderbird 就有一個(gè)廣為詬病的問題,就是它們都使用自己實(shí)現(xiàn)的密碼管理器而不使用 Keychain,這有兩個(gè)弊端:

        • OS X 用戶習(xí)慣使用 Keychain 并建立了信任。如果提供自己的密碼管理器,那么用戶對(duì)它的信任度跟對(duì)你的信任度是一樣的,一般來說不如他們對(duì) Apple 公司的信任度。

        • 用戶不能在你的應(yīng)用程序之外訪問密碼。例如,Mac 版的 Chrome,Safari 和 Opera 就都能夠共享 Web 的登錄資料,因?yàn)樗鼈兌际褂?Keychain,并且用戶可以用 Keychain Access 應(yīng)用來修改他們看到的密碼。

        -- David Chisnall, Cocoa Programming Developer's Handbook

        上面只是簡(jiǎn)單介紹了一下 Keychain 機(jī)制和它的 API,它們的功能遠(yuǎn)不止存取密碼這么簡(jiǎn)單,只不過這些功能是最常用到的。Keychain Services 這套 API 很強(qiáng)大,但是缺點(diǎn)就是,它的接口是純 C 的,丑陋,復(fù)雜,并且因?yàn)樗腔?Core Foundation 的,所以需要你手動(dòng)管理內(nèi)存(不像 Cocoa/Cocoa-Touch 可以利用引用計(jì)數(shù)和自動(dòng)釋放池),所以極易產(chǎn)生 bug。再加上 Keychain Services 的文檔很古老,有很多錯(cuò)誤都會(huì)無故地增大學(xué)習(xí)曲線,所以,最終,我實(shí)在受夠它了,懶惰是程序員得美德,于是我找了一些開源的 Objective-C wrapper,這些 wrapper 雖然簡(jiǎn)化了使用,但是功能上要么太簡(jiǎn)陋(只能存取 generic password 和 Internet password,而沒有實(shí)現(xiàn) Access Control List 這類強(qiáng)大的功能),要么年代久遠(yuǎn)。所以決定自己寫一個(gè)全特性的封裝,而不僅是限于存取密碼這種簡(jiǎn)單的功能。

        Keychain Services vs. WaxSealCore

        @紅薯 說得好,框架的作者們不要總吹噓自己的框架多么好用,而是要看你的框架能夠?qū)崒?shí)在在地為開發(fā)者節(jié)省多少代碼,所以用兩個(gè)功能來比較一下 WaxSealCore 和純 C 的 Keychain Services。

        • 使用一個(gè)顯示指定的密碼常見一個(gè)空的 Keychain

        使用 Keychain Services 的純 C 接口實(shí)現(xiàn):

        OSStatus resultCode = errSecSuccess;
        SecKeychainRef secEmptyKeychain = NULL;
        NSURL* URL = [ [ [ NSBundle mainBundle ] bundleURL ] URLByAppendingPathComponent: @"EmptyKeychainForWiki.keychain" ];
        char* passphrase = "waxsealcore";
        
        // Create an empty keychain with given passphrase
        resultCode = SecKeychainCreate( URL.path.UTF8String
                                      , ( UInt32 )strlen( passphrase )
                                      , ( void const* )passphrase
                                      , ( Boolean )NO
                                      , NULL
                                      , &secEmptyKeychain
                                      );
        
        NSAssert( resultCode == errSecSuccess, @"Failed to create new empty keychain" );
        
        resultCode = SecKeychainDelete( secEmptyKeychain );
        NSAssert( resultCode == errSecSuccess, @"Failed to delete the given keychain" );
        
        if ( secEmptyKeychain )
            // Keychain Services is based on Core Foundation,
            // you have to manage the memory manually
            CFRelease( secEmptyKeychain );

        使用 WaxSealCore 實(shí)現(xiàn):

        NSError* error = nil;
        
        // Create an empty keychain with given passphrase
        WSCKeychain* emptyKeychain = [ [ WSCKeychainManager defaultManager ]
            createKeychainWithURL: [ [ [ NSBundle mainBundle ] bundleURL ] URLByAppendingPathComponent: @"EmptyKeychainForWiki.keychain" ]
                       passphrase: @"waxsealcore"
                   becomesDefault: NO
                            error: &error ];
        
        // You have no need for managing the memory manually,
        // emptyKeychain will be released automatically.
        • 查找下面截圖中的這個(gè)密碼項(xiàng),并且獲取它的賬戶名,密碼和注釋信息(注釋信息含有中文,Keychain Services 無法進(jìn)行查找)


        使用 Keychain Services 的純 C 接口實(shí)現(xiàn):

        OSStatus resultCode = errSecSuccess;
        
        // Attributes that will be used for constructing search criteria
        char* label = "secure.imdb.com";
        SecProtocolType* ptrProtocolType = malloc( sizeof( SecProtocolType ) );
        *ptrProtocolType = kSecProtocolTypeHTTPS;
        
        SecKeychainAttribute attrs[] = { { kSecLabelItemAttr, ( UInt32 )strlen( label ), ( void* )label }
                                       , { kSecProtocolItemAttr, ( UInt32 )sizeof( SecProtocolType ), ( void* )ptrProtocolType }
                                       };
        
        SecKeychainAttributeList attrsList = { sizeof( attrs ) / sizeof( attrs[ 0 ] ), attrs };
        
        // Creates a search object matching the given list of search criteria.
        SecKeychainSearchRef searchObject = NULL;
        if ( ( resultCode = SecKeychainSearchCreateFromAttributes( NULL
                                                                 , kSecInternetPasswordItemClass
                                                                 , &attrsList
                                                                 , &searchObject
                                                                 ) ) == errSecSuccess )
            {
            SecKeychainItemRef matchedItem = NULL;
        
            // Finds the next keychain item matching the given search criteria.
            while ( ( resultCode = SecKeychainSearchCopyNext( searchObject, &matchedItem ) ) != errSecItemNotFound )
                {
                SecKeychainAttribute theAttributes[] = { { kSecAccountItemAttr, 0, NULL }
                                                       , { kSecCommentItemAttr, 0, NULL }
                                                       };
        
                SecKeychainAttributeList theAttrList = { sizeof( theAttributes ) / sizeof( theAttributes[ 0 ] ), theAttributes };
                UInt32 lengthOfPassphrase = 0;
                char* passphraseBuffer = NULL;
                if ( ( resultCode = SecKeychainItemCopyContent( matchedItem
                                                              , NULL
                                                              , &theAttrList
                                                              , &lengthOfPassphrase
                                                              , ( void** )&passphraseBuffer
                                                              ) ) == errSecSuccess )
                    {
                    NSLog( @"\n==============================\n" );
                    NSLog( @"Passphrase: %@", [ [ [ NSString alloc ] initWithBytes: passphraseBuffer length: lengthOfPassphrase encoding: NSUTF8StringEncoding ] autorelease ] );
        
                    for ( int _Index = 0; _Index < theAttrList.count; _Index++ )
                        {
                        SecKeychainAttribute attrStruct = theAttrList.attr[ _Index ];
                        NSString* attributeValue = [ [ [ NSString alloc ] initWithBytes: attrStruct.data length: attrStruct.length encoding: NSUTF8StringEncoding ] autorelease ];
        
                        if ( attrStruct.tag == kSecAccountItemAttr )
                            NSLog( @"IMDb User Name: %@", attributeValue );
                        else if ( attrStruct.tag == kSecCommentItemAttr )
                            NSLog( @"Comment: %@", attributeValue );
                        }
        
                    NSLog( @"\n==============================\n" );
                    }
        
                SecKeychainItemFreeContent( &theAttrList, passphraseBuffer );
                CFRelease( matchedItem );
                }
            }
        
        if ( ptrProtocolType )
            free( ptrProtocolType );
        
        if ( searchObject )
            CFRelease( searchObject );

        使用 WaxSealCore 實(shí)現(xiàn):

        只需一個(gè)方法的調(diào)用即可實(shí)現(xiàn):

        NSError* error = nil;
        
        WSCPassphraseItem* IMDbLoginPassphrase = ( WSCPassphraseItem* )[ [ WSCKeychain login ]
            findFirstKeychainItemSatisfyingSearchCriteria: @{ WSCKeychainItemAttributeLabel : @"secure.imdb.com"
                                                            , WSCKeychainItemAttributeProtocol : WSCInternetProtocolCocoaValue( WSCInternetProtocolTypeHTTPS )
                                                            , WSCKeychainItemAttributeComment : @"這是一個(gè)用于演示 WaxSealCore 的密碼項(xiàng)"
                                                            }
                                                itemClass: WSCKeychainItemClassInternetPassphraseItem
                                                    error: &error ];
        
        // WaxSealCore supports Unicode-based search, so you can use Emoji or Chinese in your search criteria.
        // One step. So easy, is not it?

        打印賬戶名,密碼,和注釋,并且更改注釋內(nèi)容:

        if ( IMDbLoginPassphrase )
            {
            NSLog( @"==============================" );
            // Use the `account` property
            NSLog( @"IMDb User Name: %@", IMDbLoginPassphrase.account );
        
            // Use the `passphrase` property
            NSLog( @"Passphrase: %@", [ [ [ NSString alloc ] initWithData: IMDbLoginPassphrase.passphrase encoding: NSUTF8StringEncoding ] autorelease ] );
        
            // Use the `comment` property
            NSLog( @"Comment: %@", IMDbLoginPassphrase.comment );
            NSLog( @"==============================" );
        
            // -setComment:
            IMDbLoginPassphrase.comment = @"IMDb Passphrase";
            }
        else
            NSLog( @"I'm so sorry!" );

        簡(jiǎn)單地進(jìn)行批量搜索:

        // Find all the Internet passphrases that met the given search criteria
        NSArray* passphrases = [ [ WSCKeychain login ]
            // Batch search
            findAllKeychainItemsSatisfyingSearchCriteria: @{ WSCKeychainItemAttributeLabel : @"secure.imdb.com"
                                                           , WSCKeychainItemAttributeProtocol : WSCInternetProtocolCocoaValue( WSCInternetProtocolTypeHTTPS )
                                                           , WSCKeychainItemAttributeComment : @"IMDb Passphrase"
                                                           }
                                               itemClass: WSCKeychainItemClassInternetPassphraseItem
                                                   error: &error ];
        if ( passphrases.count != 0 )
            {
            for ( WSCPassphraseItem* _Passphrase in passphrases )
                {
                NSLog( @"==============================" );
                NSLog( @"IMDb User Name: %@", IMDbLoginPassphrase.account );
                NSLog( @"Passphrase: %@", [ [ [ NSString alloc ] initWithData: IMDbLoginPassphrase.passphrase encoding: NSUTF8StringEncoding ] autorelease ] );
                NSLog( @"Comment: %@", IMDbLoginPassphrase.comment );
                NSLog( @"==============================" );
        
                _Passphrase.comment = @"這是一個(gè)用于演示 WaxSealCore 的密碼項(xiàng)";
                }
            }
        else
            NSLog( @"I'm so sorry!" );

        上面的演示可以看到,使用 Keychain Services 費(fèi)很大力氣需要完成的工作,用 WaxSealCore 寥寥幾行代碼即可做到。除此之外,WaxSealCore 還簡(jiǎn)化了 Keychain 中的 Access Control List 機(jī)制,你可以更容易地使用 Keychain 更強(qiáng)大的功能。更多 API 的使用方式,可以參考我正在維護(hù)的一個(gè) Wiki,歡迎任何人來編輯這個(gè) wiki 頁面。

        WaxSealCore 是自由軟件,在 MIT 許可證下發(fā)布,你可以在這里獲取源碼,自由修改或重新分發(fā)源代碼。如果不想自己編譯代碼,可以在這里獲取到我用我的開發(fā)者證書簽名的二進(jìn)制框架包。

        Next Step

        Keychain Services 不僅僅能夠存取普通密碼,同時(shí)還能夠存取數(shù)字證書(digital certificates),私鑰(private keys)等私密數(shù)據(jù),WaxSealCore 下一個(gè)版本就要提供對(duì)跟 Keychain Services 同處于 Security.framework 框架中的 Certificate, Key, and Trust Services API 的封裝,將融合對(duì)數(shù)字證書,對(duì)稱密鑰和非對(duì)稱密鑰的存取與操作,敬請(qǐng)期待。

        獲取 WaxSealCore

        聯(lián)系作者

        如果你有任何問題,可以我發(fā)郵件 dG9yaW5Aa3dvay5pbQ==(base64ed)。

        瀏覽 22
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        WaxSealCoreOS X Keychain 的面向?qū)ο蠓庋b
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            好硬好湿好爽再深一点h视频 | 精品国产91久久久久久果冻传媒 | 九九九免费视频 | 久久久久三级电影 | 欧洲免费性爱视频 | 欧美77777色婷婷 | 日韩人妻一区二区三区三 | 男添女人下面的视频过程 | 亚洲一区二区网站 | 手机看色片|