7

I'm use crypto lib, ran into a problem: I need to convert the PublicKey type into byte[], as it can be done with a private key:

privkey.D.Bytes()

How can I solve this problem?

Folleah
  • 115
  • 1
  • 5
  • Did you see the documentation? `type PublicKey interface{}` It can be anything, and is probably not directly convertible to a byte slice. For instance an RSA PublicKey is a struct that contains a modulus and exponent. Converting this to a `[]byte` is meaningless and impossible anyway. This question is an [XY problem](https://meta.stackexchange.com/q/66377/189912). You should instead describe what you are really trying to accomplish by doing this. Then we can find a solution for the true underlying problem. – Michael Hampton Sep 25 '18 at 16:17
  • @MichaelHampton, I use another package in which I need to pass an array of bytes to the method. However, since I only have an interface, it's impossible to do it – Folleah Sep 25 '18 at 16:32
  • What other package? What method? – Michael Hampton Sep 25 '18 at 16:56
  • @MichaelHampton i use [this](https://github.com/btcsuite/btcd/issues/974#issuecomment-320462200) way, i need to get public key in byte[] format for use `btcutil.NewAddressPubKey(wif.SerializePubKey(), &chaincfg.MainNetParams)` – Folleah Sep 25 '18 at 18:51

2 Answers2

9

ecdsa.PrivateKey is a struct:

type PrivateKey struct {
        PublicKey
        D *big.Int
}

So privkey.D.Bytes() returns you the bytes of the D big integer.

Similarly, ecdsa.PublicKey:

type PublicKey struct {
        elliptic.Curve
        X, Y *big.Int
}

You may do the same with pubkey.X and pubkey.Y fields. These will give you 2 separate byte slices. If you need to merge them into one, you need to come up with some kind of "format", e.g. encoding the length of the first slice (the result of pubkey.X.Bytes()) using 4 bytes, then the first slice, then the length (4 bytes again) of the 2nd slice, and the second slice itself.

Best would be to use the elliptic.Marshal() function for this:

func Marshal(curve Curve, x, y *big.Int) []byte

Marshal converts a point into the uncompressed form specified in section 4.3.6 of ANSI X9.62.

Example using it:

var pubkey *ecdsa.PublicKey = // ...

data := elliptic.Marshal(pubkey, pubkey.X, pubkey.Y)
Community
  • 1
  • 1
icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    @Folleah, While it's tangential, it might worth mentioning that at least 3 "wire formats" were created to store/transmit cryptographic material—pkcs#7, PFX and pkcs#12 (see [this](https://en.wikipedia.org/wiki/PKCS)). The latter is arguably the most common these days, and [`github.com/SSLMate/go-pkcs12`](https://github.com/SSLMate/go-pkcs12) allows to create PKCS#12-formatted containers. [See also](https://godoc.org/?q=pkcs). – kostix Sep 25 '18 at 16:46
  • 1
    Instead of inventing your own encoding it would be wise to use the [Marshal](https://golang.org/pkg/crypto/elliptic/#Marshal) function to get a standardized uncompressed point to represent the public key. Using a container format such as suggested by the comment above is of course also a good option, but it is not similar to storing D. – Maarten Bodewes Sep 25 '18 at 16:55
  • note for if you are using rsa: `privkey.PublicKey.N.Bytes()` – Noam Hacker Oct 05 '18 at 16:25
4

For the lovely folks looking for a solution when it comes to ed25519/crypto. I banged my head for nearly 3 hours until I figured it out:

func getPrivateKey() ed25519.PrivateKey {
    // TODO You fill in this one
}

func main() {
    prvKey := getPrivateKey() // Get the private key

    pubKey := prvKey.Public().(ed25519.PublicKey)
    if !ok {
        log.Errorf("Could not assert the public key to ed25519 public key")
    }
    pubKeyBytes := []byte(pubKey)
}
  • I don't think this is entirely correct - the format of ed25519 private keys seems to be that of the seed concatenated with the public key. See https://stackoverflow.com/a/44874097/1623885 – George Aristy Nov 03 '20 at 21:53