// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" "fmt" ) var ( // RFC 3279 OidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} OidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} OidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} // RFC 4055 OidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} OidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} Asn1TagBMPString = 30 ) type sigAlgInfo struct { oid asn1.ObjectIdentifier pubKeyAlgo x509.PublicKeyAlgorithm hash crypto.Hash } var sigAlgInfos = []sigAlgInfo{ // without a digest {OidPublicKeyRSA, x509.RSA, 0}, {OidSignatureRSAPSS, x509.RSA, 0}, {OidPublicKeyDSA, x509.DSA, 0}, {OidPublicKeyECDSA, x509.ECDSA, 0}, // with a digest {oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, {oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, {oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, {oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, {oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, {oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, {oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, {oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, {oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, {oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, {oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, {oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, } // Given a public key and signer options, return the appropriate X.509 digest and signature algorithms func PkixAlgorithms(pub crypto.PublicKey, opts crypto.SignerOpts) (digestAlg, sigAlg pkix.AlgorithmIdentifier, err error) { digestAlg, ok := PkixDigestAlgorithm(opts.HashFunc()) if !ok { err = errors.New("unsupported digest algorithm") return } if pss, ok := opts.(*rsa.PSSOptions); ok { rsapub, ok := pub.(*rsa.PublicKey) if !ok { err = errors.New("RSA-PSS is only valid for RSA keys") return } var params asn1.RawValue params, err = MarshalRSAPSSParameters(rsapub, pss) if err != nil { return } sigAlg = pkix.AlgorithmIdentifier{ Algorithm: OidSignatureRSAPSS, Parameters: params, } return } switch pub.(type) { case *rsa.PublicKey: sigAlg.Algorithm = OidPublicKeyRSA case *ecdsa.PublicKey: sigAlg.Algorithm = OidPublicKeyECDSA default: err = errors.New("unsupported public key algorithm") return } sigAlg.Parameters = asn1.NullRawValue return } // Convert a crypto.Hash to a X.509 AlgorithmIdentifier func PkixDigestAlgorithm(hash crypto.Hash) (alg pkix.AlgorithmIdentifier, ok bool) { if oid, ok2 := HashOids[hash]; ok2 { alg.Algorithm = oid alg.Parameters = asn1.NullRawValue ok = true } return } // Convert a X.509 AlgorithmIdentifier to a crypto.Hash func PkixDigestToHash(alg pkix.AlgorithmIdentifier) (hash crypto.Hash, ok bool) { for hash, oid := range HashOids { if alg.Algorithm.Equal(oid) { return hash, true } } return 0, false } // Convert a X.509 AlgorithmIdentifier to a crypto.Hash func PkixDigestToHashE(alg pkix.AlgorithmIdentifier) (hash crypto.Hash, err error) { hash, ok := PkixDigestToHash(alg) if ok && hash.Available() { return hash, nil } return 0, UnknownDigestError{Algorithm: alg.Algorithm} } // Convert a crypto.PublicKey to a X.509 AlgorithmIdentifier func PkixPublicKeyAlgorithm(pub crypto.PublicKey) (alg pkix.AlgorithmIdentifier, ok bool) { _, alg, err := PkixAlgorithms(pub, nil) return alg, err == nil } // Verify a signature using the algorithm specified by the given X.509 AlgorithmIdentifier func PkixVerify(pub crypto.PublicKey, digestAlg, sigAlg pkix.AlgorithmIdentifier, digest, sig []byte) error { hash, err := PkixDigestToHashE(digestAlg) if err != nil { return err } var info sigAlgInfo for _, a := range sigAlgInfos { if sigAlg.Algorithm.Equal(a.oid) { info = a } } if info.hash != 0 && info.hash != hash { return errors.New("signature type does not match digest type") } switch info.pubKeyAlgo { case x509.RSA: key, ok := pub.(*rsa.PublicKey) if !ok { return errors.New("incorrect key type for signature") } if sigAlg.Algorithm.Equal(OidSignatureRSAPSS) { opts, err := UnmarshalRSAPSSParameters(hash, sigAlg.Parameters) if err != nil { return err } return rsa.VerifyPSS(key, hash, digest, sig, opts) } return rsa.VerifyPKCS1v15(key, hash, digest, sig) case x509.ECDSA: key, ok := pub.(*ecdsa.PublicKey) if !ok { return errors.New("incorrect key type for signature") } esig, err := UnmarshalEcdsaSignature(sig) if err != nil { return err } if !ecdsa.Verify(key, digest, esig.R, esig.S) { return errors.New("ECDSA verification failed") } return nil default: return errors.New("unsupported public key algorithm") } } type UnknownDigestError struct { Algorithm asn1.ObjectIdentifier } func (e UnknownDigestError) Error() string { return fmt.Sprintf("unsupported hash algorithm %s", e.Algorithm) }