Obtain int CRL Number from CRL using Cryptography Library - python

I'm playing around with the cryptography library in Python and I have a CRL object. Here is the setup:
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.x509.oid import ExtensionOID, NameOID, AuthorityInformationAccessOID
# Need to set default backend for cryptography as our version is low:
default_backend = default_backend()
crl_file = path_to/some_crl_file.crl
crl = x509.load_pem_x509_crl(crl_file, default_backend)
print(crl.extensions)
I receive the following output of extensions:
<Extensions([<Extension(oid=<ObjectIdentifier(oid=2.5.29.20, name=cRLNumber)>, critical=False, value=<CRLNumber(17)>)>, <Extension(oid=<ObjectIdentifier(oid=2.5.29.35, name=authorityKeyIdentifier)>, critical=False, value=<AuthorityKeyIdentifier(key_identifier=b"\xe4\xaf+&q\x1a+H'\x85/Rf,\xef\xf0\x89\x13q>", authority_cert_issuer=None, authority_cert_serial_number=None)>)>])>
I then get the CRL Number with the following:
print(crl.extensions.get_extension_for_oid(ExtensionOID.CRL_NUMBER).value)
This outputs <CRLNumber(17)>. How do I get it so that I receive the int value of 17 from this?

I swear someone answered this and I responded, but maybe I dreamed it up.
Anywho. I took another look into this and resolved it with the following:
print(crl.extensions.get_extension_for_oid(ExtensionOID.CRL_NUMBER).value.crl_number)
I needed to add the .crl_number at the end of value.

Related

Why are signatures created with ecdsa Python library not valid with coincurve?

I'm switching from the pure Python ecdsa library to the much faster coincurve library for signing data. I would also like to switch to coincurve for verifying the signatures (including the old signatures created by the ecdsa library).
It appears that signatures created with ecdsa are not (always?) valid in coincurve. Could someone please explain why this is not working? Also, it seems that cryptography library is able to validate both ecdsa signatures and coincurve signatures without issues, consistently.
What is even more confusing, if you run below script a few times, is that sometimes it prints point 3 and other times it does not. Why would coincurve only occasionally find the signature valid?
pip install ecdsa cryptography coincurve
import ecdsa
import hashlib
import coincurve
from coincurve.ecdsa import deserialize_compact, cdata_to_der
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
ecdsa_private_key = ecdsa.SigningKey.generate(ecdsa.SECP256k1, None, hashlib.sha256)
ecdsa_pub = ecdsa_private_key.get_verifying_key()
message = b"Hello world!"
digest = hashlib.sha256(message).digest()
serialized_signature = ecdsa_private_key.sign_digest_deterministic(digest, hashfunc=hashlib.sha256)
signature = cdata_to_der(deserialize_compact(serialized_signature))
cc_private_key = coincurve.PrivateKey(ecdsa_private_key.to_string())
cc_pub = cc_private_key.public_key
crypto_pub = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), cc_pub.format(True))
if ecdsa_pub.verify_digest(serialized_signature, digest) is True:
print("1. ecdsa can validate its own signature")
crypto_pub.verify(signature, digest, ec.ECDSA(Prehashed(hashes.SHA256())))
print("2. cryptography can validate ecdsa signature (raises exception if not valid)")
if cc_pub.verify(signature, digest, None) is False:
print("3. coincurve will not validate ecdsa signature")
signature = cc_private_key.sign(digest, None)
crypto_pub.verify(signature, digest, ec.ECDSA(Prehashed(hashes.SHA256())))
print("4. cryptography can validate coincurve signature (raises exception if not valid)")
if cc_pub.verify(signature, digest, None) is True:
print("5. coincurve will validate its own signature")
Bitcoin and the coincurve library use canonical signatures while this is not true for the ecdsa library.
What does canonical signature mean?
In general, if (r,s) is a valid signature, then (r,s') := (r,-s mod n) is also a valid signature (n is the order of the base point).
A canonical signature uses the value s' = -s mod n = n - s instead of s, i.e. the signature (r, n-s), if s > n/2, s. e.g. here.
All signatures from the ecdsa library that were not been successfully validated by the coincurve library in your test program have an s > n/2 and thus are not canonical, whereas those that were successfully validated are canonical.
So the fix is simply to canonize the signature of the ecdsa library, e.g.:
def canonize(s_bytes):
n = 115792089237316195423570985008687907852837564279074904382605163141518161494337
s = int.from_bytes(s_bytes, byteorder='big')
if s > n//2:
s = n - s
return s.to_bytes(32, byteorder='big')
...
serialized_signature = serialized_signature[:32] + canonize(serialized_signature[32:]) # Fix: canonize
signature = cdata_to_der(deserialize_compact(serialized_signature))
...
With this fix, the coincurve library successfully validates all signatures from the ecdsa library in your test program.

How to create x X.509 certificate without direct access to the private key?

Working with an HSM I can download the public key of a RSA 4096, and can use the HSM to encrypt a blob using the corresponding private key, which I can't download.
I'm trying to find a reference to how I can use the HSM apis, to build the X509 certificate myself. I could find the example below in python, wrapping up openssl libs.
It does mostly all, but
from OpenSSL import crypto, SSL
from socket import gethostname
from pprint import pprint
from time import gmtime, mktime
CERT_FILE = "selfsigned.crt"
KEY_FILE = "private.key"
def create_self_signed_cert():
# create a key pair
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 1024)
# create a self-signed cert
cert = crypto.X509()
cert.get_subject().C = "UK"
cert.get_subject().ST = "London"
cert.get_subject().L = "London"
cert.get_subject().O = "Dummy Company Ltd"
cert.get_subject().OU = "Dummy Company Ltd"
cert.get_subject().CN = gethostname()
cert.set_serial_number(1000)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10*365*24*60*60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(k)
cert.sign(k, 'sha1')
open(CERT_FILE, "wt").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
open(KEY_FILE, "wt").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
create_self_signed_cert()
Mostly what I would expect to be possible is to replace the line
cert.sign(k, 'sha1')
by the corresponding calls to the APIs of my HSM with "an export" of the structure of "cert". Is it possible at all ?
So after one day of research I found a satisfactory answer at Adding a signature to a certificate . While there seems to be some Python examples out how to build the certificate through Python with pyasn1, the most robust seems to be the ones in java using boucycastle libraries. I also found a strong answer how to incorporate it with openssl here How to generate certificate if private key is in HSM? (mostly points 3 and 4), but the approach is far beyond my capacities.
When you want someone else, like your HSM, to sign a certificate for you the standard way to do that is to generate a Certificate Signing Request (CSR) and send the CSR to the signer. The CSR can contain all of the fields that you can set when making a certificate, and those values will be reflected in the resulting certificate.
While you can do this with the OpenSSL library that you are using, see X509Req, their documentation (and I quote) "strongly suggests" that you use the cryptography library instead. (They're maintained by the same group of people.)
In the cryptography library you use the CertificateSigningRequestBuilder to make a CSR. To quote from their docs:
>>> from cryptography import x509
>>> from cryptography.hazmat.primitives import hashes
>>> from cryptography.hazmat.primitives.asymmetric import rsa
>>> from cryptography.x509.oid import AttributeOID, NameOID
>>> private_key = rsa.generate_private_key(
... public_exponent=65537,
... key_size=2048,
... )
>>> builder = x509.CertificateSigningRequestBuilder()
>>> builder = builder.subject_name(x509.Name([
... x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
... ]))
>>> builder = builder.add_extension(
... x509.BasicConstraints(ca=False, path_length=None), critical=True,
... )
>>> builder = builder.add_attribute(
... AttributeOID.CHALLENGE_PASSWORD, b"changeit"
... )
>>> request = builder.sign(
... private_key, hashes.SHA256()
... )
>>> isinstance(request, x509.CertificateSigningRequest)
True
To render out the CSR in the format usually used for sending them around:
>>> from cryptography.hazmat.primitives import serialization
>>> request.public_bytes(serialization.Encoding.PEM)

RSA sign a string with private key in python

I am communicating with our clients server. For an api I need to sign a string with my private key. They have the following condition to follow
User SHA 256 algorithm to calculate the hash of the string
Use the private key and RSA (PKCS1_PADDING) algorithm to sign the Hash Value.
Base64 encode the encrypted Hash Value
and I am doing following
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import base64
pkey = RSA.importKey(keystring)
message = "Hello world"
h = SHA256.new(message.encode())
signature = PKCS1_v1_5.new(pkey).sign(h)
result = base64.b64encode(signature).decode()
Here I am getting a string as result. But on the server side my signature is not matching.
Is there anything I am going wrong with ?? Can anyone help me on this ?
I came back to this question recently and noticed it was never resolved. I don't know what was going wrong with the OPs setup but the following code worked for me.
First, the python code that generates the signature of "Hello world":
from Cryptodome.Signature import PKCS1_v1_5
from Cryptodome.Hash import SHA256
from Cryptodome.PublicKey import RSA
import base64
def sign(message: str, private_key_str: str) -> str:
priv_key = RSA.importKey(private_key_str)
h = SHA256.new(message.encode('utf-8'))
signature = PKCS1_v1_5.new(priv_key).sign(h)
result = base64.b64encode(signature).decode()
return result
And now the Java code that verifies it:
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
...
...
public static boolean verify(String message, String b64Sig, byte[] pubkey_spki) throws GeneralSecurityException {
var pubKey = (PublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pubkey_spki));
var verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(pubKey);
verifier.update(message.getBytes(StandardCharsets.UTF_8));
return verifier.verify(Base64.getDecoder().decode(b64Sig));
}
Perhaps the trickiest part of this is specifying the correct padding scheme in each language/library. These signatures use the scheme identified as RSASSA-PKCS1-v1_5 in the PKCS#1 RFC 8017. On the python side this is accomplished by providing the SHA256 hash object to the PKCS1_v1_5 signature object. In Java it is perhaps a little more straightforward in that you ask for Signature object that implements the RSA algorithm with SHA256 as the hash function, but still have to know that this is RSASSA-PKCS1-v1_5 and not some other possibility in RFC 8017.
I think if one is not already something of an expert then understanding that these magic choices in python and Java produce compatible code is going to be difficult.

Python cryptography fails with "Expected interface of CipherAlgorithm"

I'm trying to use the cryptography python module (cryptography.io) but cannot implement a working example. From example in documentation.
This code:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import openssl
from cryptography.hazmat.backends import default_backend
dbackend = default_backend
iv = 'ababababcdcdcdcd1212121234343434'.encode('hex')
cipher = Cipher(modes.CBC(iv), algorithms.AES('aabbccddaabbccdd1122334411223344'.decode('hex')), backend=dbackend)
e = cipher.encryptor()
ct = e.update("Secret messagexx") + e.finalize()
d = cipher.decryptor()
clear = d.update(ct) + d.finalize()
fails with:
cryptography.exceptions.UnsupportedAlgorithm: Backend object does not implement CipherBackend.
I then try with openssl backend:
obackend = openssl.backend
cipher = Cipher(modes.CBC(iv), algorithms.AES('aabbccddaabbccdd1122334411223344'.decode('hex')), backend=obackend)
And it fails with:
TypeError: Expected interface of CipherAlgorithm.
I've been trying to read the docs but I can't even get the example code to work. Any help appreciated.
Update - solved:
In case someone stumbles over this I add this working example here (where I use ECB mode which was what I actually wanted).
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
backend = default_backend()
cipher = Cipher(algorithms.AES('aabbccddaabbccdd1122334411223344'.decode('hex')), modes.ECB(), backend=backend)
e = cipher.encryptor()
ct = e.update("Secret messagexx") + e.finalize()
d = cipher.decryptor()
clear = d.update(ct) + d.finalize()
print clear
You are passing default_backend as the backend argument, but that's actually a function. Call it with default_backend() and it will return a backend object you can pass in.
The non-hazmat layer does contain a symmetric encryption recipe (known as Fernet), so you may want to consider using that if it meets your needs.

SAML signature verification using Python/M2Crypto

I'm attempting to use M2Crypto to verify a signature contained in an XML response returned from my SSO/SAML provider in my django/python app, but I can't seem to get it to work.
My XML response looks sort of like the second example here.
ETA: And here's a pastebin of my actual XML.
I'm using some code like this to attempt the verification:
def verify_signature(signed_info, cert, signature):
from M2Crypto import EVP, RSA, X509
x509 = X509.load_cert_string(base64.decodestring(cert), X509.FORMAT_DER)
pubkey = x509.get_pubkey().get_rsa()
verify_EVP = EVP.PKey()
verify_EVP.assign_rsa(pubkey)
verify_EVP.reset_context(md='sha1')
verify_EVP.verify_init()
verify_EVP.verify_update(signature.decode('base64'))
result = verify_EVP.verify_final(signed_info)
return result
I can successfully get the NameID from the response, and I know I'm successfully loading the certificate, because I can pull the issuer, etc. out of it.
As for the signature, though, I've tried hashing the passed in XML, encoding/not encoding various pieces, and passing in various bits of XML for the signed_info parameter (the SignedInfo tag, the Response tag, the whole thing), and I've tried using ElementTree/ElementC14N.py to ensure the XML is exclusively canonicalized, as the Transform implies should be done, but I'm not getting a positive result.
What am I missing here? Am I trying to validate against the wrong XML? Something wrong with my verification technique?
You were so close! You should pass to verify_update the signed_info, and then to verify_final pass the signature.
You do need to make sure that your signed_info is correctly canonicalized before verifying the signature.
Here is the correct method:
def verify_signature(signed_info, cert, signature):
from M2Crypto import EVP, RSA, X509
x509 = X509.load_cert_string(base64.decodestring(cert), X509.FORMAT_DER)
pubkey = x509.get_pubkey().get_rsa()
verify_EVP = EVP.PKey()
verify_EVP.assign_rsa(pubkey)
verify_EVP.reset_context(md='sha1')
verify_EVP.verify_init()
verify_EVP.verify_update(signed_info)
result = verify_EVP.verify_final(signature.decode('base64'))
return result
FYI, I was facing the same problem as you, and found no useful software for validating XML signatures in Python, so I wrote a new library: https://github.com/kislyuk/signxml.
from lxml import etree
from signxml import xmldsig
with open("saml2_idp_metadata.xml", "rb") as fh:
cert = etree.parse(fh).find("//ds:X509Certificate").text
root = ElementTree.fromstring(signature_data)
xmldsig(root).verify(x509_cert=cert)

Categories