2016-08-14 22 views
8

doğrulamak için, Kenar artık Windows Merhaba kullanarak biyometrik kimlik doğrulama (Bkz https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/device/web-authentication/, https://blogs.windows.com/msedgedev/2016/04/12/a-world-without-passwords-windows-hello-in-microsoft-edge/)Biyometrik giriş Go (webauthn), nasıl çok yeni, Windows Yıldönümü güncellemeyle imzayı

destekler C# bazı örnekleri var, PHP ve Node.js ve Go'da çalışmasını sağlamaya çalışıyorum.

JS aşağıdaki işler

(Ben meydan okuma ve anahtar kodlanmış olan):

seferde ise
function parseBase64(s) { 
    s = s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, '');   
    return new Uint8Array(Array.prototype.map.call(atob(s), function (c) { return c.charCodeAt(0) }));   
} 

function concatUint8Array(a1,a2) { 
    var d = new Uint8Array(a1.length + a2.length); 
    d.set(a1); 
    d.set(a2,a1.length); 
    return d; 
} 

var credAlgorithm = "RSASSA-PKCS1-v1_5"; 
var id,authenticatorData,signature,hash; 
webauthn.getAssertion("chalenge").then(function(assertion) { 
    id = assertion.credential.id; 
    authenticatorData = assertion.authenticatorData; 
    signature = assertion.signature; 
    return crypto.subtle.digest("SHA-256",parseBase64(assertion.clientData)); 
}).then(function(h) { 
    hash = new Uint8Array(h); 
    var publicKey = "{\"kty\":\"RSA\",\"alg\":\"RS256\",\"ext\":false,\"n\":\"mEqGJwp0GL1oVwjRikkNfzd-Rkpb7vIbGodwQkTDsZT4_UE02WDaRa-PjxzL4lPZ4rUpV5SqVxM25aEIeGkEOR_8Xoqx7lpNKNOQs3E_o8hGBzQKpGcA7de678LeAUZdJZcnnQxXYjNf8St3aOIay7QrPoK8wQHEvv8Jqg7O1-pKEKCIwSKikCFHTxLhDDRo31KFG4XLWtLllCfEO6vmQTseT-_8OZPBSHOxR9VhIbY7VBhPq-PeAWURn3G52tQX-802waGmKBZ4B87YtEEPxCNbyyvlk8jRKP1KIrI49bgJhAe5Mow3yycQEnGuPDwLzmJ1lU6I4zgkyL1jI3Ghsw\",\"e\":\"AQAB\"}"; 
    return crypto.subtle.importKey("jwk",JSON.parse(publicKey),credAlgorithm,false,["verify"]); 
}).then(function(key) { 
    return crypto.subtle.verify({name:credAlgorithm, hash: { name: "SHA-256" }},key,parseBase64(signature),concatUint8Array(parseBase64(authenticatorData),hash)); 
}).then(function(result) { 
    console.log("ID=" + id + "\r\n" + result); 
}).catch(function(err) { 
    console.log('got err: ', err); 
}); 

Ben req dizeleri ile bir yapıdır (yukarıda JS kodu eşleşecek anlamına aşağıdaki kodu var) JSON talep vücuttan:

func webauthnSigninConversion(g string) ([]byte, error) { 
    g = strings.Replace(g, "-", "+", -1) 
    g = strings.Replace(g, "_", "/", -1) 
    switch(len(g) % 4) { // Pad with trailing '='s 
    case 0: 
        // No pad chars in this case 
    case 2: 
        // Two pad chars 
        g = g + "==" 
    case 3: 
        // One pad char 
        g = g + "="; 
    default: 
        return nil, fmt.Errorf("invalid string in public key") 
    } 
    b, err := base64.StdEncoding.DecodeString(g) 
    if err != nil { 
        return nil, err 
    } 
    return b, nil 
} 


clientData, err := webauthnSigninConversion(req.ClientData) 
if err != nil { 
    return err 
} 

authenticatorData, err := webauthnSigninConversion(req.AuthenticatorData) 
if err != nil { 
    return err 
} 

signature, err := webauthnSigninConversion(req.Signature) 
if err != nil { 
    return err 
} 

publicKey := "{\"kty\":\"RSA\",\"alg\":\"RS256\",\"ext\":false,\"n\":\"mEqGJwp0GL1oVwjRikkNfzd-Rkpb7vIbGodwQkTDsZT4_UE02WDaRa-PjxzL4lPZ4rUpV5SqVxM25aEIeGkEOR_8Xoqx7lpNKNOQs3E_o8hGBzQKpGcA7de678LeAUZdJZcnnQxXYjNf8St3aOIay7QrPoK8wQHEvv8Jqg7O1-pKEKCIwSKikCFHTxLhDDRo31KFG4XLWtLllCfEO6vmQTseT-_8OZPBSHOxR9VhIbY7VBhPq-PeAWURn3G52tQX-802waGmKBZ4B87YtEEPxCNbyyvlk8jRKP1KIrI49bgJhAe5Mow3yycQEnGuPDwLzmJ1lU6I4zgkyL1jI3Ghsw\",\"e\":\"AQAB\"}" // this is really from a db, not hardcoded 
// load json from public key, extract modulus and public exponent 
obj := strings.Replace(publicKey, "\\", "", -1) // remove escapes 
var k struct { 
    N string `json:"n"` 
    E string `json:"e"` 
} 
if err = json.Unmarshal([]byte(obj), &k); err != nil { 
    return err 
} 
n, err := webauthnSigninConversion(k.N) 
if err != nil { 
    return err 
} 
e, err := webauthnSigninConversion(k.E) 
if err != nil { 
    return err 
} 
pk := &rsa.PublicKey{ 
    N: new(big.Int).SetBytes(n), // modulus 
    E: int(new(big.Int).SetBytes(e).Uint64()), // public exponent 
} 
  
hash := sha256.Sum256(clientData) 

// Create data buffer to verify signature over 
b := append(authenticatorData, hash[:]...) 
  
if err = rsa.VerifyPKCS1v15(pk, crypto.SHA256, b, signature); err != nil { 
    return err 
} 

// if no error, signature matches 

Bu kod crypto/rsa: input must be hashed message başarısız olur. rsa.VerifyPKCS1v15'da b yerine hash[:]'u değiştirirseniz, crypto/rsa: verification error ile başarısız olur. authenticatorData ve hash'u birleştirmem gerektiğine inandığım nedeni, C# ve PHP örnek kodlarında ne olduğudır (cf, https://github.com/adrianba/fido-snippets/blob/master/csharp/app.cs, https://github.com/adrianba/fido-snippets/blob/master/php/fido-authenticator.php).

Git belki farklı bir yol mu?

JS ve Go bayt dizileri basılmış ve clientData, signatureData, authenticatorData ve hash (ve son iki kombine dizi) aynı değerlere sahip olduğunu doğrulamıştır. Genel anahtar oluşturulduktan sonra n ve e alanlarını JS'den çıkaramadım, bu nedenle ortak anahtarı nasıl oluşturduğum konusunda bir sorun olabilir.

+0

Eğer 1500+ rep olan birisi "webauthn" adında bir etiket ekleyebilirse, bu harika olurdu. (Bu standardın adıdır, bkz. Https://www.w3.org/Webauthn/) – yngling

cevap

2

Ben bir kripto uzmanı değilim, ancak PHP ile imzalanmış imzaları doğrulama da dahil olmak üzere bazı deneyimlerim var. Dolayısıyla, karşılaştırılan bayt değerlerinin aynı olduğu varsayılırsa, probleminizin muhtemelen genel anahtar oluşturma olduğunu söyleyebilirim. Benim genel anahtarı ve Yours (Playground) karşılaştırıldı

func CreatePublicKey(nStr, eStr string)(pubKey *rsa.PublicKey, err error){ 

    decN, err := base64.StdEncoding.DecodeString(nStr) 
    n := big.NewInt(0) 
    n.SetBytes(decN) 

    decE, err := base64.StdEncoding.DecodeString(eStr) 
    if err != nil { 
     fmt.Println(err) 
     return nil, err 
    } 
    var eBytes []byte 
    if len(decE) < 8 { 
     eBytes = make([]byte, 8-len(decE), 8) 
     eBytes = append(eBytes, decE...) 
    } else { 
     eBytes = decE 
    } 
    eReader := bytes.NewReader(eBytes) 
    var e uint64 
    err = binary.Read(eReader, binary.BigEndian, &e) 
    if err != nil { 
     fmt.Println(err) 
     return nil, err 
    } 
    pKey := rsa.PublicKey{N: n, E: int(e)} 
    return &pKey, nil 
} 

ve farklı değerlere sahip: Bu işlevi ile modülü ve üsten genel anahtarları yaratma benim çözüm denemek öneririm. Çalışıyorsa kodunuzla önerdiğim çözüm hakkında bana geri bildirim verebilir misiniz?

Düzenleme 1: URLencoding örnek Playground 2

Düzenleme 2: Düzenleme 2 snippet'te 'veri' değişken aynı veridir Yani

hasher := sha256.New() 
hasher.Write([]byte(data)) 
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hasher.Sum(nil), signature) 

: Bu benim imzayı doğrulamak nasıl PHP tarafında imzalamak için kullanılan (mesaj).

+0

Ayrıca, aşağıdaki gibi StdEncoding yerine URLEncoding kullanarak modülü deşifre etmeye çalışmalısınız: base64.URLEncoding.DecodeString (nStr) ile benim örneğim – kingSlayer

+0

İlginiz için çok teşekkür ederim! Yukarıdaki (ve URLEncoding) denedim, bu soruda bahsettiğim aynı hataları verir ("kripto/rsa: giriş hashed iletisi olmalıdır" ve "kripto/rsa: doğrulama hatası"). Kodunuzda, rsa.VerifyPKCS1v15'i nasıl ararsınız? AuthenticatorData ve hash birleştirir misiniz? – yngling

+0

PHP örnek kodunda, doğrulama "$ rsa-> onayla ($ a.") Yapılır.$ h, $ s); ", ancak aynı şeyi Go’da yaparsam hatayı alıyorum" crypto/rsa: input hashed iletisi olmalıdır. " – yngling