Backend

Java

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.util.Base64;
 
public class App {
 
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
 
    public static void main(String[] args) throws Exception {
        byte[] payloadBytes = response.toByteArray();
        String payload = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadBytes);
        PrivateKey key = getPrivateKeyFromPEMFile("<path to your private key>");
        byte[] sign = signPayload(("." + payload).getBytes(), key);
        String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(sign);
    }
 
    public static byte[] signPayload(byte[] payloadBytes, PrivateKey privateKey) throws Exception {
        Signature signer = Signature.getInstance("SHA256withPlain-ECDSA", "BC");
        signer.initSign(privateKey);
        signer.update(payloadBytes);
        return signer.sign();
    }
}

Python

import base64
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from google.protobuf import message
import sell_pb2  # Import the generated protobuf classes
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
 
 
def base64_url_encode(data):
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')
 
 
def sign_payload(payload, private_key):
    # Sign the payload using ECDSA with SHA-256
    der_signature = private_key.sign(
        payload,
        ec.ECDSA(hashes.SHA256())
    )
    # Decode the DER-encoded signature to get r and s
    r, s = decode_dss_signature(der_signature)
    # Convert r and s to bytes
    r_bytes = r.to_bytes(32, byteorder='big')
    s_bytes = s.to_bytes(32, byteorder='big')
    return r_bytes + s_bytes
 
 
if __name__ == "__main__":
    payload = response.SerializeToString()
    encoded_payload = base64_url_encode(payload)
    # Sign the payload
    signature = sign_payload(("." + encoded_payload).encode('utf-8'), private_key)
    # Base64 URL encode the signature
    encoded_signature = base64_url_encode(signature)
    provider_sig = {
        "payload": encoded_payload,
        "signature": encoded_signature
    }

typescript

const base64EncodedPayload = base64url.encode(
   Buffer.from(uInt8ArrayEncodedPayload), // this is encoded payload using protobuf
);
 
function signProviderSignaturePayload(base64EncodedPayload: string): string {
    const ec = new EC('secp256k1');
    const keyPair = ec.keyFromPrivate(
      <PRIVATE_KEY_GOES_HERE>,
      'hex',
    );
    const hashedMessage = crypto
      .createHash('sha256')
      .update(`.${base64EncodedPayload}`)
      .digest();
    const signature = keyPair.sign(hashedMessage);
 
    const r = signature.r.toString('hex').padStart(64, '0');
    const s = signature.s.toString('hex').padStart(64, '0');
 
    const signatureBuffer = Buffer.from(r + s, 'hex');
    const signatureBase64 = signatureBuffer.toString('base64url');
 
    return signatureBase64;
}
Ledger
Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Nano S, Ledger Vault, Ledger OS are registered trademarks of Ledger SAS