我也遇到了这个问题,想从 Go 调用 REST API,所以需要转换 Python 脚本。我遇到的一个问题是标准 Go 库不支持加密的 PKCS 8 密钥,我实际上建议在没有加密密钥的情况下开始以消除额外的障碍。我了解您从文档中完成了这些步骤,但我将逐步将它们放入其中。
生成密钥(未加密)
$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
$ cat rsa_key.p8
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQChSSRI8qUHxvoe
TME1CQuUtGWQ+a2esAZ/yOaaVbGpAo8B3X8....
$ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
$ cat rsa_key.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoUkkSPKlB8b6HkzBNQkL
lLRlkPmtnrAGf8jmmlWxqQKPAd1/Aw+kn....
更新雪花用户
为用户设置公钥,只有密钥本身删除第一行和最后一行
ALTER USER MY_USER SET rsa_public_key='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoUkkSPKlB8b6HkzBNQkL
lLRlkPmtnrAGf8jmmlWxqQKPAd1/Aw+kn....';
获取更新后存储在用户身上的指纹。
DESCRIBE USER MY_USER;
找到属性RSA_PUBLIC_KEY_FP
并复制值。这将用于创建 JWT。
SHA256:+Uys1...
创建指纹
可以在此处找到用于创建此指纹的 Python 代码。
https://github.com/snowflakedb/snowflake-ingest-python/blob/master/snowflake/ingest/utils/tokentools.py#L108
这对我来说是一个很大的起点,我想确保我可以创建与 Snowflake 中的指纹相匹配的指纹。这是我在 Go 中的代码和测试。
import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"github.com/pkg/errors"
)
func calculatePublicKeyFingerprint(privateKey string) (string, error) {
pemBlock, _ := pem.Decode([]byte(privateKey))
parsedKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
if err != nil {
return "", errors.Wrap(err, "parse error")
}
var privKey *rsa.PrivateKey
var ok bool
if privKey, ok = parsedKey.(*rsa.PrivateKey); !ok {
return "", errors.New("Unable to parse RSA private key")
}
pubKey := privKey.Public().(*rsa.PublicKey)
pubDER, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", err
}
hasher := sha256.New()
hasher.Write(pubDER)
shaB64encoded := base64.StdEncoding.EncodeToString(hasher.Sum(nil))
return "SHA256:" + shaB64encoded, nil
}
单元测试 - Snowflake 的私钥和预期指纹已被截断,使用您的完整值。还使用 Testify 进行断言。
const privateKey = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQChSSRI8qUHxvoe
TME1CQuUtGWQ+a2esAZ/yOaaVbGpAo8B3X8....`
func Test_calculatePublicKeyFingerprint(t *testing.T) {
gotFingerprint, err := calculatePublicKeyFingerprint(privateKey)
require.NoError(t, err)
fromSnowflake := "SHA256:+Uys1..."
require.Equal(t, fromSnowflake, gotFingerprint)
}
创建 JWT
Python 安全管理器的 get_token 部分调用指纹创建并返回 JWT。
这是我的 Go 代码,它不像 Python 代码那样检查当前标记,这并不奇怪。它每次都会根据您的 Snowflake 帐户、用户和私钥创建一个新令牌。
import (
"github.com/dgrijalva/jwt-go"
)
const (
issuer = "iss"
expireTime = "exp"
issueTime = "iat"
subject = "sub"
expireDuration = time.Hour
)
func CreateJWT(account, user, privateKey string) (string, error) {
qualifiedUsername := strings.ToUpper(account + "." + user)
publicKeyFp, err := calculatePublicKeyFingerprint(privateKey)
if err != nil {
return "", err
}
claims := jwt.MapClaims{
issuer: qualifiedUsername + "." + publicKeyFp,
subject: qualifiedUsername,
issueTime: time.Now().Unix(),
expireTime: time.Now().Add(expireDuration).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
pk, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
if err != nil {
return "", err
}
return token.SignedString(pk)
}
为了清楚起见,我的声明看起来像
claims = jwt.MapClaims{
"iss": "MYSFACCT.MY_USER.SHA256:+Uys1...",
"sub": "MYSFACCT.MY_USER",
"iat": 1620322087,
"exp": 1620325687,
}
发出请求
curl --location --request POST 'https://mysfacct.snowflakecomputing.com/v1/data/pipes/MY_DATABASE.MY_SCHEMA.MY_PIPE/insertFiles' \
--header 'Authorization: Bearer generated.JWT.from-CreateJWT(account, user, privatekey)' \
--header 'Content-Type: application/json' \
--data-raw '{"files":[{"path":"my-file.json"}]}'
确保将 URL 替换为您的帐户和完全合格的管道。还将标题替换为您生成的 JWT。