You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While enhancing the use of ECDH, ensure compatibility of generated key-pairs between OpenSSL genpkey and golang's crypto/ecdh.
Details
To generate key-pairs of ECDH with X25519 curve in OpenSSL command is as below:
$ openssl genpkey -algorithm X25519 -out x25519-private.pem -outpubkey x25519-public.pem
$ cat x25519-private.pem-----BEGIN PRIVATE KEY-----MC4CAQAwBQYDK2VuBCIEICgJptfzbyGJr1Xk1tPBiZdT3u6ut6hqGuSnGdXp7JFS-----END PRIVATE KEY-----
$ cat x25519-public.pem-----BEGIN PUBLIC KEY-----MCowBQYDK2VuAyEABCS8+49qsm8+5JH+uin9L0ksOl/I5ZtzU1xaL16lTS0=-----END PUBLIC KEY-----
$ openssl --versionOpenSSL 3.4.0 22 Oct 2024 (Library: OpenSSL 3.4.0 22 Oct 2024)
To parse the file in go would be:
// Read the PEM file containing the public keypubKeyPEM, err:=os.ReadFile(path)
iferr!=nil {
returnnil, nil, "", errors.Wrap(err, "failed to read public key PEM")
}
// Decode the PEM blockblock, _:=pem.Decode(pubKeyPEM)
ifblock==nil||block.Type!="PUBLIC KEY" {
returnnil, nil, "", errors.New("failed to decode PEM block")
}
fmt.Printf("PubKey: %x\n", block.Bytes)
But the bytes in block are not directly compatible with ecdh.PublicKey.Bytes().
This is due to the format of the decoded bytes from PEM.
The decoded bytes of the contents is typically a DER encoded ASN.1 structure.
Thus, we need to ensure the compatibility of the keys between OpenSSL and crypto/ecdh.
Here's my two cents to test.
$ openssl genpkey -algorithm X25519 -out x25519-private.pem -outpubkey x25519-public.pem
$ cat x25519-private.pem-----BEGIN PRIVATE KEY-----MC4CAQAwBQYDK2VuBCIEICgJptfzbyGJr1Xk1tPBiZdT3u6ut6hqGuSnGdXp7JFS-----END PRIVATE KEY-----
$ cat x25519-public.pem-----BEGIN PUBLIC KEY-----MCowBQYDK2VuAyEABCS8+49qsm8+5JH+uin9L0ksOl/I5ZtzU1xaL16lTS0=-----END PUBLIC KEY-----
$ go run main.goSuccessfully parsed private key: *ecdh.PrivateKeyPrivate Key (Parsed): 2809a6d7f36f2189af55e4d6d3c1899753deeeaeb7a86a1ae4a719d5e9ec9152Private Key (DER HEX): 302e020100300506032b656e042204202809a6d7f36f2189af55e4d6d3c1899753deeeaeb7a86a1ae4a719d5e9ec9152Private Key (DER Base64): MC4CAQAwBQYDK2VuBCIEICgJptfzbyGJr1Xk1tPBiZdT3u6ut6hqGuSnGdXp7JFSSuccessfully parsed public key: *ecdh.PublicKeyPublic Key (DER Base64): MCowBQYDK2VuAyEABCS8+49qsm8+5JH+uin9L0ksOl/I5ZtzU1xaL16lTS0=Public Key (DER Base64 from file): MCowBQYDK2VuAyEABCS8+49qsm8+5JH+uin9L0ksOl/I5ZtzU1xaL16lTS0=Derived Public Key (Base64): BCS8+49qsm8+5JH+uin9L0ksOl/I5ZtzU1xaL16lTS0=Does OpenSSL public key match derived public key? true
The main.go is:
package main
import (
"crypto/ecdh""crypto/x509""encoding/base64""encoding/pem""fmt""os""github.com/pkg/errors"
)
funcmain() {
// Read and parse the private key from PEM fileprivKey, privKeyDER, privKeyBase64, err:=PEMtoECDHprivate("x25519-private.pem")
panicOnError(err)
// Print the parsed private key informationfmt.Printf("Successfully parsed private key: %T\n", privKey)
fmt.Printf("Private Key (Parsed): %x\n", privKey.Bytes())
fmt.Printf("Private Key (DER HEX): %x\n", privKeyDER)
fmt.Printf("Private Key (DER Base64): %s\n", privKeyBase64)
// Read and parse the public key from PEM filepubKey, pubKeyDER, pubKeyBase64, err:=PEMtoECDHpublic("x25519-public.pem")
panicOnError(err)
// Print the parsed public key informationfmt.Printf("Successfully parsed public key: %T\n", pubKey)
fmt.Printf("Public Key (DER Base64): %s\n", pubKeyBase64)
// Print the DER-encoded public key in Base64fmt.Printf("Public Key (DER Base64 from file): %s\n", base64.StdEncoding.EncodeToString(pubKeyDER))
// Derive the public key from the private key and compare it with the parsed public keyderivedPubKey:=privKey.PublicKey()
// Print the derived public key in Base64fmt.Printf("Derived Public Key (Base64): %s\n", base64.StdEncoding.EncodeToString(derivedPubKey.Bytes()))
// Compare the parsed public key with the derived public keyisMatch:=base64.StdEncoding.EncodeToString(pubKey.Bytes()) ==base64.StdEncoding.EncodeToString(derivedPubKey.Bytes())
fmt.Printf("Does OpenSSL public key match derived public key? %v\n", isMatch)
}
// PEMtoECDHprivate reads a PEM file containing an ECDH private key and returns:// - the parsed key in ecdh.PrivateKey type// - the raw private key bytes with DER encoded ASN.1 structure// - the Base64 encoded of the raw private key// - an error if anyfuncPEMtoECDHprivate(pathstring) (*ecdh.PrivateKey, []byte, string, error) {
// Read the PEM file containing the private keyprivKeyPEM, err:=os.ReadFile(path)
iferr!=nil {
returnnil, nil, "", errors.Wrap(err, "failed to read private key PEM")
}
// Decode the PEM blockblock, _:=pem.Decode(privKeyPEM)
ifblock==nil||block.Type!="PRIVATE KEY" {
returnnil, nil, "", errors.New("failed to decode PEM block")
}
// Parse the PKCS#8 private key to get *ecdh.PrivateKeyparsedKey, err:=x509.ParsePKCS8PrivateKey(block.Bytes)
iferr!=nil {
returnnil, nil, "", errors.Wrap(err, "failed to parse PKCS#8 private key")
}
// Ensure the key is of type *ecdh.PrivateKeyprivKey, ok:=parsedKey.(*ecdh.PrivateKey)
if!ok {
returnnil, nil, "", errors.Errorf("unexpected private key type: %T", parsedKey)
}
// Base64 encode the raw private key (DER format)privKeyBase64:=base64.StdEncoding.EncodeToString(block.Bytes)
returnprivKey, block.Bytes, privKeyBase64, nil
}
// PEMtoECDHpublic reads a PEM file containing an ECDH public key and returns:// - the parsed key in ecdh.PublicKey type// - the raw public key bytes with DER encoded ASN.1 structure// - the Base64 encoded of the raw public key// - an error if anyfuncPEMtoECDHpublic(pathstring) (*ecdh.PublicKey, []byte, string, error) {
// Read the PEM file containing the public keypubKeyPEM, err:=os.ReadFile(path)
iferr!=nil {
returnnil, nil, "", errors.Wrap(err, "failed to read public key PEM")
}
// Decode the PEM blockblock, _:=pem.Decode(pubKeyPEM)
ifblock==nil||block.Type!="PUBLIC KEY" {
returnnil, nil, "", errors.New("failed to decode PEM block")
}
// Parse the public key from the PEMparsedKey, err:=x509.ParsePKIXPublicKey(block.Bytes)
iferr!=nil {
returnnil, nil, "", errors.Wrap(err, "failed to parse PKIX public key")
}
// Ensure the key is of type *ecdh.PublicKeypubKey, ok:=parsedKey.(*ecdh.PublicKey)
if!ok {
returnnil, nil, "", errors.Errorf("unexpected public key type: %T", parsedKey)
}
// Base64 encode the raw public key (DER format)pubKeyBase64:=base64.StdEncoding.EncodeToString(block.Bytes)
returnpubKey, block.Bytes, pubKeyBase64, nil
}
// Panic if an error occursfuncpanicOnError(errerror) {
iferr!=nil {
panic(err)
}
}
The text was updated successfully, but these errors were encountered:
TL; DR
While enhancing the use of ECDH, ensure compatibility of generated key-pairs between OpenSSL
genpkey
and golang'scrypto/ecdh
.Details
To generate key-pairs of ECDH with X25519 curve in OpenSSL command is as below:
To parse the file in go would be:
But the bytes in
block
are not directly compatible withecdh.PublicKey.Bytes()
.This is due to the format of the decoded bytes from PEM.
The decoded bytes of the contents is typically a DER encoded ASN.1 structure.
Thus, we need to ensure the compatibility of the keys between OpenSSL and
crypto/ecdh
.Here's my two cents to test.
The
main.go
is:The text was updated successfully, but these errors were encountered: