generate valid dkim key using golang - go

I am tring to generate dkim key using golang, my current code is :
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/gob"
"encoding/pem"
"fmt"
"os"
)
func Keymaker(path string) {
reader := rand.Reader
bitSize := 2048
key, err := rsa.GenerateKey(reader, bitSize)
checkError(err)
publicKey := key.PublicKey
//saveGobKey("private.key", key)
savePEMKey(path+".priv", key)
//saveGobKey("public.key", publicKey)
savePublicPEMKey(path+".pub", publicKey)
}
func savePEMKey(fileName string, key *rsa.PrivateKey) {
outFile, err := os.Create(fileName)
checkError(err)
defer outFile.Close()
var privateKey = &pem.Block{
Type: "PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
err = pem.Encode(outFile, privateKey)
checkError(err)
}
func savePublicPEMKey(fileName string, pubkey rsa.PublicKey) {
asn1Bytes, err := asn1.Marshal(pubkey)
checkError(err)
var pemkey = &pem.Block{
Type: "PUBLIC KEY",
Bytes: asn1Bytes,
}
pemfile, err := os.Create(fileName)
checkError(err)
defer pemfile.Close()
err = pem.Encode(pemfile, pemkey)
checkError(err)
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
the code is working, generate the dkim private/public keys, problem is this keys return bad keys by sender, if i generate keys using this site : https://easydmarc.com/tools/dkim-record-generator/ the keys results valid.
this is my dkim settings in dns :
v=DKIM1;t=s;p=MIGJAoGBD1DSPRFFTujiCAiadP9ZeA6eAyf0GktfCQO2WKbrAx2Cu15y2A+agQMWNTmZbbhcznGZiz3kA9y7c0wOqN2iaJchBAxt4qdg1vUhy+6CkSZcY5bdm8uOxsXW3gjOTyZrOur/c118wMTOmSJKHOjn9xLG3ta8tXJtqab8idtx4TlBAgMBAAE=
i try to understend way keys generated by easydmarc.com working, and golang keys not working. Any ideea ?

Related

OpenSSL SHA256 signature

I have a private key in a file -----BEGIN PRIVATE KEY-----\nxxx-----END PRIVATE KEY-----\n
I am trying to perform the below which is in Ruby in Go and got stuck as Go accepts only pem file. Kindly suggest.
azure_certificate_private_key = OpenSSL::PKey.read(private_key_file)
base64_signature = Base64.strict_encode64(private_key.sign(OpenSSL::Digest::SHA256.new, headerPayloadConcatenated))
I have tried as,
signBytes, err := ioutil.ReadFile(privKeyPath)
signer, err := ParsePrivateKey(signBytes)
no key found error
package main
import (
"encoding/pem"
"crypto/x509"
"io/ioutil"
)
func main() {
signBytes, err := ioutil.ReadFile(privKeyPath)
if err != nil {
panic(err) //or do whatever with err
}
keyRSA := parseRSAKey(signBytes)
//keyRSA is a *rsa.PrivateKey
//....use the key
}
func parseRSAKey(b []byte) *rsa.PrivateKey {
block, _ := pem.Decode(b)
if block == nil {
panic("no PEM block")
}
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
return key
}
this should work to parse a RSA private key.

Error Decrypting test message with RSA/PEM files

Hi all I am currently trying to accomplish three things with the following code.
Generate a public/private key pair using the crypto/rsa library.
Export the public and private keys into individual PEM files to be used in separate programs.
Load the PEM files respectively into their individual scripts to encode/decode messages.
Everything works fine until I try to decrypt a test message with "Private-key-decryption.go". I received this error when decrypting the ciphertext "Error from decryption: crypto/rsa: decryption error"
Included are all of my code blocks I am using
Key-Generation.go
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
//Write_private_key_to_file write pem_key to a file
func WriteToFile(Pem_Key string, filename string) {
f, err := os.Create(filename)
if err != nil {
fmt.Println(err)
return
}
l, err := f.WriteString(Pem_Key)
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(l, "bytes written successfully")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
//ExportRsaPrivateKeyAsPemStr returns private pem key
func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string {
privkey_bytes := x509.MarshalPKCS1PrivateKey(privkey)
privkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privkey_bytes,
},
)
return string(privkey_pem)
}
//ExportRsaPublicKeyAsPemStr_to_pem_file extracts public key from generated private key
func ExportRsaPublicKeyAsPemStr(publickey *rsa.PublicKey) (string, error) {
pubkey_bytes, err := x509.MarshalPKIXPublicKey(publickey)
if err != nil {
return "", err
}
//fmt.Println(pubkey_bytes)
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return string(pubkey_pem), nil
}
func main() {
// generate a 1024-bit private-key
priv, err := rsa.GenerateKey(rand.Reader, 1024)
// extract the public key from the private key as string
pub := &priv.PublicKey
message := []byte("test message")
hashed := sha256.Sum256(message)
signature, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, hashed[:])
if err != nil {
fmt.Printf("Error from signing: %s\n", err)
return
}
err = rsa.VerifyPKCS1v15(&priv.PublicKey, crypto.SHA256, hashed[:], signature)
if err != nil {
fmt.Printf("Error from verification: %s\n", err)
return
} else {
fmt.Printf("signature is verified\n")
}
//calling function to export private key into PEM file
pem_priv := ExportRsaPrivateKeyAsPemStr(priv)
//writing private key to file
WriteToFile(pem_priv, "private-key.pem")
//calling function to export public key as pPEM file
pem_pub, _ := ExportRsaPublicKeyAsPemStr(pub)
WriteToFile(pem_pub, "public-key.pem")
}
Public-key_encryption.go
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
)
//ParseRsaPublicKeyFromPemStr takes a publicKeyPEM file as a string and returns a rsa.PublicKey object
func ParseRsaPublicKeyFromPemStr(pubPEM string) (*rsa.PublicKey, error) {
block, _ := pem.Decode([]byte(pubPEM))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the key")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
switch pub := pub.(type) {
case *rsa.PublicKey:
return pub, nil
default:
break // fall through
}
return nil, errors.New("Key type is not RSA")
}
func main() {
//reading in the public key file to be passed the the rsa object creator
PublicKeyAsString, err := ioutil.ReadFile("public-key.pem")
if err != nil {
fmt.Print(err)
}
//Creating parsing Public PEM key to *rsa.PublicKey
rsa_public_key_object, _ := ParseRsaPublicKeyFromPemStr(string(PublicKeyAsString))
challengeMsg := []byte("c")
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsa_public_key_object, challengeMsg, nil)
if err != nil {
fmt.Printf("Error from encryption: %s\n", err)
return
}
fmt.Printf("%x", ciphertext)
}
Private-key-decryption.go
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
)
//takes a privatekey PEM file as a string and returns a pointer rsa.PublicKey object
func parseRsaPrivateKeyFromPemStr(p string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(p))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the key")
}
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return key, nil
}
func main() {
//reading in the public key file to be passed the the rsa object creator
PrivateKeyAsString, err := ioutil.ReadFile("private-key.pem")
if err != nil {
fmt.Print(err)
}
//Creating parsing private PEM key to *rsa.PublicKey
rsa_private_key_object, _ := parseRsaPrivateKeyFromPemStr(string(PrivateKeyAsString))
ciphertext := []byte("1f58ab29106c7971c9a4307c39b6b09f8910b7ac38a8d0abc15de14cbb0f651aa5c7ca377fd64a20017eaaff0a57358bc8dd05645c8b2b24bbb137ab2e5cf657f9a6a7593ce8d043dd774d79986b00f679fc1492a6ed4961f0e1941a5ef3c6ec99f952b0756700a05314c31c768fe9463f77f23312a51a97587b04b4d8b50de0")
plaintext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, rsa_private_key_object, ciphertext, nil)
if err != nil {
fmt.Printf("Error from decryption: %s\n", err)
return
}
fmt.Printf("\nPlaintext: %s\n", string(plaintext))
}
Please let me know what needs to be changed. This is my first crypto project and I am starting to get bags under my eyes lol
You're close. In the encryption part, you produce a hex string with that %x format string. So, in the decryption part, you should do the corresponding hex decode.
In your Private-key-decryption.go, change
ciphertext := []byte("1f58ab29106c7971c9a4307c39b6b09f8910b7ac38a8d0abc15de14cbb0f651aa5c7ca377fd64a20017eaaff0a57358bc8dd05645c8b2b24bbb137ab2e5cf657f9a6a7593ce8d043dd774d79986b00f679fc1492a6ed4961f0e1941a5ef3c6ec99f952b0756700a05314c31c768fe9463f77f23312a51a97587b04b4d8b50de0")
to
ciphertext, err := hex.DecodeString("1f58ab29106c7971c9a4307c39b6b09f8910b7ac38a8d0abc15de14cbb0f651aa5c7ca377fd64a20017eaaff0a57358bc8dd05645c8b2b24bbb137ab2e5cf657f9a6a7593ce8d043dd774d79986b00f679fc1492a6ed4961f0e1941a5ef3c6ec99f952b0756700a05314c31c768fe9463f77f23312a51a97587b04b4d8b50de0")
if err != nil {
fmt.Printf("Error from hex decode: %s\n", err)
return
}

generate rsa key valid for dkim validation

I am tring to generate dkim key using golang, my current code is :
package appdkim
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"os"
)
func Keymaker(path string) {
reader := rand.Reader
bitSize := 2048
key, err := rsa.GenerateKey(reader, bitSize)
checkError(err)
publicKey := key.PublicKey
savePEMKey(path+".priv", key)
savePublicPEMKey2(path+".pub", publicKey)
return
}
func savePEMKey(fileName string, key *rsa.PrivateKey) {
outFile, err := os.Create(fileName)
checkError(err)
defer outFile.Close()
var privateKey = &pem.Block{
Type: "PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
err = pem.Encode(outFile, privateKey)
checkError(err)
}
func savePublicPEMKey2(fileName string, pubkey rsa.PublicKey) {
//var PublicKeyRow bytes.Buffer
var pemkey = &pem.Block{
Type: "PUBLIC KEY",
Bytes: x509.MarshalPKCS1PublicKey(&pubkey),
}
pemfile, err := os.Create(fileName)
checkError(err)
defer pemfile.Close()
err = pem.Encode(pemfile, pemkey)
checkError(err)
//err = pem.Encode(&PublicKeyRow, pemkey)
//checkError(err)
//return PublicKeyRow.String()
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
the code is working, generate the dkim private/public keys, problem is this keys return bad keys by sender, if i generate keys using this site : https://easydmarc.com/tools/dkim-record-generator/ the keys results valid.
this is my dkim settings in dns :
v=DKIM1;t=s;p=MIGJAoGBD1DSPRFFTujiCAiadP9ZeA6eAyf0GktfCQO2WKbrAx2Cu15y2A+agQMWNTmZbbhcznGZiz3kA9y7c0wOqN2iaJchBAxt4qdg1vUhy+6CkSZcY5bdm8uOxsXW3gjOTyZrOur/c118wMTOmSJKHOjn9xLG3ta8tXJtqab8idtx4TlBAgMBAAE=
I try to understend what if diferent with my golang key like key generated by easydmarc.com. What i need to change to my golang code for be valid for dkim validation ?

How to verify RSA key length in Go?

I'm setting up a https server in Go using the following function.
err := http.ListenAndServeTLS(":8080", key, cert, nil)
if err != nil {
log.Fatal("error...")
}
Where key and cert are respectively self-signed key and certificate files.
My problem is that for security I need to validate they self-signed key to have a 2048 bits size (or more).
How can I securely and cleanly check for this in Go?
package main
import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"log"
"net/http"
)
func main() {
certFile := "/tmp/cert.pem"
keyFile := "/tmp/key.pem"
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
log.Fatal(err)
}
var bitLen int
switch privKey := cert.PrivateKey.(type) {
case *rsa.PrivateKey:
bitLen = privKey.N.BitLen()
case *ecdsa.PrivateKey:
bitLen = privKey.Curve.Params().BitSize
default:
log.Fatal("unsupported private key")
}
if bitLen < 2048 {
log.Fatalf("private key length is too small (size: %d)\n", bitLen)
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{cert},
}
server := http.Server{
Addr: ":8080",
TLSConfig: &tlsConfig,
}
if err := server.ListenAndServeTLS("", ""); err != nil {
log.Fatal(err)
}
}

Save and load crypto/rsa PrivateKey to and from the disk

I'm using crypto/rsa, and trying to find a way to properly save and load a key. Is there a correct way to create a []byte from an rsa.PrivateKey. If so, is there a way to properly do so for an rsa.PublicKey?
Thank you all very much.
You need some sort of format to marshal the key into. One format supported by the Go standard library can be found here: http://golang.org/pkg/crypto/x509/#MarshalPKCS1PrivateKey
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte
The inverse function is http://golang.org/pkg/crypto/x509/#ParsePKCS1PrivateKey.
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)
However, it is relatively standard to encode the marshaled key into a PEM file.
pemdata := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
},
)
You can find a full example here.
Here's code snippet that shows the import and export of both public and private keys. It's based on the other answers which were super helpful, as well as copy-pasta from the official docs.
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
)
func GenerateRsaKeyPair() (*rsa.PrivateKey, *rsa.PublicKey) {
privkey, _ := rsa.GenerateKey(rand.Reader, 4096)
return privkey, &privkey.PublicKey
}
func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string {
privkey_bytes := x509.MarshalPKCS1PrivateKey(privkey)
privkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privkey_bytes,
},
)
return string(privkey_pem)
}
func ParseRsaPrivateKeyFromPemStr(privPEM string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(privPEM))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the key")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return priv, nil
}
func ExportRsaPublicKeyAsPemStr(pubkey *rsa.PublicKey) (string, error) {
pubkey_bytes, err := x509.MarshalPKIXPublicKey(pubkey)
if err != nil {
return "", err
}
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return string(pubkey_pem), nil
}
func ParseRsaPublicKeyFromPemStr(pubPEM string) (*rsa.PublicKey, error) {
block, _ := pem.Decode([]byte(pubPEM))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the key")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
switch pub := pub.(type) {
case *rsa.PublicKey:
return pub, nil
default:
break // fall through
}
return nil, errors.New("Key type is not RSA")
}
func main() {
// Create the keys
priv, pub := GenerateRsaKeyPair()
// Export the keys to pem string
priv_pem := ExportRsaPrivateKeyAsPemStr(priv)
pub_pem, _ := ExportRsaPublicKeyAsPemStr(pub)
// Import the keys from pem string
priv_parsed, _ := ParseRsaPrivateKeyFromPemStr(priv_pem)
pub_parsed, _ := ParseRsaPublicKeyFromPemStr(pub_pem)
// Export the newly imported keys
priv_parsed_pem := ExportRsaPrivateKeyAsPemStr(priv_parsed)
pub_parsed_pem, _ := ExportRsaPublicKeyAsPemStr(pub_parsed)
fmt.Println(priv_parsed_pem)
fmt.Println(pub_parsed_pem)
// Check that the exported/imported keys match the original keys
if priv_pem != priv_parsed_pem || pub_pem != pub_parsed_pem {
fmt.Println("Failure: Export and Import did not result in same Keys")
} else {
fmt.Println("Success")
}
}
Since the public key part of your question wasn't answered and I just ran into the same problem and solved it, here it is:
Note the & in front of the Argument to MarshalPKIXPublicKey
Priv := rsa.GenerateKey(rand.Reader, 4096)
pubASN1, err := x509.MarshalPKIXPublicKey(&Priv.PublicKey)
if err != nil {
// do something about it
}
pubBytes := pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubASN1,
})
ioutil.WriteFile("key.pub", pubBytes, 0644)
Relevant reads:
MarshalPKIXPublicKey(pub interface{}) ([]byte, error) godoc
EncodeToMemory(b *Block) []byte godoc
Block godoc
PS: MarshalPKIXPublicKey also accepts ECDSA keys, ajust the pem header appropriately.

Resources