WebAuthn(也叫Web Authentication API)是Credential Management API的一個擴展,它經過公鑰保證了免密認證的安全性。咱們經過一個Demo來看它作了什麼。git
WebAuthn用公鑰證書代替了密碼,完成用戶的註冊和身份認證(登陸)。它更像是現有身份認證的加強或補充。爲了保證通訊數據安全,通常基於HTTPS(TLS)通訊。在這個過程當中,有4個模塊。github
註冊過程分爲7個階段web
瀏覽器發起註冊請求,包含用戶基本信息。瀏覽器
這是一個異步任務,JS腳本調用瀏覽器的navigator.credentials.create建立證書。安全
getMakeCredentialsChallenge({username, name})
.then((response) => {
let publicKey = preformatMakeCredReq(response);
return navigator.credentials.create({ publicKey })
})
.then((response) => {
console.log(response);
let makeCredResponse = publicKeyCredentialToJSON(response);
return sendWebAuthnResponse(makeCredResponse)
})
.then((response) => {
if(response.status === 'ok') {
loadMainContainer()
} else {
alert(`Server responed with error. The message is: ${response.message}`);
}
})
.catch((error) => alert(error))
複製代碼
瀏覽器到認證模塊之間的數據用JSON格式傳遞,幷包含如下內容:服務器
瀏覽器會以{ AttestationObject, ClientDataJSON }的格式返回給JS腳本。異步
檢查Challenge、Origin,並存儲公鑰和用戶信息。ui
一樣分爲7步,多數內容與註冊類似。url
瀏覽器發起登陸請求,包含用戶基本信息。spa
JS腳本調用瀏覽器的navigator.credentials.get檢索證書。
getGetAssertionChallenge({username})
.then((response) => {
console.log(response)
let publicKey = preformatGetAssertReq(response);
return navigator.credentials.get({ publicKey })
})
.then((response) => {
console.log(response)
let getAssertionResponse = publicKeyCredentialToJSON(response);
return sendWebAuthnResponse(getAssertionResponse)
})
.then((response) => {
if(response.status === 'ok') {
loadMainContainer()
} else {
alert(`Server responed with error. The message is: ${response.message}`);
}
})
.catch((error) => alert(error))
複製代碼
檢查Challenge、Origin,並驗證公鑰和用戶信息。
let verifyAuthenticatorAssertionResponse = (webAuthnResponse, authenticators) => {
let authr = findAuthr(webAuthnResponse.id, authenticators);
let authenticatorData = base64url.toBuffer(webAuthnResponse.response.authenticatorData);
let response = {'verified': false};
if(authr.fmt === 'fido-u2f') {
let authrDataStruct = parseGetAssertAuthData(authenticatorData);
if(!(authrDataStruct.flags & U2F_USER_PRESENTED))
throw new Error('User was NOT presented durring authentication!');
let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))
let signatureBase = Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, clientDataHash]);
let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));
let signature = base64url.toBuffer(webAuthnResponse.response.signature);
response.verified = verifySignature(signature, signatureBase, publicKey)
if(response.verified) {
if(response.counter <= authr.counter)
throw new Error('Authr counter did not increase!');
authr.counter = authrDataStruct.counter
}
}
return response
}
複製代碼