diff --git a/core/crypto/bccsp/bccsp_opts.go b/core/crypto/bccsp/bccsp_opts.go index f865a3efc3d..8e8466a672f 100644 --- a/core/crypto/bccsp/bccsp_opts.go +++ b/core/crypto/bccsp/bccsp_opts.go @@ -16,6 +16,12 @@ limitations under the License. package bccsp +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" +) + const ( // ECDSA Elliptic Curve Digital Signature Algorithm (key gen, import, sign, verify), // at default security level (see primitives package). @@ -36,6 +42,9 @@ const ( // SHA Secure Hash Algorithm using default family (see primitives package) SHA = "SHA" + + // X509Certificate Label for X509 certificate realted operation + X509Certificate = "X509Certificate" ) // ECDSAKeyGenOpts contains options for ECDSA key generation. @@ -55,6 +64,64 @@ func (opts *ECDSAKeyGenOpts) Ephemeral() bool { return opts.Temporary } +// ECDSAPKIXPublicKeyImportOpts contains options for ECDSA public key importation in PKIX format +type ECDSAPKIXPublicKeyImportOpts struct { + Temporary bool +} + +// Algorithm returns an identifier for the algorithm to be used +// to generate a key. +func (opts *ECDSAPKIXPublicKeyImportOpts) Algorithm() string { + return ECDSA +} + +// Ephemeral returns true if the key to generate has to be ephemeral, +// false otherwise. +func (opts *ECDSAPKIXPublicKeyImportOpts) Ephemeral() bool { + return opts.Temporary +} + +// ECDSAPrivateKeyImportOpts contains options for ECDSA secret key importation in DER format +// or PKCS#8 format. +type ECDSAPrivateKeyImportOpts struct { + Temporary bool +} + +// Algorithm returns an identifier for the algorithm to be used +// to generate a key. +func (opts *ECDSAPrivateKeyImportOpts) Algorithm() string { + return ECDSA +} + +// Ephemeral returns true if the key to generate has to be ephemeral, +// false otherwise. +func (opts *ECDSAPrivateKeyImportOpts) Ephemeral() bool { + return opts.Temporary +} + +// ECDSAGoPublicKeyImportOpts contains options for ECDSA key importation from ecdsa.PublicKey +type ECDSAGoPublicKeyImportOpts struct { + Temporary bool + PK *ecdsa.PublicKey +} + +// Algorithm returns an identifier for the algorithm to be used +// to generate a key. +func (opts *ECDSAGoPublicKeyImportOpts) Algorithm() string { + return ECDSA +} + +// Ephemeral returns true if the key to generate has to be ephemeral, +// false otherwise. +func (opts *ECDSAGoPublicKeyImportOpts) Ephemeral() bool { + return opts.Temporary +} + +// PublicKey returns the ecdsa.PublicKey to be imported. +func (opts *ECDSAGoPublicKeyImportOpts) PublicKey() *ecdsa.PublicKey { + return opts.PK +} + // ECDSAReRandKeyOpts contains options for ECDSA key re-randomization. type ECDSAReRandKeyOpts struct { Temporary bool @@ -206,3 +273,49 @@ func (opts *RSAKeyGenOpts) Algorithm() string { func (opts *RSAKeyGenOpts) Ephemeral() bool { return opts.Temporary } + +// ECDSAGoPublicKeyImportOpts contains options for RSA key importation from rsa.PublicKey +type RSAGoPublicKeyImportOpts struct { + Temporary bool + PK *rsa.PublicKey +} + +// Algorithm returns an identifier for the algorithm to be used +// to generate a key. +func (opts *RSAGoPublicKeyImportOpts) Algorithm() string { + return RSA +} + +// Ephemeral returns true if the key to generate has to be ephemeral, +// false otherwise. +func (opts *RSAGoPublicKeyImportOpts) Ephemeral() bool { + return opts.Temporary +} + +// PublicKey returns the ecdsa.PublicKey to be imported. +func (opts *RSAGoPublicKeyImportOpts) PublicKey() *rsa.PublicKey { + return opts.PK +} + +// X509PublicKeyImportOpts contains options for importing public keys from an x509 certificate +type X509PublicKeyImportOpts struct { + Temporary bool + Cert *x509.Certificate +} + +// Algorithm returns an identifier for the algorithm to be used +// to generate a key. +func (opts *X509PublicKeyImportOpts) Algorithm() string { + return X509Certificate +} + +// Ephemeral returns true if the key to generate has to be ephemeral, +// false otherwise. +func (opts *X509PublicKeyImportOpts) Ephemeral() bool { + return opts.Temporary +} + +// PublicKey returns the ecdsa.PublicKey to be imported. +func (opts *X509PublicKeyImportOpts) Certificate() *x509.Certificate { + return opts.Cert +} diff --git a/core/crypto/bccsp/sw/ecdsakey.go b/core/crypto/bccsp/sw/ecdsakey.go index 7ad900c66ab..f5cb5f0a528 100644 --- a/core/crypto/bccsp/sw/ecdsakey.go +++ b/core/crypto/bccsp/sw/ecdsakey.go @@ -28,7 +28,7 @@ type ecdsaPrivateKey struct { k *ecdsa.PrivateKey } -// ToByte converts this key to its byte representation, +// Bytes converts this key to its byte representation, // if this operation is allowed. func (k *ecdsaPrivateKey) Bytes() (raw []byte, err error) { return @@ -64,7 +64,7 @@ type ecdsaPublicKey struct { k *ecdsa.PublicKey } -// ToByte converts this key to its byte representation, +// Bytes converts this key to its byte representation, // if this operation is allowed. func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) { raw, err = x509.MarshalPKIXPublicKey(k.k) diff --git a/core/crypto/bccsp/sw/impl.go b/core/crypto/bccsp/sw/impl.go index 729c01fe9e8..0b3f9dedf5c 100644 --- a/core/crypto/bccsp/sw/impl.go +++ b/core/crypto/bccsp/sw/impl.go @@ -256,18 +256,16 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e // The opts argument should be appropriate for the primitive used. func (csp *impl) KeyImport(raw []byte, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { // Validate arguments - if len(raw) == 0 { - return nil, errors.New("Invalid raw. Zero length.") - } if opts == nil { return nil, errors.New("Invalid Opts parameter. It must not be nil.") } switch opts.(type) { + case *bccsp.AES256ImportKeyOpts: if len(raw) != 32 { - return nil, fmt.Errorf("Invalid Key Length [%d]. Must be 32 bytes", len(raw)) + return nil, fmt.Errorf("[AES256ImportKeyOpts] Invalid Key Length [%d]. Must be 32 bytes", len(raw)) } aesK := &aesPrivateKey{utils.Clone(raw), false} @@ -282,8 +280,13 @@ func (csp *impl) KeyImport(raw []byte, opts bccsp.KeyImportOpts) (k bccsp.Key, e } return aesK, nil + case *bccsp.HMACImportKeyOpts: + if len(raw) == 0 { + return nil, errors.New("[HMACImportKeyOpts] Invalid raw. It must not be nil.") + } + aesK := &aesPrivateKey{utils.Clone(raw), false} // If the key is not Ephemeral, store it. @@ -296,6 +299,123 @@ func (csp *impl) KeyImport(raw []byte, opts bccsp.KeyImportOpts) (k bccsp.Key, e } return aesK, nil + + case *bccsp.ECDSAPKIXPublicKeyImportOpts: + + if len(raw) == 0 { + return nil, errors.New("[ECDSAPKIXPublicKeyImportOpts] Invalid raw. It must not be nil.") + } + + lowLevelKey, err := primitives.DERToPublicKey(raw) + if err != nil { + return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err) + } + + ecdsaPK, ok := lowLevelKey.(*ecdsa.PublicKey) + if !ok { + return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.") + } + + k = &ecdsaPublicKey{ecdsaPK} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.ECDSAPrivateKeyImportOpts: + + if len(raw) == 0 { + return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw. It must not be nil.") + } + + lowLevelKey, err := primitives.DERToPrivateKey(raw) + if err != nil { + return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err) + } + + ecdsaSK, ok := lowLevelKey.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.") + } + + k = &ecdsaPrivateKey{ecdsaSK} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.ECDSAGoPublicKeyImportOpts: + + lowLevelKey := opts.(*bccsp.ECDSAGoPublicKeyImportOpts).PublicKey() + if lowLevelKey == nil { + return nil, errors.New("Invalid Opts. ECDSA Public key cannot be nil") + } + + k = &ecdsaPublicKey{lowLevelKey} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.RSAGoPublicKeyImportOpts: + + lowLevelKey := opts.(*bccsp.RSAGoPublicKeyImportOpts).PublicKey() + if lowLevelKey == nil { + return nil, errors.New("Invalid Opts. ECDSA Public key cannot be nil") + } + + k = &rsaPublicKey{lowLevelKey} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.X509PublicKeyImportOpts: + + x509Cert := opts.(*bccsp.X509PublicKeyImportOpts).Certificate() + if x509Cert == nil { + return nil, errors.New("Invalid Opts. X509 certificate cannot be nil") + } + + pk := x509Cert.PublicKey + + switch pk.(type) { + case *ecdsa.PublicKey: + return csp.KeyImport(nil, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral(), PK: pk.(*ecdsa.PublicKey)}) + case *rsa.PublicKey: + return csp.KeyImport(nil, &bccsp.RSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral(), PK: pk.(*rsa.PublicKey)}) + default: + return nil, errors.New("Certificate public key type not recognized. Supported keys: [ECDSA, RSA]") + } + default: return nil, errors.New("Import Key Options not recognized") } @@ -407,9 +527,17 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer } return ecdsa.Verify(&(k.(*ecdsaPrivateKey).k.PublicKey), digest, ecdsaSignature.R, ecdsaSignature.S), nil + case *ecdsaPublicKey: + ecdsaSignature := new(primitives.ECDSASignature) + _, err := asn1.Unmarshal(signature, ecdsaSignature) + if err != nil { + return false, fmt.Errorf("Failed unmashalling signature [%s]", err) + } + + return ecdsa.Verify(k.(*ecdsaPublicKey).k, digest, ecdsaSignature.R, ecdsaSignature.S), nil case *rsaPrivateKey: if opts == nil { - return false, errors.New("Invalid options. Nil.") + return false, errors.New("Invalid options. It must not be nil.") } switch opts.(type) { case *rsa.PSSOptions: @@ -417,6 +545,20 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer (opts.(*rsa.PSSOptions)).Hash, digest, signature, opts.(*rsa.PSSOptions)) + return err == nil, err + default: + return false, fmt.Errorf("Opts type not recognized [%s]", opts) + } + case *rsaPublicKey: + if opts == nil { + return false, errors.New("Invalid options. It must not be nil.") + } + switch opts.(type) { + case *rsa.PSSOptions: + err := rsa.VerifyPSS(k.(*rsaPublicKey).k, + (opts.(*rsa.PSSOptions)).Hash, + digest, signature, opts.(*rsa.PSSOptions)) + return err == nil, err default: return false, fmt.Errorf("Opts type not recognized [%s]", opts) diff --git a/core/crypto/bccsp/sw/impl_test.go b/core/crypto/bccsp/sw/impl_test.go index 3f5bd01c2bb..317ea50bd3d 100644 --- a/core/crypto/bccsp/sw/impl_test.go +++ b/core/crypto/bccsp/sw/impl_test.go @@ -23,7 +23,18 @@ import ( "crypto" "crypto/rsa" + "crypto/ecdsa" + + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "math/big" + "net" + "time" + "github.com/hyperledger/fabric/core/crypto/bccsp" + "github.com/hyperledger/fabric/core/crypto/bccsp/signer" "github.com/hyperledger/fabric/core/crypto/primitives" "github.com/spf13/viper" ) @@ -299,6 +310,303 @@ func TestECDSAKeyDeriv(t *testing.T) { } } +func TestECDSAKeyImportFromExportedKey(t *testing.T) { + csp := getBCCSP(t) + + // Generate an ECDSA key + k, err := csp.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting ECDSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting ECDSA raw public key [%s]", err) + } + + // Import the exported public key + pk2, err := csp.KeyImport(pkRaw, &bccsp.ECDSAPKIXPublicKeyImportOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := csp.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestECDSAKeyImportFromECDSAPublicKey(t *testing.T) { + csp := getBCCSP(t) + + // Generate an ECDSA key + k, err := csp.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting ECDSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting ECDSA raw public key [%s]", err) + } + + pub, err := primitives.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to ecdsa.PublicKey [%s]", err) + } + + // Import the ecdsa.PublicKey + pk2, err := csp.KeyImport(pkRaw, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: true, PK: pub.(*ecdsa.PublicKey)}) + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := csp.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestECDSAKeyImportFromECDSAPrivateKey(t *testing.T) { + csp := getBCCSP(t) + + // Generate an ECDSA key + key, err := primitives.NewECDSAKey() + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Import the ecdsa.PrivateKey + priv, err := primitives.PrivateKeyToDER(key) + if err != nil { + t.Fatalf("Failed converting raw to ecdsa.PrivateKey [%s]", err) + } + + sk, err := csp.KeyImport(priv, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed importing ECDSA private key [%s]", err) + } + if sk == nil { + t.Fatal("Failed importing ECDSA private key. Return BCCSP key cannot be nil.") + } + + // Import the ecdsa.PublicKey + pub, err := primitives.PublicKeyToDER(&key.PublicKey) + if err != nil { + t.Fatalf("Failed converting raw to ecdsa.PublicKey [%s]", err) + } + + pk, err := csp.KeyImport(pub, &bccsp.ECDSAPKIXPublicKeyImportOpts{Temporary: true}) + + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(sk, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := csp.Verify(pk, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestKeyImportFromX509ECDSAPublicKey(t *testing.T) { + csp := getBCCSP(t) + + // Generate an ECDSA key + k, err := csp.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Generate a self-signed certificate + testExtKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth} + testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}} + extraExtensionData := []byte("extra extension") + commonName := "test.example.com" + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"Σ Acme Co"}, + Country: []string{"US"}, + ExtraNames: []pkix.AttributeTypeAndValue{ + { + Type: []int{2, 5, 4, 42}, + Value: "Gopher", + }, + // This should override the Country, above. + { + Type: []int{2, 5, 4, 6}, + Value: "NL", + }, + }, + }, + NotBefore: time.Now().Add(-1 * time.Hour), + NotAfter: time.Now().Add(1 * time.Hour), + + SignatureAlgorithm: x509.ECDSAWithSHA256, + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageCertSign, + + ExtKeyUsage: testExtKeyUsage, + UnknownExtKeyUsage: testUnknownExtKeyUsage, + + BasicConstraintsValid: true, + IsCA: true, + + OCSPServer: []string{"http://ocsp.example.com"}, + IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"}, + + DNSNames: []string{"test.example.com"}, + EmailAddresses: []string{"gopher@golang.org"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, + + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PermittedDNSDomains: []string{".example.com", "example.com"}, + + CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"}, + + ExtraExtensions: []pkix.Extension{ + { + Id: []int{1, 2, 3, 4}, + Value: extraExtensionData, + }, + }, + } + + signer := &signer.CryptoSigner{} + err = signer.Init(csp, k) + if err != nil { + t.Fatalf("Failed initializing CyrptoSigner [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting ECDSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting ECDSA raw public key [%s]", err) + } + + pub, err := primitives.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to ECDSA.PublicKey [%s]", err) + } + + certRaw, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, signer) + if err != nil { + t.Fatalf("Failed generating self-signed certificate [%s]", err) + } + + cert, err := primitives.DERToX509Certificate(certRaw) + if err != nil { + t.Fatalf("Failed generating X509 certificate object from raw [%s]", err) + } + + // Import the certificate's public key + pk2, err := csp.KeyImport(nil, &bccsp.X509PublicKeyImportOpts{Temporary: true, Cert: cert}) + + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := csp.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + func TestAESKeyGen(t *testing.T) { csp := getBCCSP(t) @@ -751,3 +1059,189 @@ func TestRSAVerify(t *testing.T) { t.Fatal("Failed verifying RSA signature. Signature not valid.") } } + +func TestRSAKeyImportFromRSAPublicKey(t *testing.T) { + csp := getBCCSP(t) + + // Generate an RSA key + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting RSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting RSA raw public key [%s]", err) + } + + pub, err := primitives.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to RSA.PublicKey [%s]", err) + } + + // Import the RSA.PublicKey + pk2, err := csp.KeyImport(pkRaw, &bccsp.RSAGoPublicKeyImportOpts{Temporary: true, PK: pub.(*rsa.PublicKey)}) + if err != nil { + t.Fatalf("Failed importing RSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing RSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + + valid, err := csp.Verify(pk2, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } +} + +func TestKeyImportFromX509RSAPublicKey(t *testing.T) { + csp := getBCCSP(t) + + // Generate an RSA key + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + // Generate a self-signed certificate + testExtKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth} + testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}} + extraExtensionData := []byte("extra extension") + commonName := "test.example.com" + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"Σ Acme Co"}, + Country: []string{"US"}, + ExtraNames: []pkix.AttributeTypeAndValue{ + { + Type: []int{2, 5, 4, 42}, + Value: "Gopher", + }, + // This should override the Country, above. + { + Type: []int{2, 5, 4, 6}, + Value: "NL", + }, + }, + }, + NotBefore: time.Now().Add(-1 * time.Hour), + NotAfter: time.Now().Add(1 * time.Hour), + + SignatureAlgorithm: x509.SHA256WithRSA, + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageCertSign, + + ExtKeyUsage: testExtKeyUsage, + UnknownExtKeyUsage: testUnknownExtKeyUsage, + + BasicConstraintsValid: true, + IsCA: true, + + OCSPServer: []string{"http://ocsp.example.com"}, + IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"}, + + DNSNames: []string{"test.example.com"}, + EmailAddresses: []string{"gopher@golang.org"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, + + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PermittedDNSDomains: []string{".example.com", "example.com"}, + + CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"}, + + ExtraExtensions: []pkix.Extension{ + { + Id: []int{1, 2, 3, 4}, + Value: extraExtensionData, + }, + }, + } + + signer := &signer.CryptoSigner{} + err = signer.Init(csp, k) + if err != nil { + t.Fatalf("Failed initializing CyrptoSigner [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting RSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting RSA raw public key [%s]", err) + } + + pub, err := primitives.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to RSA.PublicKey [%s]", err) + } + + certRaw, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, signer) + if err != nil { + t.Fatalf("Failed generating self-signed certificate [%s]", err) + } + + cert, err := primitives.DERToX509Certificate(certRaw) + if err != nil { + t.Fatalf("Failed generating X509 certificate object from raw [%s]", err) + } + + // Import the certificate's public key + pk2, err := csp.KeyImport(nil, &bccsp.X509PublicKeyImportOpts{Temporary: true, Cert: cert}) + + if err != nil { + t.Fatalf("Failed importing RSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing RSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + + valid, err := csp.Verify(pk2, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } +} diff --git a/core/crypto/primitives/keys.go b/core/crypto/primitives/keys.go index 2fe2c2159de..a971b2d5eb2 100644 --- a/core/crypto/primitives/keys.go +++ b/core/crypto/primitives/keys.go @@ -222,7 +222,7 @@ func DERToPublicKey(derBytes []byte) (pub interface{}, err error) { } */ -// PublicKeyToPEM marshals a public key to the pem forma +// PublicKeyToPEM marshals a public key to the pem format func PublicKeyToPEM(publicKey interface{}, pwd []byte) ([]byte, error) { if len(pwd) != 0 { return PublicKeyToEncryptedPEM(publicKey, pwd) @@ -247,6 +247,22 @@ func PublicKeyToPEM(publicKey interface{}, pwd []byte) ([]byte, error) { } } +// PublicKeyToDER marshals a public key to the der format +func PublicKeyToDER(publicKey interface{}) ([]byte, error) { + switch x := publicKey.(type) { + case *ecdsa.PublicKey: + PubASN1, err := x509.MarshalPKIXPublicKey(x) + if err != nil { + return nil, err + } + + return PubASN1, nil + + default: + return nil, utils.ErrInvalidKey + } +} + // PublicKeyToEncryptedPEM converts a public key to encrypted pem func PublicKeyToEncryptedPEM(publicKey interface{}, pwd []byte) ([]byte, error) { switch x := publicKey.(type) {