Initial QSfera import
This commit is contained in:
+116
@@ -0,0 +1,116 @@
|
||||
package xmlenc
|
||||
|
||||
import (
|
||||
|
||||
// nolint: gas
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
)
|
||||
|
||||
// ErrAlgorithmNotImplemented is returned when encryption used is not
|
||||
// supported.
|
||||
type ErrAlgorithmNotImplemented string
|
||||
|
||||
func (e ErrAlgorithmNotImplemented) Error() string {
|
||||
return "algorithm is not implemented: " + string(e)
|
||||
}
|
||||
|
||||
// ErrCannotFindRequiredElement is returned by Decrypt when a required
|
||||
// element cannot be found.
|
||||
type ErrCannotFindRequiredElement string
|
||||
|
||||
func (e ErrCannotFindRequiredElement) Error() string {
|
||||
return "cannot find required element: " + string(e)
|
||||
}
|
||||
|
||||
// ErrIncorrectTag is returned when Decrypt is passed an element which
|
||||
// is neither an EncryptedType nor an EncryptedKey
|
||||
var ErrIncorrectTag = fmt.Errorf("tag must be an EncryptedType or EncryptedKey")
|
||||
|
||||
// ErrIncorrectKeyLength is returned when the fixed length key is not
|
||||
// of the required length.
|
||||
type ErrIncorrectKeyLength int
|
||||
|
||||
func (e ErrIncorrectKeyLength) Error() string {
|
||||
return fmt.Sprintf("expected key to be %d bytes", int(e))
|
||||
}
|
||||
|
||||
// ErrIncorrectKeyType is returned when the key is not the correct type
|
||||
type ErrIncorrectKeyType string
|
||||
|
||||
func (e ErrIncorrectKeyType) Error() string {
|
||||
return fmt.Sprintf("expected key to be %s", string(e))
|
||||
}
|
||||
|
||||
// Decrypt decrypts the encrypted data using the provided key. If the
|
||||
// data are encrypted using AES or 3DEC, then the key should be a []byte.
|
||||
// If the data are encrypted with PKCS1v15 or RSA-OAEP-MGF1P then key should
|
||||
// be a *rsa.PrivateKey.
|
||||
func Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) {
|
||||
encryptionMethodEl := ciphertextEl.FindElement("./EncryptionMethod")
|
||||
if encryptionMethodEl == nil {
|
||||
return nil, ErrCannotFindRequiredElement("EncryptionMethod")
|
||||
}
|
||||
algorithm := encryptionMethodEl.SelectAttrValue("Algorithm", "")
|
||||
decrypter, ok := decrypters[algorithm]
|
||||
if !ok {
|
||||
return nil, ErrAlgorithmNotImplemented(algorithm)
|
||||
}
|
||||
return decrypter.Decrypt(key, ciphertextEl)
|
||||
}
|
||||
|
||||
func getCiphertext(encryptedKey *etree.Element) ([]byte, error) {
|
||||
ciphertextEl := encryptedKey.FindElement("./CipherData/CipherValue")
|
||||
if ciphertextEl == nil {
|
||||
return nil, fmt.Errorf("cannot find CipherData element containing a CipherValue element")
|
||||
}
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(strings.TrimSpace(ciphertextEl.Text()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func validateRSAKeyIfPresent(key interface{}, encryptedKey *etree.Element) (*rsa.PrivateKey, error) {
|
||||
rsaKey, ok := key.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("expected key to be a *rsa.PrivateKey")
|
||||
}
|
||||
|
||||
// extract and verify that the public key matches the certificate
|
||||
// this section is included to either let the service know up front
|
||||
// if the key will work, or let the service provider know which key
|
||||
// to use to decrypt the message. Either way, verification is not
|
||||
// security-critical.
|
||||
//nolint:revive,staticcheck // Keep the later empty branch so that we know to address this at a later date.
|
||||
if el := encryptedKey.FindElement("./KeyInfo/X509Data/X509Certificate"); el != nil {
|
||||
certPEMbuf := el.Text()
|
||||
certPEMbuf = "-----BEGIN CERTIFICATE-----\n" + certPEMbuf + "\n-----END CERTIFICATE-----\n"
|
||||
certPEM, _ := pem.Decode([]byte(certPEMbuf))
|
||||
if certPEM == nil {
|
||||
return nil, fmt.Errorf("invalid certificate")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certPEM.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected certificate to be an *rsa.PublicKey")
|
||||
}
|
||||
if rsaKey.N.Cmp(pubKey.N) != 0 || rsaKey.E != pubKey.E {
|
||||
return nil, fmt.Errorf("certificate does not match provided key")
|
||||
}
|
||||
} else if el = encryptedKey.FindElement("./KeyInfo/X509Data/X509IssuerSerial"); el != nil {
|
||||
// TODO: determine how to validate the issuer serial information
|
||||
}
|
||||
return rsaKey, nil
|
||||
}
|
||||
Reference in New Issue
Block a user