1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| type APIRequest struct { Host string `json:"host"` Username string `json:"username"` Command string `json:"command"` }
type SignResult struct { XTimestamp string XPublicKey string XSignature string SignedBody []byte }
func MakeEd25519Headers(privBase64 string, body APIRequest) (*SignResult, error) { const ( privDERLen = 48 seedStartIdx = 16 pubPrefix = "MCowBQYDK2VwAyEA" timestampFmt = "20060102150405" ) der, err := base64.StdEncoding.DecodeString(privBase64) if err != nil || len(der) != privDERLen { return nil, fmt.Errorf("decode priv base64: %w", err) } seed := der[seedStartIdx:] privKey := ed25519.NewKeyFromSeed(seed) pubKey := privKey.Public().(ed25519.PublicKey)
bodyBytes, _ := json.Marshal(body) ts := time.Now().UTC().Format(timestampFmt) msg := append([]byte(ts), bodyBytes...)
sig := ed25519.Sign(privKey, msg) pubDER, _ := base64.StdEncoding.DecodeString(pubPrefix) pubDER = append(pubDER, pubKey...)
return &SignResult{ XTimestamp: ts, XPublicKey: base64.StdEncoding.EncodeToString(pubDER), XSignature: base64.StdEncoding.EncodeToString(sig), SignedBody: bodyBytes, }, nil }
func SendSignedRequest(url string, body APIRequest, sig *SignResult) (string, string, error) { j, _ := json.Marshal(body) req, _ := http.NewRequest("POST", url, bytes.NewReader(j)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Timestamp", sig.XTimestamp) req.Header.Set("X-Public-Key", sig.XPublicKey) req.Header.Set("X-Signature", sig.XSignature)
cli := &http.Client{Timeout: 8 * time.Second} resp, err := cli.Do(req) if err != nil { return "", "", err } defer resp.Body.Close() b, _ := io.ReadAll(resp.Body) return resp.Status, string(b), nil }
|