【问题标题】:Xamarin.iOS Keychain hangs when trying to query for a key storing NSData尝试查询存储 NSData 的密钥时,Xamarin.iOS 钥匙串挂起
【发布时间】:2018-12-01 00:44:42
【问题描述】:

我有一个现有的企业部署应用程序,它使用OpenID Connect 进行身份验证。一旦通过身份验证,我将 AuthState 对象存储在 iOS 钥匙串中以确保安全,并且以后能够仅使用 Face/Touch ID 登录用户(假设 AuthState 中的刷新令牌仍然有效)。 AuthState 对象如下所示:

namespace OpenId.AppAuth
{
    [Register ("OIDAuthState", true)]
    public class AuthState : NSObject, INSCoding, INativeObject, IDisposable, INSSecureCoding
    {
        [CompilerGenerated]
        private static readonly IntPtr class_ptr = Class.GetHandle ("OIDAuthState");

        [CompilerGenerated]
        private object __mt_ErrorDelegate_var;

        [CompilerGenerated]
        private object __mt_StateChangeDelegate_var;

        public override IntPtr ClassHandle {
            get;
        }

        [CompilerGenerated]
        public virtual NSError AuthorizationError {
            [Export ("authorizationError")]
            get;
        }

        [CompilerGenerated]
        public virtual IAuthStateErrorDelegate ErrorDelegate {
            [Export ("errorDelegate", ArgumentSemantic.Weak)]
            get;
            [Export ("setErrorDelegate:", ArgumentSemantic.Weak)]
            set;
        }

        [CompilerGenerated]
        public virtual bool IsAuthorized {
            [Export ("isAuthorized")]
            get;
        }

        [CompilerGenerated]
        public virtual AuthorizationResponse LastAuthorizationResponse {
            [Export ("lastAuthorizationResponse")]
            get;
        }

        [CompilerGenerated]
        public virtual RegistrationResponse LastRegistrationResponse {
            [Export ("lastRegistrationResponse")]
            get;
        }

        [CompilerGenerated]
        public virtual TokenResponse LastTokenResponse {
            [Export ("lastTokenResponse")]
            get;
        }

        [CompilerGenerated]
        public virtual string RefreshToken {
            [Export ("refreshToken")]
            get;
        }

        [CompilerGenerated]
        public virtual string Scope {
            [Export ("scope")]
            get;
        }

        [CompilerGenerated]
        public virtual IAuthStateChangeDelegate StateChangeDelegate {
            [Export ("stateChangeDelegate", ArgumentSemantic.Weak)]
            get;
            [Export ("setStateChangeDelegate:", ArgumentSemantic.Weak)]
            set;
        }

        public static IAuthorizationFlowSession PresentAuthorizationRequest (AuthorizationRequest authorizationRequest, UIViewController presentingViewController, AuthStateAuthorizationCallback callback);

        [CompilerGenerated]
        [DesignatedInitializer]
        [EditorBrowsable (EditorBrowsableState.Advanced)]
        [Export ("initWithCoder:")]
        public AuthState (NSCoder coder)
            : base (NSObjectFlag.Empty);

        [CompilerGenerated]
        [EditorBrowsable (EditorBrowsableState.Advanced)]
        protected AuthState (NSObjectFlag t)
            : base (t);

        [CompilerGenerated]
        [EditorBrowsable (EditorBrowsableState.Advanced)]
        protected internal AuthState (IntPtr handle)
            : base (handle);

        [Export ("initWithAuthorizationResponse:")]
        [CompilerGenerated]
        public AuthState (AuthorizationResponse authorizationResponse)
            : base (NSObjectFlag.Empty);

        [Export ("initWithAuthorizationResponse:tokenResponse:")]
        [CompilerGenerated]
        public AuthState (AuthorizationResponse authorizationResponse, TokenResponse tokenResponse)
            : base (NSObjectFlag.Empty);

        [Export ("initWithRegistrationResponse:")]
        [CompilerGenerated]
        public AuthState (RegistrationResponse registrationResponse)
            : base (NSObjectFlag.Empty);

        [Export ("initWithAuthorizationResponse:tokenResponse:registrationResponse:")]
        [DesignatedInitializer]
        [CompilerGenerated]
        public AuthState (AuthorizationResponse authorizationResponse, TokenResponse tokenResponse, RegistrationResponse registrationResponse)
            : base (NSObjectFlag.Empty);

        [Export ("encodeWithCoder:")]
        [CompilerGenerated]
        [Preserve (Conditional = true)]
        public virtual void EncodeTo (NSCoder encoder);

        [Export ("performActionWithFreshTokens:")]
        [CompilerGenerated]
        public unsafe virtual void PerformWithFreshTokens ([BlockProxy (typeof(ObjCRuntime.Trampolines.NIDAuthStateAction))] AuthStateAction action);

        [Export ("performActionWithFreshTokens:additionalRefreshParameters:")]
        [CompilerGenerated]
        public unsafe virtual void PerformWithFreshTokens ([BlockProxy (typeof(ObjCRuntime.Trampolines.NIDAuthStateAction))] AuthStateAction action, NSDictionary<NSString, NSString> additionalParameters);

        [Export ("authStateByPresentingAuthorizationRequest:UICoordinator:callback:")]
        [CompilerGenerated]
        public unsafe static IAuthorizationFlowSession PresentAuthorizationRequest (AuthorizationRequest authorizationRequest, IAuthorizationUICoordinator UICoordinator, [BlockProxy (typeof(ObjCRuntime.Trampolines.NIDAuthStateAuthorizationCallback))] AuthStateAuthorizationCallback callback);

        [Export ("setNeedsTokenRefresh")]
        [CompilerGenerated]
        public virtual void SetNeedsTokenRefresh ();

        [Export ("tokenRefreshRequest")]
        [CompilerGenerated]
        public virtual TokenRequest TokenRefreshRequest ();

        [Export ("tokenRefreshRequestWithAdditionalParameters:")]
        [CompilerGenerated]
        public virtual TokenRequest TokenRefreshRequest (NSDictionary<NSString, NSString> additionalParameters);

        [Export ("updateWithAuthorizationResponse:error:")]
        [CompilerGenerated]
        public virtual void Update (AuthorizationResponse authorizationResponse, NSError error);

        [Export ("updateWithTokenResponse:error:")]
        [CompilerGenerated]
        public virtual void Update (TokenResponse tokenResponse, NSError error);

        [Export ("updateWithAuthorizationError:")]
        [CompilerGenerated]
        public virtual void Update (NSError authorizationError);

        [Export ("updateWithRegistrationResponse:")]
        [CompilerGenerated]
        public virtual void UpdateWithRegistrationResponse (RegistrationResponse registrationResponse);

        [CompilerGenerated]
        protected override void Dispose (bool disposing);
    }
}

iOS 12.1 更新之前一切正常

问题: 安装 ios 12.1 更新后,仅在以发布模式运行时(在连接到 VS for Mac 调试器时在开发模式下调试时工作)查询存储二进制文件的钥匙串中的现有密钥NSData 表示此 AuthState 对象挂起并最终终止应用程序,因为超过 10 秒无响应。

有没有人遇到过类似的问题?如果有人能阐明这里可能发生的事情或指出我正确的方向,那就太棒了。

附加信息:

我如何获得NSData 对象的AppAuth 二进制表示:

NSData authStateData = NSKeyedArchiver.ArchivedDataWithRootObject(authState);

我如何将这个NSData 保存到钥匙串中:

    var secAccess = new SecAccessControl(SecAccessible.WhenUnlockedThisDeviceOnly, SecAccessControlCreateFlags.UserPresence);
    var secRecord = new SecRecord(SecKind.GenericPassword)
    {
        Account = "keystring",
        Service = "ServiceNameString",
        Label = "keystring",
        ValueData = authStateData,
        AccessControl = secAccess
    };
    var result = SecKeyChain.Add(secRecord);

我如何查询keychain中的现有数据:

var searchRecord = new SecRecord(SecKind.GenericPassword)
{
    Service = ServiceName,
    Label = key,
};
var match = SecKeyChain.QueryAsRecord(searchRecord, out SecStatusCode resultCode);

设备日志中没有明显的错误,我检查了。我错过的 iOS 12.1 中是否有重大影响?

更新:我将代码重构为仅将加密密钥密码存储在钥匙串中而不是整个 AuthState 对象中,并将序列化的 AuthState 加密存储到本地文件中。仍然看到相同的问题,在设备上的调试模式下,一切正常,从钥匙串中写入和读取都很好,当在同一设备上没有附加调试器的情况下运行时,相同的构建,写入正常,在成功验证 TouchID/FaceID 后读取挂起时,是Xamarin.iOS SDK 中是否存在未赶上导致此问题的最新 iOS 中的某些更改的错误?

【问题讨论】:

    标签: ios xamarin.ios keychain appauth


    【解决方案1】:

    您应该启用钥匙串共享权利。

    在 Entitlements.plist 中

    这里有一个similar case,你可以参考。

    【讨论】:

    • 感谢您的回复,但我很确定不是这样。我在将数据保存到钥匙串时没有任何问题,并且一直到 12.0 都可以很好地检索它。此外,我正在做的所有测试都是在我以调试模式或发布模式运行的物理设备天气上完成的。我已经尝试显式启用此权利,但无济于事。
    【解决方案2】:

    我已经解决了我遇到的问题,它似乎是某种竞争条件导致应用程序被锁定,因为调用保存到钥匙串是在主线程上完成的。通常,这个调用非常快,过去锁定任何东西都没有问题,但 iOS 12.1 更新中的某些东西改变了这一点。无论如何,我只是在后台线程中显式运行将加密密码保存到钥匙串中的代码,从而解决了问题:

    Task.Run(() =>
    {
        var keychain = new KeyChain();
        keychain.SetValueForKey("securedvalue", "securedvaluekey");
    }).ConfigureAwait(false);
    

    【讨论】:

      猜你喜欢
      • 2020-09-13
      • 2021-10-01
      • 1970-01-01
      • 2018-08-29
      • 2020-03-16
      • 1970-01-01
      • 2017-05-11
      • 2023-03-29
      • 2017-09-05
      相关资源
      最近更新 更多