2013-08-02 8 views
8

Ich versuche Chef API client in Go zu implementieren, aber stecken, versuchen, korrekte Anfrage Header RSA-Signatur zu erstellen. Nach documentation:Verschlüsseln Nachricht mit RSA privaten Schlüssel (wie in OpenSSL RSA_private_encrypt)

ist ein kanonischer Header mit dem privaten Schlüssel signiert von der Client-Maschine verwendet, von dem die Anfrage gesendet wird, und ist auch mit Base64 codiert.

Der folgende Rubin Aufruf OpenSSL::PKey::RSA.private_encrypt() in mixlib-authenticationgem code gefunden werden, verwendet es OpenSSL bindings, private_encrypt() Methode RSA_private_encryptopenssl function nennt.

Leider kann ich keine passende Funktion in Gos Standardbibliothek finden; crypto/rsa sieht in der Nähe, aber es implementiert nur konventionelle Verschlüsselungsmethoden: Verschlüsselung mit öffentlichen Schlüssel, Hash-Signatur mit private Schlüssel. OpenSSL RSA_private_encrypt tut das Gegenteil: Es verschlüsselt (kleine) Nachricht mit privaten Schlüssel (ähnlich wie eine Signatur aus Nachrichtenhash erstellen).

Diese „Signieren“ kann auch mit diesem Befehl erreicht werden:

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

Gibt es native Bibliotheken Go das gleiche Ergebnis wie OpenSSL RSA_private_encrypt oder der einzige Weg ist, mit CGO zu erreichen, diese Funktion aufzurufen aus OpenSSL-Bibliothek? Vielleicht vermisse ich etwas. Meine Idee war, den Client ohne irgendwelche Abhängigkeiten zu implementieren.

Ich bin ein Go-Neuling, also bin ich mir nicht sicher, ob ich in crypto/rsa Modulquellen tauchen kann.


die similar question gefunden, aber the answer verwenden SignPKCS1v15 offensichtlich falsch ist (dies function encrypts message's hash, not the message itself).

+0

Der Chef-API-Dokumentation auf tragische Weise unklar ist, aber ich denke, Sie sollen die Header zu unterschreiben, was bedeutet, SignPKCS1v15 ist wahrscheinlich was du willst. –

+0

@GregS, das ist leider nicht der Fall, ich habe dies mit [mixlib-authentication sources] überprüft (https://github.com/opscode/mixlib-authentication/blob/master/lib/mixlib/authentication/signedheaderauth. rb # L94). – artyom

+0

Ich denke, Sie könnten Recht haben. Ich vermute, dass PKCS1 v15 * block type 1 * padding verwendet wird, aber das hilft dir wirklich nicht. Es tut uns leid. –

Antwort

5

Mit great help of the golang community wurde die Lösung gefunden:

Originalcode bei http://play.golang.org/p/jrqN2KnUEM von Alex geschrieben (siehe mailing list).

Ich habe Eingangsblockgröße Prüfung hinzugefügt, wie in Abschnitt 8 von rfc2313 angegeben: http://play.golang.org/p/dGTl9siO8E

Hier ist der Code:

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)) 
} 

Update: wir eine native haben erwarten können Unterstützung für diese Art der Anmeldung in Go 1.3, siehe the appropriate commit.

+0

Vielen Dank für Ihre Lösung. Es hat meinen Tag gerettet! – holys

+0

Der Link zu den eigentlichen Commits scheint kaputt zu sein, ich brauchte diese Art der Verschlüsselung auch um eine HTTP-Anfrage zu signieren. Wie lautet der genaue Name dieser Verschlüsselung, weil Sie die PKCS-Namensgebung usw. haben? –

+0

Jerry, Ich habe den Link zu commit to github aktualisiert, die Funktion, die Sie brauchen, ist 'rsa.SignPKCS1v15' – artyom

0

Willkommen zu den Freuden von openssl ... Das ist eine unglaublich schlecht benannte Funktion. Wenn Sie in dem Ruby-Code herumzustochern es ruft diese openssl Funktion

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

die Dokumentation Lesen, dies ist die Unterzeichnung tatsächlich den Puffers mit dem privaten Schlüssel und Verschlüsselung nicht.

BESCHREIBUNG

Diese Funktionen hand RSA-Signaturen auf einem niedrigen Niveau.

RSA_private_encrypt() signiert die flen-Bytes von (normalerweise ein Nachrichtenauszug mit einem Algorithmusbezeichner) unter Verwendung des privaten Schlüssels rsa und speichert die Signatur in in. muss auf RSA_size (RSA) Bytes des Speichers zeigen.

+0

Trotz der Dokumentation handelt es sich tatsächlich um eine Signieroperation: Sie können den Entschlüsselungsschritt für das Ergebnis ausführen und die ursprüngliche Nachricht abrufen. Ich nehme an, es war beabsichtigt, Signaturen zu verschlüsseln, diese Schlussfolgerung wird durch einen Kommentar in Klammern unterstützt (siehe die von Ihnen zitierte Beschreibung): * normalerweise ein Nachrichtenauszug mit einer Algorithmuskennung *. – artyom

+0

In der vorherigen Nachricht meinte ich eigentlich "RSA_private_encrypt" ist eine * Verschlüsselung * Operation, nicht signieren. – artyom

+0

@artyom Das Problem ist, dass wenn Sie "verschlüsseln" innerhalb von etwas, das jeder mit dem ** öffentlichen ** Schlüssel entschlüsseln kann, Sie tatsächlich nichts verschlüsseln, in dem Sinne, dass Sie es verstecken . Es gibt nur wenige gute Gründe, diesen Aufruf zu verwenden, um etwas Sicheres zu implementieren. Insofern ist "private encrypt" tatsächlich näher am Signieren (wo man einen Digest "verschlüsseln" würde). – Bruno

Verwandte Themen