Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
350 views
in Technique[技术] by (71.8m points)

go - Marshal AppleID's Public Key to rsa.PublicKey

I am trying to validate a JWT from Apple in Go and I need their public key as Go's rsa.PublicKey to do so. I retrieved their keys from the endpoint specified in:

https://developer.apple.com/documentation/sign_in_with_apple/fetch_apple_s_public_key_for_verifying_token_signature

and got the following:

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "86D88Kf",
      "use": "sig",
      "alg": "RS256",
      "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "kid": "eXaunmL",
      "use": "sig",
      "alg": "RS256",
      "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
      "e": "AQAB"
    }
  ]
}

I've tried the x509.ParsePKCS1PublicKey(...) function by parsing both the individual key and the entire JSON returned using asn1.Marshall(...) function. I got an error trying to parse the der.

I then noticed that the key contains "n" and "e" string pairs, and are defined here so I tried to create the public key directly. However, N and E in the rsa.PublicKey are a *big.Int and int respectively.

I can't seem to find the encoding for "n" and "e", so I can't accurately convert the values to an rsa.PublicKey. I've tried base64, but that did not work.

Can anyone tell me how I can convert Apple's public key to a suitable rsa.PublicKey please?

question from:https://stackoverflow.com/questions/66067321/marshal-appleids-public-key-to-rsa-publickey

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The values of "n" and "e" in your JSON are just base64-encoded big-endian binary integers, so once you've decoded them you can convert them to type *big.Int with big.Int.SetBytes, and then use those to populate an *rsa.PublicKey.

You mentioned you tried base64 and it didn't work, but you need to make sure you use the right encoding and padding options- the presence of - and _ characters in the encoded string indicates that you're dealing with the RFC 4648 URL-safe encoding, and the fact that the length of the string is not divisible by 4 indicates that no padding characters are present, so therefore base64.URLEncoding.WithPadding(base64.NoPadding) is what you need to use.

Comprehensive example of a type you can directly unmarshal into and convert:

package main

import (
        "crypto/rsa"
        "encoding/base64"
        "encoding/json"
        "log"
        "math/big"
)

const keyJSON = `{
    "kty": "RSA",
    "kid": "86D88Kf",
    "use": "sig",
    "alg": "RS256",
    "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
    "e": "AQAB"
}`

// decodeBase64BigInt decodes a base64-encoded larger integer from Apple's key format.
func decodeBase64BigInt(s string) *big.Int {
        buffer, err := base64.URLEncoding.WithPadding(base64.NoPadding).DecodeString(s)
        if err != nil {
                log.Fatalf("failed to decode base64: %v", err)
        }

        return big.NewInt(0).SetBytes(buffer)
}

// appleKey is a type of public key.
type appleKey struct {
        KTY string
        KID string
        Use string
        Alg string
        N   *big.Int
        E   int
}

// UnmarshalJSON parses a JSON-encoded value and stores the result in the object.
func (k *appleKey) UnmarshalJSON(b []byte) error {
        var tmp struct {
                KTY string `json:"kty"`
                KID string `json:"kid"`
                Use string `json:"use"`
                Alg string `json:"alg"`
                N   string `json:"n"`
                E   string `json:"e"`
        }

        if err := json.Unmarshal(b, &tmp); err != nil {
                return err
        }

        *k = appleKey{
                KTY: tmp.KTY,
                KID: tmp.KID,
                Use: tmp.Use,
                Alg: tmp.Alg,
                N:   decodeBase64BigInt(tmp.N),
                E:   int(decodeBase64BigInt(tmp.E).Int64()),
        }

        return nil
}

// RSA returns a corresponding *rsa.PublicKey
func (k appleKey) RSA() *rsa.PublicKey {
        return &rsa.PublicKey{
                N: k.N,
                E: k.E,
        }
}

func main() {
        // Decode the Apple key.
        var ak appleKey
        if err := json.Unmarshal([]byte(keyJSON), &ak); err != nil {
                log.Fatalf("failed to unmarshal JSON: %v", err)
        }

        // Convert it to a normal *rsa.PublicKey.
        rk := ak.RSA()

        if rk.Size() != 256 {
                log.Fatalf("unexpected key size: %d", rk.Size())
        }

        // Do what you like with the RSA key now.
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...