2017-12-13 125 views
5

Tôi đang cố gắng triển khai HOTP (rfc-4226) ở Golang và tôi đang cố gắng tạo một HOTP hợp lệ. Tôi có thể tạo ra nó trong java nhưng vì một lý do nào đó việc triển khai của tôi trong Golang là khác nhau. Dưới đây là các mẫu:Java so với Golang cho HOTP (rfc-4226)

public static String constructOTP(final Long counter, final String key) 
     throws NoSuchAlgorithmException, DecoderException, InvalidKeyException { 
    final Mac mac = Mac.getInstance("HmacSHA512"); 

    final byte[] binaryKey = Hex.decodeHex(key.toCharArray()); 

    mac.init(new SecretKeySpec(binaryKey, "HmacSHA512")); 
    final byte[] b = ByteBuffer.allocate(8).putLong(counter).array(); 
    byte[] computedOtp = mac.doFinal(b); 

    return new String(Hex.encodeHex(computedOtp)); 
} 

và Go:

func getOTP(counter uint64, key string) string { 
    str, err := hex.DecodeString(key) 
    if err != nil { 
     panic(err) 
    } 
    h := hmac.New(sha512.New, str) 
    bs := make([]byte, 8) 
    binary.BigEndian.PutUint64(bs, counter) 
    h.Write(bs) 
    return base64.StdEncoding.EncodeToString(h.Sum(nil)) 
} 

Tôi tin rằng vấn đề là dòng Java: ByteBuffer.allocate(8).putLong(counter).array(); tạo ra một mảng byte khác so với dòng Go: binary.BigEndian.PutUint64(bs, counter).

Trong Java, mảng byte sau được tạo: 83 -116 -9 -98 115 -126 -3 -48 và trong Go: 83 140 247 158 115 130 253 207.

Có ai biết sự khác biệt trong hai dòng và cách tôi có thể chuyển dòng java để đi không?

Trả lời

10

Loại byte trong Java được ký kết, nó có một loạt các -128..127, trong khi tại Gò byte là một bí danh của uint8 và có một loạt các 0..255. Vì vậy, nếu bạn muốn so sánh kết quả, bạn phải thay đổi giá trị Java âm bằng 256 (thêm 256).

Mẹo: Để hiển thị một giá trị Java byte theo kiểu unsigned, sử dụng: byteValue & 0xff đó chuyển đổi nó để int sử dụng 8 bit của byte là 8 bit thấp nhất trong int. Hoặc tốt hơn: hiển thị cả hai kết quả dưới dạng hex để bạn không phải quan tâm đến việc đăng nhập ...

Thêm 256 vào giá trị byte Java âm, đầu ra là gần như giống hệt với Go: byte được tắt theo 1:

javabytes := []int{83, -116, -9, -98, 115, -126, -3, -48} 
for i, b := range javabytes { 
    if b < 0 { 
     javabytes[i] += 256 
    } 
} 
fmt.Println(javabytes) 

Output là:

[83 140 247 158 115 130 253 208] 

Vì vậy, các byte cuối cùng của mảng Java của bạn là 208 còn Go là 207. Tôi đoán số counter của bạn được tăng lên một lần ở một nơi khác trong mã mà bạn chưa đăng.

Điều khác biệt là trong Java bạn trả về kết quả được mã hóa hex trong khi bạn trả về kết quả được mã hóa Base64 (chúng là 2 mã hóa khác nhau cho kết quả hoàn toàn khác nhau). Như bạn đã xác nhận, trong Go, hãy quay lại hex.EncodeToString(h.Sum(nil)) kết quả phù hợp.

Mẹo # 2: Để hiển thị byte Go theo kiểu ký, bạn chỉ cần chuyển đổi chúng sang int8 (được ký kết) như thế này:

gobytes := []byte{83, 140, 247, 158, 115, 130, 253, 207} 
for _, b := range gobytes { 
    fmt.Print(int8(b), " ") 
} 

đầu ra này:

83 -116 -9 -98 115 -126 -3 -49 
+0

trên thực tế, 'uint8 (-48)' là 208, không phải 207 (https://play.golang.org/p/marMBd3rwg) – JimB

+0

@JimB Đúng vậy, byte cuối cùng không khớp. – icza

+1

wow - TIL! hoạt động nghịch đảo sẽ là gì? (lấy phiên bản Go và tạo phiên bản java). Cảm ơn rất nhiều! –