[이동] PBKDF2 구현해보기

26330 단어 go

소개



이번에는 PBKDF2에 대한 기능을 구현해 보겠습니다.
마지막으로 ASP.NET Core Identity를 시도했을 때 PasswordHasher 클래스를 사용했습니다.
그리고 암호를 저장하기 위해 PBKDF2를 사용했습니다.


  • 저번에 작성한 SHA-512함수와 HMAC-SHA512함수를 사용하겠습니다.



  • Go 패키지 사용



    "golang.org/x/crypto/pbkdf2"로 PBKDF2 값을 생성할 수 있습니다.
    소금 값이 필요합니다.

    PasswordHasher 클래스는 "RandomNumberGenerator"를 사용하여 생성합니다.
    지정된 길이의 난수를 생성합니다.

  • RandomNumberGenerator Class(System.Security.Cryptography) - Microsoft Learn

  • package main
    
    import (
        "crypto/rand"
        "crypto/sha512"
        "fmt"
        "log"
        "math"
        "math/big"
    
        "golang.org/x/crypto/pbkdf2"
    )
    
    func main() {
        inputData := []byte("hello")
        salt, err := generateRandomSalt(128 / 8)
        if err != nil {
            log.Panicln(err.Error())
        }
        result = ""
        keyPkg := generatePDKDF2Package(inputData, salt, 100_000, 256/8)
        for _, k := range keyPkg {
            result += fmt.Sprintf("%02X", k)
        }
        log.Println(result)
    ...
    }
    // Generate a salt value
    func generateRandomSalt(length int) ([]byte, error) {
        results := make([]byte, length)
        for i := 0; i < length; i++ {
            salt, err := rand.Int(rand.Reader, big.NewInt(255))
            if err != nil {
                return nil, err
            }
            results[i] = byte(salt.Int64())
        }
        return results, nil
    }
    ...
    func generatePDKDF2Package(password []byte, salt []byte, iterateCount int, keyLength int) []byte {
        return pbkdf2.Key(password, salt, iterateCount, keyLength, sha512.New)
    }
    


    결과




    58A07933EA993981DBF1856FCCB708B522B0B0E6C8F192C3093307337E9CC747
    


    자체 기능 작성




    ...
    func main() {
        inputData := []byte("hello")
        salt, err := generateRandomSalt(128 / 8)
        if err != nil {
            log.Panicln(err.Error())
        }
    ...
        result = ""
        keyPkg := generatePDKDF2Package(inputData, salt, 100_000, 256/8)
        for _, k := range keyPkg {
            result += fmt.Sprintf("%02X", k)
        }
        log.Println(result)
    
    }
    ...
    func generatePDKDF2(password []byte, salt []byte, iterateCount int, keyLength int) []byte {
        hashLength := sha512.Size
        // 1. dkLen > (2^32 - 1)
        if keyLength > ((int(math.Exp2(32)) - 1) * hashLength) {
            log.Println("derived key too long")
            return nil
        }
        // 2. Block size
        // l = CEIL (dkLen / hLen)
        blockCount := int(math.Ceil(float64(keyLength) / float64(hashLength)))
        // r = dkLen - (l - 1) * hLen
        //  r := keyLength - (blockCount-1)*hashLength
    
        dk := make([]byte, hashLength*blockCount)
        s := make([]byte, len(salt)+4)
        copy(s, salt)
    
        // 3. T_1 = F (P, S, c, 1) , 〜 T_l = F (P, S, c, l) ,
        for blockIndex := 1; blockIndex <= blockCount; blockIndex++ {
            // S || INT (i)
            s[len(s)-4] = byte(blockIndex >> 24)
            s[len(s)-3] = byte(blockIndex >> 16)
            s[len(s)-2] = byte(blockIndex >> 8)
            s[len(s)-1] = byte(blockIndex)
            // U_1 = PRF (P, S || INT (i))
            u := HashHMACSHA512(password, s)
            // 4. DK = T_1 || T_2 ||  ...  || T_l<0..r-1>
            if blockIndex > 1 {
                dk = append(dk, u[:]...)
            } else {
                dk = u[:]
            }
            // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
            for ci := 1; ci < iterateCount; ci++ {
                u = HashHMACSHA512(password, u)
                for ui := range u {
                    dk[ui+(hashLength*(blockIndex-1))] ^= u[ui]
                }
            }
        }
        return dk[:keyLength]
    }
    ...
    


    결과




    58A07933EA993981DBF1856FCCB708B522B0B0E6C8F192C3093307337E9CC747
    


    솔트 값과 반복 횟수를 추가하여 해시 값 생성



    PasswordHasher 클래스는 사용자 암호를 저장하기 위해 해시 값을 생성합니다.
    인증을 위해 동일한 암호에서 매번 동일한 해시 값을 생성해야 합니다.

    따라서 해시된 비밀번호에 솔트 값과 반복 횟수를 추가해야 합니다.

    "PasswordHasher.cs"에 따르면 해시된 암호 앞에 추가해야 합니다.
  • PasswordHasher.cs - dotnet / aspnetcore - GitHub

  • main.go




    package main
    
    import (
        "crypto/rand"
        "crypto/sha512"
        "encoding/base64"
        "fmt"
        "log"
        "math"
        "math/big"
    
        "golang.org/x/crypto/pbkdf2"
    )
    func main() {
    ...
        keySelf := generatePDKDF2(inputData, salt, 100_000, 256/8)
    ...
        // Add the salt value and the iteration count
        outputBytes := make([]byte, len(keySelf)+len(salt)+13)
        outputBytes[0] = 0x01
        // In ASP.NET Core Identity, the value of "KeyDerivationPrf.HMACSHA512" is "2"
        writeNetworkByteOrder(outputBytes, 1, 2)
        writeNetworkByteOrder(outputBytes, 5, 100_000)
        writeNetworkByteOrder(outputBytes, 9, uint(len(salt)))
        blockCopy(salt, outputBytes, 13, len(salt))
        blockCopy(keySelf, outputBytes, 13+len(salt), len(keySelf))
    
        // Base64 encoding
        output := base64.StdEncoding.EncodeToString(outputBytes)
        log.Printf("Result : %s", output)
    }
    ...
    func writeNetworkByteOrder(buffer []byte, offset int, value uint) {
        buffer[offset+0] = byte(value >> 24)
        buffer[offset+1] = byte(value >> 16)
        buffer[offset+2] = byte(value >> 8)
        buffer[offset+3] = byte(value >> 0)
    }
    func blockCopy(src []byte, dst []byte, offset int, copyLength int) {
        index := offset
        for i := 0; i < copyLength; i++ {
            dst[index] = src[i]
            index += 1
        }
    }
    


    결과




    AQAAAAIAAYagAAAAEEZHtMLiF5yoYHif+AJgoy5QqO2CTfdOzoF7jkOM+omSShyzooRnwl1soh0xzLpbxg==
    


    자원


  • PKCS #5: Password-Based Cryptography Specification Version 2.1
  • SP 800-132 Recommendation for Password-Based Key Derivation: Part 1: Storage Applications
  • pbkdf2 package - golang.org/x/crypto - Go Packages
  • PasswordHasher.cs - dotnet / aspnetcore - GitHub
  • ManagedPbkdf2Provider.cs - dotnet / aspnetcore - GitHub
  • RandomNumberGenerator Class(System.Security.Cryptography) - Microsoft Learn
  • 좋은 웹페이지 즐겨찾기