<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

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

          什么是 WaxSealCore

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

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

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

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

          4. 詳盡的文檔支持

          什么是 Keychain Services

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

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

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

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

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

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

          -- David Chisnall, Cocoa Programming Developer's Handbook

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

          Keychain Services vs. WaxSealCore

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

          • 使用一個顯示指定的密碼常見一個空的 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.
          • 查找下面截圖中的這個密碼項(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):

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

          NSError* error = nil;
          
          WSCPassphraseItem* IMDbLoginPassphrase = ( WSCPassphraseItem* )[ [ WSCKeychain login ]
              findFirstKeychainItemSatisfyingSearchCriteria: @{ WSCKeychainItemAttributeLabel : @"secure.imdb.com"
                                                              , WSCKeychainItemAttributeProtocol : WSCInternetProtocolCocoaValue( WSCInternetProtocolTypeHTTPS )
                                                              , WSCKeychainItemAttributeComment : @"這是一個用于演示 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!" );

          簡單地進(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 = @"這是一個用于演示 WaxSealCore 的密碼項(xiàng)";
                  }
              }
          else
              NSLog( @"I'm so sorry!" );

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

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

          Next Step

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

          獲取 WaxSealCore

          聯(lián)系作者

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

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          編輯 分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色日韩网站 | 91精品国产福利 | 成人区精品一区二区婷婷255 | 91成人做爰A片无遮挡直播 | 伊人午夜视频 |