def _verify_signature_with_pubkey(self, signed_info_c14n, raw_signature, key_value, signature_alg): if "ecdsa-" in signature_alg: ec_key_value = self._find(key_value, "ECKeyValue", namespace="dsig11") named_curve = self._find(ec_key_value, "NamedCurve", namespace="dsig11") public_key = self._find(ec_key_value, "PublicKey", namespace="dsig11") key_data = b64decode(public_key.text)[1:] x = bytes_to_long(key_data[:len(key_data)//2]) y = bytes_to_long(key_data[len(key_data)//2:]) curve_class = self.known_ecdsa_curves[named_curve.get("URI")] key = ec.EllipticCurvePublicNumbers(x=x, y=y, curve=curve_class()).public_key(backend=default_backend()) verifier = key.verifier(raw_signature, ec.ECDSA(self._get_signature_digest_method(signature_alg))) elif "dsa-" in signature_alg: dsa_key_value = self._find(key_value, "DSAKeyValue") p = self._get_long(dsa_key_value, "P") q = self._get_long(dsa_key_value, "Q") g = self._get_long(dsa_key_value, "G", require=False) y = self._get_long(dsa_key_value, "Y") pn = dsa.DSAPublicNumbers(y=y, parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g)) key = pn.public_key(backend=default_backend()) from asn1crypto.algos import DSASignature sig_as_der_seq = DSASignature.from_p1363(raw_signature).dump() verifier = key.verifier(sig_as_der_seq, self._get_signature_digest_method(signature_alg)) elif "rsa-" in signature_alg: rsa_key_value = self._find(key_value, "RSAKeyValue") modulus = self._get_long(rsa_key_value, "Modulus") exponent = self._get_long(rsa_key_value, "Exponent") key = rsa.RSAPublicNumbers(e=exponent, n=modulus).public_key(backend=default_backend()) verifier = key.verifier(raw_signature, padding=PKCS1v15(), algorithm=self._get_signature_digest_method(signature_alg)) else: raise NotImplementedError() verifier.update(signed_info_c14n) verifier.verify()
def encode_dss_signature(r, s): if ( not isinstance(r, six.integer_types) or not isinstance(s, six.integer_types) ): raise ValueError("Both r and s must be integers") return DSASignature({'r': r, 's': s}).dump()
def encode_ecdsa_signature(signature): """ Encode a signature (generated by :meth:`pkcs11.SignMixin.sign`) into DER-encoded ASN.1 (ECDSA_Sig_Value) format. :param bytes signature: signature as bytes :rtype: bytes """ return DSASignature.from_p1363(signature).dump()
def decode_ecdsa_signature(der): """ Decode a DER-encoded ASN.1 (ECDSA_Sig_Value) signature (as generated by OpenSSL/X.509) into PKCS #11 format. :param bytes der: DER-encoded signature :rtype bytes: """ asn1 = DSASignature.load(der) return asn1.to_p1363()
def __init__( self, key: EccPublicKey, meta: EcdsaSignatureMetadata, r: Optional[int] = None, s: Optional[int] = None, der: Optional[ByteString] = None, ) -> None: self.key = key self.meta = meta if not r and not s and der: val = DSASignature.load(der) self.r = val["r"].native self.s = val["s"].native self.der = bytes(der) elif r and s and not der: self.r = r self.s = s self.der = DSASignature({"r": self.r, "s": self.s}).dump() else: raise ValueError("Bad parameters")
def _verify_signature_with_pubkey(self, signed_info_c14n, raw_signature, key_value, signature_alg): if "ecdsa-" in signature_alg: ec_key_value = self._find(key_value, "ECKeyValue", namespace="dsig11") named_curve = self._find(ec_key_value, "NamedCurve", namespace="dsig11") public_key = self._find(ec_key_value, "PublicKey", namespace="dsig11") key_data = b64decode(public_key.text)[1:] x = bytes_to_long(key_data[:len(key_data)//2]) y = bytes_to_long(key_data[len(key_data)//2:]) curve_class = self.known_ecdsa_curves[named_curve.get("URI")] key = ec.EllipticCurvePublicNumbers(x=x, y=y, curve=curve_class()).public_key(backend=default_backend()) key.verify(raw_signature, data=signed_info_c14n, signature_algorithm=ec.ECDSA(self._get_signature_digest_method(signature_alg))) elif "dsa-" in signature_alg: dsa_key_value = self._find(key_value, "DSAKeyValue") p = self._get_long(dsa_key_value, "P") q = self._get_long(dsa_key_value, "Q") g = self._get_long(dsa_key_value, "G", require=False) y = self._get_long(dsa_key_value, "Y") pn = dsa.DSAPublicNumbers(y=y, parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g)) key = pn.public_key(backend=default_backend()) from asn1crypto.algos import DSASignature sig_as_der_seq = DSASignature.from_p1363(raw_signature).dump() key.verify(sig_as_der_seq, data=signed_info_c14n, algorithm=self._get_signature_digest_method(signature_alg)) elif "rsa-" in signature_alg: rsa_key_value = self._find(key_value, "RSAKeyValue") modulus = self._get_long(rsa_key_value, "Modulus") exponent = self._get_long(rsa_key_value, "Exponent") key = rsa.RSAPublicNumbers(e=exponent, n=modulus).public_key(backend=default_backend()) key.verify(raw_signature, data=signed_info_c14n, padding=PKCS1v15(), algorithm=self._get_signature_digest_method(signature_alg)) else: raise NotImplementedError()
def decode_dss_signature(signature): data = DSASignature.load(signature, strict=True).native return data['r'], data['s']
def sign(self, data, key=None, passphrase=None, cert=None, reference_uri=None, key_name=None, key_info=None, id_attribute=None, always_add_key_value=False): """ Sign the data and return the root element of the resulting XML tree. :param data: Data to sign :type data: String, file-like object, or XML ElementTree Element API compatible object :param key: Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string/bytes containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPrivateKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPrivateKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey` object. When signing with a HMAC, this should be a string containing the shared secret. :type key: string, bytes, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPrivateKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPrivateKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey` object :param passphrase: Passphrase to use to decrypt the key, if any. :type passphrase: string :param cert: X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate certificates. :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects :param reference_uri: Custom reference URI or list of reference URIs to incorporate into the signature. When ``method`` is set to ``detached`` or ``enveloped``, reference URIs are set to this value and only the referenced elements are signed. :type reference_uri: string or list :param key_name: Add a KeyName element in the KeyInfo element that may be used by the signer to communicate a key identifier to the recipient. Typically, KeyName contains an identifier related to the key pair used to sign the message. :type key_name: string :param key_info: A custom KeyInfo element to insert in the signature. Use this to supply ``<wsse:SecurityTokenReference>`` or other custom key references. An example value can be found here: https://github.com/XML-Security/signxml/blob/master/test/wsse_keyinfo.xml :type key_info: :py:class:`lxml.etree.Element` :param id_attribute: Name of the attribute whose value ``URI`` refers to. By default, SignXML will search for "Id", then "ID". :type id_attribute: string :param always_add_key_value: Write the key value to the KeyInfo element even if a X509 certificate is present. Use of this parameter is discouraged, as it introduces an ambiguity and a security hazard. The public key used to sign the document is already encoded in the certificate (which is in X509Data), so the verifier must either ignore KeyValue or make sure it matches what's in the certificate. This parameter is provided for compatibility purposes only. :type always_add_key_value: boolean :returns: A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and the payload data. To specify the location of an enveloped signature within **data**, insert a ``<ds:Signature Id="placeholder"></ds:Signature>`` element in **data** (where "ds" is the "http://www.w3.org/2000/09/xmldsig#" namespace). This element will be replaced by the generated signature, and excised when generating the digest. """ if id_attribute is not None: self.id_attributes = (id_attribute, ) if isinstance(cert, (str, bytes)): cert_chain = list(iterate_pem(cert)) else: cert_chain = cert if isinstance(reference_uri, (str, bytes)): reference_uris = [reference_uri] else: reference_uris = reference_uri sig_root, doc_root, c14n_inputs, reference_uris = self._unpack(data, reference_uris) signed_info_element, signature_value_element = self._build_sig(sig_root, reference_uris, c14n_inputs) if key is None: raise InvalidInput('Parameter "key" is required') signed_info_c14n = self._c14n(signed_info_element, algorithm=self.c14n_alg) if self.sign_alg.startswith("hmac-"): from cryptography.hazmat.primitives.hmac import HMAC signer = HMAC(key=key, algorithm=self._get_hmac_digest_method_by_tag(self.sign_alg), backend=default_backend()) signer.update(signed_info_c14n) signature_value_element.text = ensure_str(b64encode(signer.finalize())) sig_root.append(signature_value_element) elif any(self.sign_alg.startswith(i) for i in ["dsa-", "rsa-", "ecdsa-"]): if isinstance(key, (str, bytes)): from cryptography.hazmat.primitives.serialization import load_pem_private_key key = load_pem_private_key(ensure_bytes(key), password=passphrase, backend=default_backend()) hash_alg = self._get_signature_digest_method_by_tag(self.sign_alg) if self.sign_alg.startswith("dsa-"): signature = key.sign(signed_info_c14n, algorithm=hash_alg) elif self.sign_alg.startswith("ecdsa-"): signature = key.sign(signed_info_c14n, signature_algorithm=ec.ECDSA(algorithm=hash_alg)) elif self.sign_alg.startswith("rsa-"): signature = key.sign(signed_info_c14n, padding=PKCS1v15(), algorithm=hash_alg) else: raise NotImplementedError() if self.sign_alg.startswith("dsa-"): # Note: The output of the DSA signer is a DER-encoded ASN.1 sequence of two DER integers. from asn1crypto.algos import DSASignature decoded_signature = DSASignature.load(signature).native r = decoded_signature['r'] s = decoded_signature['s'] signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0") signature_value_element.text = ensure_str(b64encode(signature)) if key_info is None: key_info = SubElement(sig_root, ds_tag("KeyInfo")) if key_name is not None: keyname = SubElement(key_info, ds_tag("KeyName")) keyname.text = key_name if cert_chain is None or always_add_key_value: self._serialize_key_value(key, key_info) if cert_chain is not None: x509_data = SubElement(key_info, ds_tag("X509Data")) for cert in cert_chain: x509_certificate = SubElement(x509_data, ds_tag("X509Certificate")) if isinstance(cert, (str, bytes)): x509_certificate.text = strip_pem_header(cert) else: from OpenSSL.crypto import dump_certificate, FILETYPE_PEM x509_certificate.text = strip_pem_header(dump_certificate(FILETYPE_PEM, cert)) else: sig_root.append(key_info) else: raise NotImplementedError() if self.method == methods.enveloping: for c14n_input in c14n_inputs: doc_root.append(c14n_input) return doc_root if self.method == methods.enveloped else sig_root
def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an ECDSA signature in pure Python (thus slow) :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ has_asn1 = hasattr(certificate_or_public_key, 'asn1') if not has_asn1 or not isinstance(certificate_or_public_key.asn1, (keys.PublicKeyInfo, Certificate)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the oscrypto.asymmetric.PublicKey or oscrypto.asymmetric.Certificate classes, not %s ''', type_name(certificate_or_public_key) )) curve_name = certificate_or_public_key.curve if curve_name not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' certificate_or_public_key does not use one of the named curves secp256r1, secp384r1 or secp521r1 ''' )) if not isinstance(signature, byte_cls): raise TypeError(pretty_message( ''' signature must be a byte string, not %s ''', type_name(signature) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) asn1 = certificate_or_public_key.asn1 if isinstance(asn1, Certificate): asn1 = asn1.public_key curve_base_point = { 'secp256r1': SECP256R1_BASE_POINT, 'secp384r1': SECP384R1_BASE_POINT, 'secp521r1': SECP521R1_BASE_POINT, }[curve_name] x, y = asn1['public_key'].to_coords() n = curve_base_point.order # Validates that the point is valid public_key_point = PrimePoint(curve_base_point.curve, x, y, n) try: signature = DSASignature.load(signature) r = signature['r'].native s = signature['s'].native except (ValueError): raise SignatureError('Signature is invalid') invalid = 0 # Check r is valid invalid |= r < 1 invalid |= r >= n # Check s is valid invalid |= s < 1 invalid |= s >= n if invalid: raise SignatureError('Signature is invalid') hash_func = getattr(hashlib, hash_algorithm) digest = hash_func(data).digest() z = int_from_bytes(digest, signed=False) % n w = inverse_mod(s, n) u1 = (z * w) % n u2 = (r * w) % n hash_point = (curve_base_point * u1) + (public_key_point * u2) if r != (hash_point.x % n): raise SignatureError('Signature is invalid')
def ecdsa_sign(private_key, data, hash_algorithm): """ Generates an ECDSA signature in pure Python (thus slow) :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "sha1", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if not hasattr(private_key, 'asn1') or not isinstance(private_key.asn1, keys.PrivateKeyInfo): raise TypeError(pretty_message( ''' private_key must be an instance of the oscrypto.asymmetric.PrivateKey class, not %s ''', type_name(private_key) )) curve_name = private_key.curve if curve_name not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' private_key does not use one of the named curves secp256r1, secp384r1 or secp521r1 ''' )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) hash_func = getattr(hashlib, hash_algorithm) ec_private_key = private_key.asn1['private_key'].parsed private_key_bytes = ec_private_key['private_key'].contents private_key_int = ec_private_key['private_key'].native curve_num_bytes = CURVE_BYTES[curve_name] curve_base_point = { 'secp256r1': SECP256R1_BASE_POINT, 'secp384r1': SECP384R1_BASE_POINT, 'secp521r1': SECP521R1_BASE_POINT, }[curve_name] n = curve_base_point.order # RFC 6979 section 3.2 # a. digest = hash_func(data).digest() hash_length = len(digest) h = int_from_bytes(digest, signed=False) % n # b. V = b'\x01' * hash_length # c. K = b'\x00' * hash_length # d. K = hmac.new(K, V + b'\x00' + private_key_bytes + digest, hash_func).digest() # e. V = hmac.new(K, V, hash_func).digest() # f. K = hmac.new(K, V + b'\x01' + private_key_bytes + digest, hash_func).digest() # g. V = hmac.new(K, V, hash_func).digest() # h. r = 0 s = 0 while True: # h. 1 T = b'' # h. 2 while len(T) < curve_num_bytes: V = hmac.new(K, V, hash_func).digest() T += V # h. 3 k = int_from_bytes(T[0:curve_num_bytes], signed=False) if k == 0 or k >= n: continue # Calculate the signature in the loop in case we need a new k r = (curve_base_point * k).x % n if r == 0: continue s = (inverse_mod(k, n) * (h + (private_key_int * r) % n)) % n if s == 0: continue break return DSASignature({'r': r, 's': s}).dump()
def sign(self, data, key=None, passphrase=None, cert=None, reference_uri=None, key_name=None, key_info=None, id_attribute=None): """ Sign the data and return the root element of the resulting XML tree. :param data: Data to sign :type data: String, file-like object, or XML ElementTree Element API compatible object :param key: Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object. When signing with a HMAC, this should be a string containing the shared secret. :type key: string, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object :param passphrase: Passphrase to use to decrypt the key, if any. :type passphrase: string :param cert: X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate certificates. :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects :param reference_uri: Custom reference URI or list of reference URIs to incorporate into the signature. When ``method`` is set to ``detached`` or ``enveloped``, reference URIs are set to this value and only the referenced elements are signed. :type reference_uri: string or list :param key_name: Add a KeyName element in the KeyInfo element that may be used by the signer to communicate a key identifier to the recipient. Typically, KeyName contains an identifier related to the key pair used to sign the message. :type key_name: string :param key_info: A custom KeyInfo element to insert in the signature. Use this to supply ``<wsse:SecurityTokenReference>`` or other custom key references. :type key_info: :py:class:`lxml.etree.Element` :param id_attribute: Name of the attribute whose value ``URI`` refers to. By default, SignXML will search for "Id", then "ID". :type id_attribute: string :returns: A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and the payload data. To specify the location of an enveloped signature within **data**, insert a ``<ds:Signature Id="placeholder"></ds:Signature>`` element in **data** (where "ds" is the "http://www.w3.org/2000/09/xmldsig#" namespace). This element will be replaced by the generated signature, and excised when generating the digest. """ if id_attribute is not None: self.id_attributes = (id_attribute, ) if isinstance(cert, (str, bytes)): cert_chain = list(iterate_pem(cert)) else: cert_chain = cert if isinstance(reference_uri, (str, bytes)): reference_uris = [reference_uri] else: reference_uris = reference_uri sig_root, doc_root, c14n_inputs, reference_uris = self._unpack(data, reference_uris) signed_info_element, signature_value_element = self._build_sig(sig_root, reference_uris, c14n_inputs) if key is None: raise InvalidInput('Parameter "key" is required') signed_info_c14n = self._c14n(signed_info_element, algorithm=self.c14n_alg) if self.sign_alg.startswith("hmac-"): from cryptography.hazmat.primitives.hmac import HMAC signer = HMAC(key=key, algorithm=self._get_hmac_digest_method_by_tag(self.sign_alg), backend=default_backend()) signer.update(signed_info_c14n) signature_value_element.text = ensure_str(b64encode(signer.finalize())) sig_root.append(signature_value_element) elif any(self.sign_alg.startswith(i) for i in ["dsa-", "rsa-", "ecdsa-"]): if isinstance(key, (str, bytes)): from cryptography.hazmat.primitives.serialization import load_pem_private_key key = load_pem_private_key(key, password=passphrase, backend=default_backend()) hash_alg = self._get_signature_digest_method_by_tag(self.sign_alg) if self.sign_alg.startswith("dsa-"): signature = key.sign(signed_info_c14n, algorithm=hash_alg) elif self.sign_alg.startswith("ecdsa-"): signature = key.sign(signed_info_c14n, signature_algorithm=ec.ECDSA(algorithm=hash_alg)) elif self.sign_alg.startswith("rsa-"): signature = key.sign(signed_info_c14n, padding=PKCS1v15(), algorithm=hash_alg) else: raise NotImplementedError() if self.sign_alg.startswith("dsa-"): # Note: The output of the DSA signer is a DER-encoded ASN.1 sequence of two DER integers. from asn1crypto.algos import DSASignature decoded_signature = DSASignature.load(signature).native r = decoded_signature['r'] s = decoded_signature['s'] signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0") signature_value_element.text = ensure_str(b64encode(signature)) if key_info is None: key_info = SubElement(sig_root, ds_tag("KeyInfo")) if key_name is not None: keyname = SubElement(key_info, ds_tag("KeyName")) keyname.text = key_name if cert_chain is None: self._serialize_key_value(key, key_info) else: x509_data = SubElement(key_info, ds_tag("X509Data")) for cert in cert_chain: x509_certificate = SubElement(x509_data, ds_tag("X509Certificate")) if isinstance(cert, (str, bytes)): x509_certificate.text = strip_pem_header(cert) else: from OpenSSL.crypto import dump_certificate, FILETYPE_PEM x509_certificate.text = strip_pem_header(dump_certificate(FILETYPE_PEM, cert)) else: sig_root.append(key_info) else: raise NotImplementedError() if self.method == methods.enveloping: for c14n_input in c14n_inputs: doc_root.append(c14n_input) return doc_root if self.method == methods.enveloped else sig_root