Consider this private and public key pair:
privateKeyStr := "55331231652752707450534414287730188629145955228891758798201450763340552825166"
publicKeyStr := "11798277315441798127238305597212517654332197982978466864717364245859439644690363141941628373531488122609933560694747426926865495896421628851963848417303440"
Go Implementation for Signing and Verification
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)
func ExecuteSecp256k1SignatureTest() {
privateDecimal := new(big.Int)
privateDecimal.SetString(privateKeyStr, 10)
fmt.Printf("Private key bytes: %v\n", privateDecimal.Bytes())
fmt.Printf("Private key hex: %x\n", privateDecimal.Bytes())
hashDigest := sha256.Sum256([]byte("Abby is beautiful and smart!"))
signatureBytes, _ := secp256k1.Sign(hashDigest[:], privateDecimal.Bytes())
fmt.Printf("Signature hex: %x\n", signatureBytes)
fmt.Printf("Signature bytes: %v\n", signatureBytes)
publicDecimal := new(big.Int)
publicDecimal.SetString(publicKeyStr, 10)
publicHex := hex.EncodeToString(publicDecimal.Bytes())
fmt.Printf("Public key hex: %v\n", publicHex)
uncompressedPublic := "04" + publicHex
publicDecimal.SetString(uncompressedPublic, 16)
fmt.Printf("Public key bytes: %v\n", publicDecimal.Bytes())
fmt.Printf("Public key hex: %x\n", publicDecimal.Bytes())
if secp256k1.VerifySignature(publicDecimal.Bytes(), hashDigest[:], signatureBytes[:64]) {
fmt.Println("Verification successful")
} else {
fmt.Println("Verification failed")
}
}
Modular Signing Function
func GenerateSecp256k1Signature(message []byte, privateKeyDecimal string) (string, error) {
keyValue := new(big.Int)
keyValue.SetString(privateKeyDecimal, 10)
fmt.Printf("Private key bytes: %v\n", keyValue.Bytes())
fmt.Printf("Private key hex: %x\n", keyValue.Bytes())
hashValue := sha256.Sum256(message)
fmt.Printf("Message hash: %x\n", hashValue)
signature, err := secp256k1.Sign(hashValue[:], keyValue.Bytes())
fmt.Printf("Signature R component: %v\n", signature[0:32])
fmt.Printf("Signature S component: %v\n", signature[32:64])
if err != nil {
return "", err
}
signatureHex := hex.EncodeToString(signature)
return signatureHex, nil
}
HTTP Request with Signature Header
func ExecuteSignedRequest(requestData Request, privateKey string) (string, error) {
serviceURL := fmt.Sprintf("http://%s/weid/api/invoke", config.GlobalConfig.WeIDServiceAddress)
jsonData, err := json.Marshal(requestData)
if err != nil {
return "", err
}
httpClient := &http.Client{}
httpRequest, err := http.NewRequest("POST", serviceURL, bytes.NewBuffer(jsonData))
if err != nil {
return "", err
}
signatureValue, signErr := GenerateSecp256k1Signature(jsonData, privateKey)
if signatureValue == "" {
return "", signErr
}
httpRequest.Header.Add("signature", signatureValue)
httpRequest.Header.Add("Content-Type", "application/json")
response, err := httpClient.Do(httpRequest)
if err != nil {
return "", err
}
defer response.Body.Close()
responseBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return "", err
}
return string(responseBody), nil
}
Java Verification Implementation
public static boolean validateSignature(String publicKeyDecimal, String rawData) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String signatureHex = request.getHeader("signature");
try {
BigInteger publicBigInt = new BigInteger(publicKeyDecimal, 10);
String publicKeyHex = publicBigInt.toString(16);
String dataHash = computeHash(rawData);
byte[] signatureBytes = Hex.decodeHex(signatureHex);
RsvSignature signature = new RsvSignature();
byte[] rComponent = new byte[32];
byte[] sComponent = new byte[32];
System.arraycopy(signatureBytes, 0, rComponent, 0, 32);
System.arraycopy(signatureBytes, 32, sComponent, 0, 32);
signature.setR(new Bytes32(rComponent));
signature.setS(new Bytes32(sComponent));
signature.setV(new Uint8(signatureBytes[64]));
return DataToolUtils.verifySignature(publicKeyHex, dataHash, signature);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return false;
}
Note the byte representation differences between lnaguages:
- Java uses signed bytes (range -128 to 127)
- Go uses unsigned bytes (range 0 to 255)
- Different processro architectures may use different endianness
- These differences must be handled during cross-language signature verification