Search code examples
gobitcoin

How to decode hex to ASN.1 in golang


I have an ECDSA public key that that is returned to me from an HSM in ASN.1 DER format. I need to create a bitcoin compatible key 33 byte. When I print out key in hex.EncodeToString(pubkey) I get the following output: 3056301006072a8648ce3d020106052b8104000a034200049bb8e80670371f45508b5f8f59946a7c4dea4b3a23a036cf24c1f40993f4a1daad1716de8bd664ecb4596648d722a4685293de208c1d2da9361b9cba74c3d1ec

I use an online decoder here: https://holtstrom.com/michael/tools/asn1decoder.php

And it outputs: 0x049bb8e80670371f45508b5f8f59946a7c4dea4b3a23a036cf24c1f40993f4a1daad1716de8bd664ecb4596648d722a4685293de208c1d2da9361b9cba74c3d1ec

I can then take that and hex.DecodeString(str) which gives me the necessary format to input this into addrPubKey, err := btcutil.NewAddressPubKey(bs, &chaincfg.TestNet3Params).

How do I decode this in golang to get the 0x049... output?

Thanks


Solution

  • The first thing we need is to use the encoding/asn1 package from the standard library. You only have to give go the right struct to decode into. From your link we can see that we have a SEQUENCE that contains another SEQUENCE with two OBJECTIDENTIFIER and a BITSTRING. In go this will be:

    type Ids struct {
        OBi1 asn1.ObjectIdentifier
        OBi2 asn1.ObjectIdentifier
    }
    
    type PubKey struct {
        Id Ids
        Bs asn1.BitString
    }
    

    Now we only have to UnMarshall the data to this structure:

        str := `3056301006072a8648ce3d020106052b8104000a034200049bb8e80670371f45508b5f8f59946a7c4dea4b3a23a036cf24c1f40993f4a1daad1716de8bd664ecb4596648d722a4685293de208c1d2da9361b9cba74c3d1ec`
    
        bstring, err := hex.DecodeString(str)
        if (err != nil) {
            panic(err)
        }
    
        var decode PubKey
        _, err = asn1.Unmarshal(bstring, &decode)
        if (err != nil) {
            panic(err)
        }
        fmt.Println(hex.EncodeToString(decode.Bs.Bytes))
    

    Note that you don't have to encode the string to hex and back again, since Unmarshall accepts a byte array

    This will print the expected result:

    049bb8e80670371f45508b5f8f59946a7c4dea4b3a23a036cf24c1f40993f4a1daad1716de8bd664ecb4596648d722a4685293de208c1d2da9361b9cba74c3d1ec
    

    Once again you probably don't need to encode to string.