I'm trying to implement what's discussed in this topic: How to verify data signed with nami wallet in a node backend? but in Java.

Obviously I don't have the js/rust lib I can play with, so I have to implement/integrate all the bits myself.

While I've successfully implemented the code to use the Ed25519 standard, and in this repo there is the code and a few examples github I don't seem to be able to verify the data returned by the signData method of the wallet (that I assume uses the same standard).

Now it's my understading that the COSE object contains all that I need to at least verify the payload has been signed with the public key returned with COSE object itself. Looking at the sample in the post above, and using the https://cbor.me/ website, I guess that:

  1. 674D11E432450118D70EA78673D5E31D5CC1AEC63DE0FF6284784876544BE340 is the HEX of the signed message
  2. 68656C6C6F is the HEX of the message to sign
  3. 01D2EB831C6CAD4ABA700EB35F86966FBEFF19D077954430E32CE65E8DA79A3ABE84F4CE817FAD066ACC1435BE2FFC6BD7DCE2EC1CC6CCA6CB is the HEX of the Base Address of the wallet
  4. A3B5ACD99DF5F3B5E4449C5A116078E9C0FCFC126A4D4E2F6A9565F40B0C77474CAFD89845E768FAE3F6EEC0DF4575FCFE7094672C8C02169D744B415C617609 is the HEX of the Public Key of the wallet.

Now, if I used the public key at (4) along with signed (1) and unsigned (2), and I test with the code (taht I know that works!) I wrong for the verification, it doesn't pass. So I guess one of the assumption i wrong.

I'm sure that (2) is the hex of the message to sign, so now either (1) is not the signed message or (4) is not the public key. Or they are in a different format

In java the code I'm running looks like the following

var msg = "hello".getBytes(StandardCharsets.UTF_8);
var publicKey = new Ed25519PublicKeyParameters(Hex.decode("A3B5ACD99DF5F3B5E4449C5A116078E9C0FCFC126A4D4E2F6A9565F40B0C77474CAFD89845E768FAE3F6EEC0DF4575FCFE7094672C8C02169D744B415C617609"), 0);
var verifier = new Ed25519Signer();
verifier.init(false, publicKey);
verifier.update(msg, 0, msg.length);
var outcome = verifier.verifySignature(Hex.decode("674D11E432450118D70EA78673D5E31D5CC1AEC63DE0FF6284784876544BE340"));
System.out.println("Outcome: " + outcome);

Any help would be greatly appreciated

2 Answers 2


After the CIP-0030 update you won't get the publicKey in the protected headers, but rather in the response object from signData directly, so the above answer won't work anymore.

Here is the example of the verification after the update: https://github.com/MartinOndejka/cip-0008-verification


First part of the solution can be found here:


Code pasted here to keep track

package com.bloxbean.cardano.client.examples.cip8;

import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.model.*;
import com.bloxbean.cardano.client.crypto.CryptoException;
import com.bloxbean.cardano.client.transaction.util.CborSerializationUtil;
import com.bloxbean.cardano.client.util.HexUtil;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;

import java.security.MessageDigest;
import java.security.Signature;
import java.util.List;

public class CIP8CBORParser {
    private static final EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);

    public void parse() throws Exception {
        //Payload = "hello"
        String coseSignMsgInHex = "845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609";

        //CBOR parsing starts here ###################
        List<DataItem> dataItemList  = CborDecoder.decode(HexUtil.decodeHexString(coseSignMsgInHex));

        List<DataItem> topArray = ((Array) dataItemList.get(0)).getDataItems();
        ByteString protectedHeaderBs = (ByteString) topArray.get(0);
        Map unprotectedMap = (Map) topArray.get(1);
        ByteString messageToSignBS = (ByteString) topArray.get(2);
        ByteString signatureArrayBS = (ByteString) topArray.get(3);

        byte[] message = messageToSignBS.getBytes();
        byte[] signature = signatureArrayBS.getBytes();

        System.out.println(new String(message));

        //parse protected header map bytestring
        List<DataItem> protectedHeaderMapDIList = CborDecoder.decode(protectedHeaderBs.getBytes());
        Map protectedHeaderMap = (Map) protectedHeaderMapDIList.get(0);

        ByteString publicKeyBS = (ByteString) protectedHeaderMap.get(new UnsignedInteger(4));
        byte[] publicKeyBytes = publicKeyBS.getBytes();

        ByteString addressBS = (ByteString)protectedHeaderMap.get(new UnicodeString("address"));
        System.out.println("Address >> " + HexUtil.encodeHexString(addressBS.getBytes()));
        //CBOR parsing ends her ###################

        //Create SignatureStructure. This is passed as message during signature verification
        Array sigStructArray = new Array();
        sigStructArray.add(new UnicodeString("Signature1"));
        sigStructArray.add(new ByteString(new byte[0]));

        byte[] messageToVerify = CborSerializationUtil.serialize(sigStructArray); //serialize
        //End SignatureStructure

        boolean verified = verify(messageToVerify, signature, publicKeyBytes);
        System.out.println("Verification status >> " + verified);

    public boolean verify(byte[] message, byte[] signatureBytes, byte[] publicKeyBytes) {

            net.i2p.crypto.eddsa.EdDSAPublicKey publicKey = new net.i2p.crypto.eddsa.EdDSAPublicKey(new EdDSAPublicKeySpec(publicKeyBytes, spec));
            Signature signature = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
            return signature.verify(signatureBytes);
        }catch(Exception e){
            throw new CryptoException("Extended signing error", e);

    public static void main(String[] args) throws Exception {
        new CIP8CBORParser().parse();

Kudos to @Satya

Second part missing is the adderss verificaiton/validation.

Will add as soon as it's done.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.