2

我想用 存储和检索密码Windows Hello。用户可以在登录时选择是要手动输入密码,还是要使用Windows Hello解锁(然后检索上次使用的密码,并为用户填写)。

如果Windows Hello设置正确,则文档中有两个用例。
一键解锁:

UserConsentVerificationResult consentResult = await UserConsentVerifier.RequestVerificationAsync("userMessage");
if (consentResult.Equals(UserConsentVerificationResult.Verified))
{
   // continue
}

和一个用于签署来自服务器的消息:

var openKeyResult = await KeyCredentialManager.OpenAsync(AccountId);
if (openKeyResult.Status == KeyCredentialStatus.Success)
{
    var userKey = openKeyResult.Credential;
    var publicKey = userKey.RetrievePublicKey();
    //the message is the challenge from the server
    var signResult = await userKey.RequestSignAsync(message);

    if (signResult.Status == KeyCredentialStatus.Success)
    {
        //the with the private key of the user signed message        
        return signResult.Result;
    }
}

两者对我的用例都不是很有用:我想要一种对称的方式来存储和检索密码。

简而言之,我的问题是:
有没有办法对称地存储数据Windows Hello

相关文档:
https ://docs.microsoft.com/en-us/windows/uwp/security/microsoft-passport

4

2 回答 2

3

我通过使用 Windows Hello 生成的密码加密/解密我想要存储的秘密解决了这个问题。密码是对固定消息的签名。

一个完整的代码示例(未经测试)来说明我的观点:

const accountId = "A ID for this key";
const challenge = "sign this challenge using Windows Hello to get a secure string";

public async Task<byte[]> GetSecureEncryptionKey() {
    // if first time; we first need to create a key
    if (firstTime) {
        if (!await this.CreateKeyAsync()) {
            return null;
        }
    }

    // get the key using Windows Hellp
    return await this.GetKeyAsync();
}

private async Task<bool> CreateKeyAsync() {
    if (!await KeyCredentialManager.IsSupportedAsync()) {
        return false;
    }

    // if app opened for the first time, we need to create an account first
    var keyCreationResult = await KeyCredentialManager.RequestCreateAsync(AccountId, KeyCredentialCreationOption.ReplaceExisting);
    return keyCreationResult.Status == KeyCredentialStatus.Success);
}

private async Task<byte[]> GetKeyAsync() {
    var openKeyResult = await KeyCredentialManager.OpenAsync(AccountId);

    if (openKeyResult.Status == KeyCredentialStatus.Success)
    {
        // convert our string challenge to be able to sign it 
        var buffer = CryptographicBuffer.ConvertStringToBinary(
            challenge, BinaryStringEncoding.Utf8
        );
        
        // request a sign from the user
        var signResult = await openKeyResult.Credential.RequestSignAsync(buffer);

        // if successful, we can use that signature as a key
        if (signResult.Status == KeyCredentialStatus.Success)
        {
            return signResult.Result.ToArray();
        }
    }

    return null;
}

完整的源代码在 github 上,并展示了我如何将这些概念集成到应用程序中。

但是,这会滥用用于不同目的的密码原语,这是非常危险的。在采用此解决方法之前,请考虑寻找更合理的方法。

具体注意事项:

  • 这假设签名不是随机的。Windows Hello 目前就是这种情况,但将来可能会改变。
  • 使用签名作为密钥时,请确保使用整个签名。如果加密/解密密钥需要 256 位,您可以使用 SHA-256 算法将签名映射到密钥,例如:key = sha256(signature);
于 2019-01-22T10:10:36.973 回答
1

您可以按如下方式使用 PasswordVault 来设置密码:

    private void SetPassword(string password, string userName)
    {
        PasswordVault myVault = new PasswordVault();
        myVault.Add(new PasswordCredential("Your App ID", userName, password));
    }

删除密码:

    private void RemovePassword(string userName)
    {
        PasswordVault myVault = new PasswordVault();

        var password = myVault.Retrieve("Your App ID", userName);
        if (password != null)
            myVault.Remove(password);
    }

如果您想将它与 Windows Hello 一起使用:

    public async Task<string> SignInAsync(string userName)
    {
        var result = await UserConsentVerifier.RequestVerificationAsync(requestMessage);
        if (result != UserConsentVerificationResult.Verified)
            return null;

        var vault = new PasswordVault();
        var credentials = vault.Retrieve("Your App ID", userName);

        return credentials?.Password;
    }

这将在访问密码值之前检查 Windows Hello

于 2019-01-18T10:58:25.727 回答