2013-08-02 46 views
8

Tôi đang cố gắng triển khai Chef API client trong Go, nhưng đã cố gắng tạo chữ ký RSA tiêu đề yêu cầu chính xác. Theo số documentation:Mã hóa tin nhắn bằng khóa riêng RSA (như trong RSA_private_encrypt của OpenSSL)

Tiêu đề chuẩn được ký bằng khóa riêng được máy khách sử dụng từ đó yêu cầu được gửi và cũng được mã hóa bằng Base64.

Cuộc gọi ruby ​​sau để OpenSSL::PKey::RSA.private_encrypt() có thể được tìm thấy trong mixlib-authenticationgem code, nó sử dụng OpenSSL bindings, private_encrypt() phương pháp gọi RSA_private_encryptopenssl function.

Thật không may, tôi không thể tìm thấy hàm phù hợp trong thư viện chuẩn của Go; crypto/rsa trông rất gần, nhưng nó chỉ thực hiện các phương pháp mã hóa thông thường: mã hóa bằng khóa công khai, ký băm với khóa riêng. OpenSSL của RSA_private_encrypt ngược lại: nó mã hóa (nhỏ) tin nhắn với khóa riêng (giống như tạo chữ ký từ băm thông báo).

này "ký" cũng có thể đạt được bằng lệnh này:

openssl rsautl -sign -inkey path/to/private/key.pem \ 
    -in file/to/encrypt -out encrypted/output 

Có bất kỳ thư viện Go mẹ đẻ để đạt được kết quả tương tự như OpenSSL của RSA_private_encrypt, hoặc là cách duy nhất là sử dụng CGO để gọi chức năng này từ Thư viện OpenSSL? Có lẽ tôi đang thiếu một cái gì đó. Ý tưởng của tôi đã được thực hiện các khách hàng mà không có bất kỳ phụ thuộc không đi.

Tôi là người mới tham gia Go, vì vậy tôi không chắc mình có thể đi sâu vào các nguồn mô-đun crypto/rsa hay không.


Tìm thấy các similar question, nhưng the answer sử dụng SignPKCS1v15 rõ ràng là sai (function encrypts message's hash, not the message itself này).

+0

Đó Chef tài liệu API là thê thảm không rõ ràng, nhưng tôi nghĩ rằng bạn có nghĩa vụ phải đăng ký các tiêu đề, có nghĩa SignPKCS1v15 có lẽ là những gì bạn muốn. –

+0

@GregS, thật đáng tiếc, đây không phải là trường hợp, tôi đã kiểm tra lại với [source mixlib-authentication] (https://github.com/opscode/mixlib-authentication/blob/master/lib/mixlib/authentication/signedheaderauth. rb # L94). – artyom

+0

Tôi nghĩ bạn có thể đúng. Tôi nghi ngờ nó đang sử dụng PKCS1 v15 * block loại 1 * padding, nhưng điều đó thực sự không giúp bạn. Lấy làm tiếc. –

Trả lời

5

Với great help of the golang community, giải pháp đã được tìm thấy:

đang gốc niêm yết tại http://play.golang.org/p/jrqN2KnUEM bởi Alex (xem mailing list).

Tôi đã thêm đầu vào kiểm tra kích thước khối theo quy định tại mục 8 của rfc2313: http://play.golang.org/p/dGTl9siO8E

Dưới đây là các mã:

package main 

import (
    "crypto/rsa" 
    "crypto/x509" 
    "encoding/pem" 
    "errors" 
    "fmt" 
    "io/ioutil" 
    "math/big" 
    "os/exec" 
) 

var (
    ErrInputSize = errors.New("input size too large") 
    ErrEncryption = errors.New("encryption error") 
) 

func PrivateEncrypt(priv *rsa.PrivateKey, data []byte) (enc []byte, err error) { 

    k := (priv.N.BitLen() + 7)/8 
    tLen := len(data) 
    // rfc2313, section 8: 
    // The length of the data D shall not be more than k-11 octets 
    if tLen > k-11 { 
     err = ErrInputSize 
     return 
    } 
    em := make([]byte, k) 
    em[1] = 1 
    for i := 2; i < k-tLen-1; i++ { 
     em[i] = 0xff 
    } 
    copy(em[k-tLen:k], data) 
    c := new(big.Int).SetBytes(em) 
    if c.Cmp(priv.N) > 0 { 
     err = ErrEncryption 
     return 
    } 
    var m *big.Int 
    var ir *big.Int 
    if priv.Precomputed.Dp == nil { 
     m = new(big.Int).Exp(c, priv.D, priv.N) 
    } else { 
     // We have the precalculated values needed for the CRT. 
     m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0]) 
     m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1]) 
     m.Sub(m, m2) 
     if m.Sign() < 0 { 
      m.Add(m, priv.Primes[0]) 
     } 
     m.Mul(m, priv.Precomputed.Qinv) 
     m.Mod(m, priv.Primes[0]) 
     m.Mul(m, priv.Primes[1]) 
     m.Add(m, m2) 

     for i, values := range priv.Precomputed.CRTValues { 
      prime := priv.Primes[2+i] 
      m2.Exp(c, values.Exp, prime) 
      m2.Sub(m2, m) 
      m2.Mul(m2, values.Coeff) 
      m2.Mod(m2, prime) 
      if m2.Sign() < 0 { 
       m2.Add(m2, prime) 
      } 
      m2.Mul(m2, values.R) 
      m.Add(m, m2) 
     } 
    } 

    if ir != nil { 
     // Unblind. 
     m.Mul(m, ir) 
     m.Mod(m, priv.N) 
    } 
    enc = m.Bytes() 
    return 
} 

func main() { 
    // o is output from openssl 
    o, _ := exec.Command("openssl", "rsautl", "-sign", "-inkey", "t.key", "-in", "in.txt").Output() 

    // t.key is private keyfile 
    // in.txt is what to encode 
    kt, _ := ioutil.ReadFile("t.key") 
    e, _ := ioutil.ReadFile("in.txt") 
    block, _ := pem.Decode(kt) 
    privkey, _ := x509.ParsePKCS1PrivateKey(block.Bytes) 
    encData, _ := PrivateEncrypt(privkey, e) 
    fmt.Println(encData) 
    fmt.Println(o) 
    fmt.Println(string(o) == string(encData)) 
} 

Cập nhật: chúng ta có thể mong đợi để có một nguồn gốc hỗ trợ cho loại đăng nhập Go 1.3, xem the appropriate commit.

+0

cảm ơn rất nhiều vì giải pháp của bạn. Nó đã cứu ngày của tôi! – holys

+0

Liên kết đến các cam kết thực sự dường như bị hỏng, tôi cần loại mã hóa này cũng để ký một yêu cầu HTTP. Tên chính xác của mã hóa này là gì vì bạn có tên PKCS ...? –

+0

Jerry, tôi đã cập nhật liên kết để cam kết với github, hàm bạn cần là 'rsa.SignPKCS1v15' – artyom

0

Chào mừng bạn đến với niềm vui của openssl ... Đó là một chức năng cực kỳ kém tên. Nếu bạn poke quanh trong mã ruby ​​nó gọi hàm openssl này

http://www.openssl.org/docs/crypto/RSA_private_encrypt.html

Đọc tài liệu hướng dẫn, điều này thực sự là ký đệm với tin chủ chốt và không mã hóa nó.

DESCRIPTION

Các chức năng này xử lý chữ ký RSA ở mức thấp.

RSA_private_encrypt() ký các byte flen tại từ (thường là thông báo thông báo bằng mã định danh thuật toán) bằng cách sử dụng khóa riêng tư và lưu chữ ký vào. phải trỏ đến RSA_size (rsa) byte của bộ nhớ.

+0

Mặc dù tài liệu, đây thực sự là thao tác ký: bạn có thể thực hiện bước giải mã trên kết quả và lấy lại thư gốc. Tôi cho rằng nó được thiết kế để mã hóa chữ ký, kết luận này được hỗ trợ bởi chú thích trong dấu ngoặc đơn (xem mô tả bạn trích dẫn): * thường là thông báo nhận dạng bằng thuật toán nhận dạng *. – artyom

+0

Trong thông điệp trước, tôi thực sự có nghĩa là «RSA_private_encrypt' là một hoạt động * mã hóa *, chứ không phải ký tên. – artyom

+0

@artyom Vấn đề là nếu bạn đang "mã hóa" trong một cái gì đó mà bất kỳ ai có khóa ** công ** có thể giải mã, bạn không thực sự ** mã hóa ** bất cứ điều gì, theo nghĩa là bạn đang ẩn nó . Có rất ít lý do chính đáng để sử dụng cuộc gọi này để thực hiện bất kỳ điều gì an toàn. Ở mức độ đó, "mã hóa riêng" thực sự gần hơn với việc ký kết (nơi bạn "mã hóa" một thông báo). – Bruno

Các vấn đề liên quan