I need to encrypt client-private-key with RSA-2048 server-public-key.
I know that private key is obviously longer than the public key and I'm not sure if it's possible... but I saw a similar task was done in Python, so I want to know your opinion.
/* main */
clientPrivateKey, _ := generateRsaPair(2048)
_, serverPublicKey := generateRsaPair(2048)
clientPrivateKeyAsByte := privateKeyToBytes(clientPrivateKey)
encryptWithPublicKey(clientPrivateKeyAsByte, serverPublicKey)
Fatal error crypto/rsa: message too long for RSA public key size
/* Functions */
func generateRsaPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey) {
privkey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
log.Error(err)
}
return privkey, &privkey.PublicKey
}
func encryptWithPublicKey(msg []byte, pub *rsa.PublicKey) []byte {
hash := sha512.New()
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
checkError(err)
return ciphertext
}
func privateKeyToBytes(priv *rsa.PrivateKey) []byte {
privBytes := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(priv),
},
)
return privBytes
}
If you want to encrypt something larger than the key size then you can simply use hybrid encryption. You first encrypt (or wrap, if a specific wrapping operation is available) the encoding of the private key using a random AES key, e.g. using AES-CBC or AES-CTR (with an all zero IV). Then you encrypt that AES key using the private key. The ciphertext consists of the encrypted AES key followed by the encrypted data - in this case an RSA private key.
Note however that a private key should really be managed by one entity. It is not called a private key for nothing. Distributing private keys is generally considered bad key management practice.
Related
How does one verify that a public key matched private key?
On start of the application, 2048-bit RSA keys are loaded from base64 PEM encoded string. I wish to verify that the keys are valid and that they match before continuing. The signing and verification is done by the underlining library I'm using.
I could sign and verify dummy data, but I am looking for alternative solutions.
Starter playground: https://play.golang.org/p/tsB8Yp-xs47
The solution is pretty simple
func verifyKeyPair(private, public string) bool {
// Handle errors here
block, _ := pem.Decode([]byte(rsaPrivateKey))
key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
pubBlock, _ := pem.Decode([]byte(rsaPublicKey))
pubKey, _ := x509.ParsePKIXPublicKey(pubBlock.Bytes)
return key.PublicKey.Equal(pubKey)
}
https://play.golang.org/p/tR6Ns0wDrlN
or alternatively if you want to handle other supported algorithms :
// KeysMatch generic check if public key is the same.
func KeysMatch(priv crypto.PrivateKey, pub crypto.PublicKey) bool {
privkey, ok := priv.(interface {
Public() crypto.PublicKey
})
if !ok {
return false
}
pubkey, ok := privkey.Public().(interface {
Equal(crypto.PublicKey) bool
})
if !ok {
return false
}
return pubkey.Equal(pub)
}
When integrating Sign in with Apple you generate a key in your apple developer account.
It's a file that is named like AuthKey_3JMD5K6.p8 and looks like
-----BEGIN PRIVATE KEY-----
MasdfjalskdasdflaASDFAadsflkjaADSFAewfljasdfljkasefasdflkjasdf
asdfljkasdfASDFASDFoiqretasdoiyjlfsbgREtaREGSDFBREtafsrgAREGfdsgaregR
LKJIOEWFNLasdflkawefjoiasdflk
-----END PRIVATE KEY-----
so I made a var appleKey := MasdfjalskdasdflaASDFAadsflkjaADSFAewfljasdfljkasefasdflkjasdf asdfljkasdfASDFASDFoiqretasdoiyjlfsbgREtaREGSDFBREtafsrgAREGfdsgaregRLKJIOEWFNLasdflkawefjoiasdflk
I've signed jwt with the HMAC-SHA method before which is fairly straightforward but I don't know how to sign a jwt with the ECDSA method.
I wrote my code the same way I did for the HMAC-SHA method but get an error key is of invalid type
So using the the jwt library for golang how can I sign my jwt with ECDSA method?
My code
// generate client secret jwt using apple key
expirationTime := time.Now().Add(5 * time.Minute)
claims := &Claims{
StandardClaims: jwt.StandardClaims {
Audience: "https://appleid.apple.com",
Subject: "com.app.ios",
Issuer: string(appleTeamId),
ExpiresAt: expirationTime.Unix(),
IssuedAt: time.Now().Unix(),
},
}
appleToken := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
appleToken.Header["kid"] = appleKid
signedAppleToken, err := appleToken.SignedString(appleKey)
I now know this isn't how you do it and it's a little bit more complex than that but what is the way to do it?
I found this article that tells you how to manually do it:
http://p.agnihotry.com/post/validating_sign_in_with_apple_authorization_code/
But I'm already using the jwt library for golang for the other part of the token:
https://godoc.org/github.com/dgrijalva/jwt-go
In the github.com/dgrijalva/jwt-go's SigningMethodECDSA.Sign docs you can find:
[...] For this signing method, key must be an ecdsa.PrivateKey struct
So, to put together an example:
p8bytes, err := ioutil.ReadFile("SomeAppleKey.p8")
if err != nil {
log.Println(err)
return
}
// Here you need to decode the Apple private key, which is in pem format
block, _ := pem.Decode(p8bytes)
// Check if it's a private key
if block == nil || block.Type != "PRIVATE KEY" {
log.Println("Failed to decode PEM block containing private key")
return
}
// Get the encoded bytes
x509Encoded := block.Bytes
token := jwt.NewWithClaims(
jwt.SigningMethodES256, // specific instance of `*SigningMethodECDSA`
jwt.StandardClaims{
// ...
},
)
// Now you need an instance of *ecdsa.PrivateKey
parsedKey, err := x509.ParsePKCS8PrivateKey(x509Encoded) // EDIT to x509Encoded from p8bytes
if err != nil {
panic(err)
}
ecdsaPrivateKey, ok := parsedKey.(*ecdsa.PrivateKey)
if !ok {
panic("not ecdsa private key")
}
// Finally sign the token with the value of type *ecdsa.PrivateKey
signed, err := token.SignedString(ecdsaPrivateKey)
if err != nil {
panic(err)
}
fmt.Println(signed) // the signed JWT
Note: as shown in the code snippet, because the key file from Apple is in PEM format, it needs to be decoded first
Warning!
Please be aware that github.com/dgrijalva/jwt-go has been unmaintained for a long time and has critical unfixed bugs. And doesn't support Go modules, before the version 4 (which is just a preview anyway). I strongly recommend to choose an different library for dealing with JWT.
Update June 2021
There is now an official community fork of the library: golang-jwt/jwt blessed by the owner of the original project.
I am trying to use jwt library to do jwt validation. And i am getting public key from another application by calling its REST endpoint which is returning public key in string format.
So now when trying to send that public key in same string format, i am getting "Invalid key format". Any help on how to convert string formatted key to a valid PEM format would be great.
func (test *TESTStrategy) doJWTValidation(token string, key string, logger *util.Logger) (TESTResponse, error) {
parsedToken, jwtErr := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return decodedJWT.ParsedPubKey, nil
})
Below is the error what i am getting when passing key as a string to jwt.Pasrse() call.
Public Key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsFWkb/eSl6I3DRVhaonW3DFy8EnL0yaPiDzCcOLuYfBjN9zZIR1wXmnMJFle1K89qHGg42wgweVTIwA1XFTfoUKSziwsjF6FscZX5H56ZYyS/wWiO3rWWynlfbSZt+ga71+ndsu+A0Dy7Nn7ZgP8kRsu4UM5vE7QQTRERNiUKpzScN1cgZUFUqSddQmkwTEN8hH1mFX1Mum54NGqWIlmQxQDrOyogmMXIaaakhabcmuIPMULVVDVwUJC9sSDsc/j05qcZn3kkiEBRyiYB6ZLY2W7WfiV+dB7/icPONsYSD0FxHWEGNnbqtiGoNf9WZWtaP+o8WMR9sB3GKGVnbLvbQIDAQAB
That's a PEM encoded key, it's just missing the BEGIN & END headers. The key is simple Base64 encoded, you can decode and unmarshal into a RSA key as follows:
base64Data := []byte(`MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsFWkb/eSl6I3DRVhaonW3DFy8EnL0yaPiDzCcOLuYfBjN9zZIR1wXmnMJFle1K89qHGg42wgweVTIwA1XFTfoUKSziwsjF6FscZX5H56ZYyS/wWiO3rWWynlfbSZt+ga71+ndsu+A0Dy7Nn7ZgP8kRsu4UM5vE7QQTRERNiUKpzScN1cgZUFUqSddQmkwTEN8hH1mFX1Mum54NGqWIlmQxQDrOyogmMXIaaakhabcmuIPMULVVDVwUJC9sSDsc/j05qcZn3kkiEBRyiYB6ZLY2W7WfiV+dB7/icPONsYSD0FxHWEGNnbqtiGoNf9WZWtaP+o8WMR9sB3GKGVnbLvbQIDAQAB`)
d := make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
n, err := base64.StdEncoding.Decode(d, base64Data)
if err != nil {
// Handle error
}
d = d[:n]
key,err:=x509.ParsePKIXPublicKey(d)
if err != nil {
// Handle error
}
fmt.Println(key)
If you need the key in PEM encoded form, simple add the appropriate header and footer, e.g. -----BEGIN PUBLIC KEY----- & -----END PUBLIC KEY-----. Note that the BEGIN header must start on its own line and end in a new line ("\n"). The END header must also be proceeded by a new line.
I am working on a go project that will need to verify an OpenPGP public key, to be able to use it to verify file signatures.
I've generated a root key, and another key, which I've signed with the root key (let's call the second key signed). I've exported the public part of the signed key to an armored text file, for easy distribution:
gpg --export -a signed > signed.asc
I've written this go code which illustrates what I want to do, in the end:
package main
import (
"flag"
"fmt"
"golang.org/x/crypto/openpgp"
"os"
)
func main() {
var keyringpath string
var signedkeypath string
flag.StringVar(&keyringpath, "keyring", "", "keyring")
flag.StringVar(&signedkeypath, "signedkey", "", "signed key")
flag.Parse()
// read the keyring
keyring, err := os.Open(keyringpath)
if err != nil {
panic(err)
}
el, err := openpgp.ReadKeyRing(keyring)
if err != nil {
panic(err)
}
var rootidentity *openpgp.Entity
for _, entity := range el {
if _, ok := entity.Identities["root"]; ok {
rootidentity = entity
}
}
fmt.Printf("%+v\n", rootidentity)
// read the public armored key
signedkey, err := os.Open(signedkeypath)
if err != nil {
panic(err)
}
el, err = openpgp.ReadArmoredKeyRing(signedkey)
if err != nil {
panic(err)
}
signed := el[0]
fmt.Printf("%+v\n", signed)
// there is only one signature on signed, the one produced by root
signature := signed.Identities["signed"].Signatures[0]
err = rootidentity.PrimaryKey.VerifyKeySignature(signed.PrimaryKey, signature)
if err != nil {
panic(err)
}
}
When I run it, I give keyring my public keyring (~/.gnupg/pubring.gpg) and signedkey my exported signed key (signed.asc).
In production, the idea is to also export the root public key from pubring.gpg into armored text, and embed that in the code.
The signature fails to verify with the following error:
panic: openpgp: invalid signature: hash tag doesn't match
Looking at the code of VerifyKeySignature (and especially this comment), I get the feeling that it's meant to only be used to verify signatures on subkeys, rather than other keys.
So, the question is, given two public PGP keys, one signed by the other, how do I verify that signature using the openpgp library?
Not sure whether I should close this question or not: I found the answer. It isn't very clear in the docs, but VerifyKeySignature is indeed probably only used for subkeys. For verifying the signatures on other users' public keys, use VerifyUserIdSignature, like so:
err = rootidentity.PrimaryKey.VerifyUserIdSignature("signed", signed.PrimaryKey, signature)
if err != nil {
panic(err)
}
I'm trying to generate a token with a rsa key using the jwt-go package in golang.
Here there is a blog explaining how to do it but that code will always be validating all tokens because is using the public key stored in the server and is not obtaining it from the token. How do you put the complete public key in the token? I was trying this:
var secretKey, _ = rsa.GenerateKey(rand.Reader, 1024)
token := jwt.New(jwt.SigningMethodRS256)
token.Claims["username"] = "victorsamuelmd"
token.Claims["N"] = secretKey.PublicKey.N
token.Claims["E"] = secretKey.PublicKey.E
tokenString, err := token.SignedString(secretKey)
nt, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
// here I need to recover the public key from the token
// but N is a big.Int and the token stores N as int64
})
Sorry about my english. Thanks.
I think storing the public key in the claims is not good idea because we can verify the JWT with that key technically, but it means it is not a signed JWT anymore. If anyone can generate the JWT with their own private key and storing the public key in JWT, we cannot sure who is signer.
Anyway, you can convert the public key into PEM format which is just a string, and store it in claims. In client side, you can also simply parse it again into public key format. The sample code is below:
privateKey, _ := rsa.GenerateKey(rand.Reader, 1024)
bytes, _ := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
pem := pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: bytes,
})
claim["publickey"] = string(pem)
and
pem := []byte(claims["publickey"].(string))
return jwt.ParseRSAPublicKeyFromPEM(pem)
jwt is dgrijalva's jwt-go.