How to calculate and insert signatures in a X509 Certificate - cryptography

I'm trying to perform the fingerprint of a TBSCertificate in order to sign it and insert it in a x509 certificate. I can't find any tool or library that allow me to do it separately.
I can create a x509 certificate and perform the signature like with openssl or many libraries but it will be included directly into the certificate, and I need to modify the signature before including it.
Do you know anyone?

I finally managed to do it using the pyasn1 library for python.
In case someone need it in the future, here you have a conversation in the pyasn1 mailing talking about it:
https://sourceforge.net/p/pyasn1/mailman/message/34523982/
and my personal solution:
from M2Crypto import X509, EC, EVP
from hashlib import sha256
from pyasn1_modules.rfc2314 import Signature
from pyasn1_modules.rfc2459 import Certificate
from pyasn1.codec.der import encoder, decoder
csr = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNyakNDQVpZQ0FRQXdEUVlKS29aSWh2Y05BUUVMQlFBd2dZWXhDekFKQmdOVkJBWVRBa05VTVJJd0VBWUQKVlFRSURBbENZWEpqWld4dmJtRXhFekFSQmdOVkJBY01Da0psYkd4aGRHVnljbUV4RVRBUEJnTlZCQW9NQ0ZCaAplVk5sYm5ObE1Rd3dDZ1lEVlFRTERBTkJRMEV4RERBS0JnTlZCQU1NQTBGRFFURWZNQjBHQ1NxR1NJYjNEUUVKCkFSWVFZV05oUUdSbGFXTXVkV0ZpTG1OaGREQWVGdzB4TlRFd01EZ3dPVFF4TURKYUZ3MHhOakV3TURjd09UUXgKTURKYU1JR0FNUXN3Q1FZRFZRUUdFd0pEVkRFU01CQUdBMVVFQ0F3SlFtRnlZMlZzYjI1aE1STXdFUVlEVlFRSApEQXBDWld4c1lYUmxjbkpoTVF3d0NnWURWUVFLREFOVlFVSXhEVEFMQmdOVkJBc01CRVJGU1VNeEt6QXBCZ05WCkJBTU1JbTF0VjFGVlFsbFpWMVJ2V2xwTmNIYzBjR0ZXVG5NeVRXNXZTbE5oUlZkUmVua3dWakFRQmdjcWhrak8KUFFJQkJnVXJnUVFBQ2dOQ0FBUjVJTWU2aDJwUlpwM201b25VcWloeGwyNkFIQjhMR01WZCtMWUNFTUl4V2U1ZQpySVJmcDA5bHNPUjB3L1B3T3N0bzRXSWFYOXFPSnBTb2JwZFlYRnJaTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCCkFRQXdHZVl3bkM5MmErR1g3VXE4N1k5Z3hXdFVKUWZiUlRwbXdCV1NPdmNlS3Z2Ky9zV3o5VFRqSGRKbElqVnYKbkQ5RHY5aHVlQUVONkE3Sm43SE80ZDhwelJ2d3pQSU5peXN4TUlzaWsxbldpTTRieTBLSDh4bE5SZEFoc3ZZTApVQ3hLSXVGMm1sUytJRGVtMUQyZE96L3ZFZlcrZVE4NVhsSVRycExpMFZlY3d5NnhwejNMN2R2SEdTOFJ1aVlyCkxkS1J3VnVGYmI4QmRHdVJ3dHgwNCtEeGJFKzlUb3hHSmFiM1VaV1loV3ByekcyS2owV0pORW44UlBwWXJjb2sKY3lBSTh4OFh0TDUrZUs5aklpV1NlcHIzcU9Zc3V6V2NqZ2VIeUdCUHNqN1lRQ2ZuWVFTbHBJMzJHRnZ5YWRmTApmRmZXSExxZE9nQmVET2JwenlaVDlyazkKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=="
cert = X509.load_cert_string(b64decode(csr))
ca_pkey = EVP.load_key(ACA_KEY)
asn1_cert = decoder.decode(cert.as_der(), asn1Spec=Certificate())[0]
tbs = asn1_cert.getComponentByName("tbsCertificate")
tbs_der = encoder.encode(tbs)
digest = sha256()
digest.update(tbs_der)
signature = ca_pkey.get_rsa().sign(digest.digest(), "sha256")
# Take the raw signature and turn it into a BitString representations (special thanks to Alex <ralienpp#gmail.com>)
bin_signature = Signature("'%s'H" % ''.join("%02X" % ord(c) for c in signature))
asn1_cert.setComponentByName("signatureValue", bin_signature)
# Check that both certificates matches
cert.sign(ca_pkey, md='sha256')
print cert.as_text()
print encoder.encode(asn1_cert) == cert.as_der()
The initial csr is a base64 encoded X509 Certificate in BER.
Is important to stress the csr already contains the proper Signature Algorithm.

Related

What am I doing wrong in the following code where I'm trying to achieve asymmetric signing and encryption using jose-python?

from jose import jws
from jose import jwe
with open('privkey.pem', mode='r') as privatefile:
privkey = privatefile.read()
with open('pubkey.pem', mode='r') as pubfile:
pubkey = pubfile.read()
####################################
## Signing request with private key
####################################
query = {
"QueryType": 1,
"QuerySubType": None
}
signed = jws.sign(query, privkey, algorithm='RS256')
print(signed)
# verify = jws.verify(signed, pubkey, algorithms=['RS256'])
# print(verify)
################################
## Encryption
################
encrypted = jwe.encrypt(signed, pubkey, algorithm='RSA_OAEP', encryption='A256CBC_HS512')
print(encrypted)
I created rsa key pair of size 2048.
The singing and verification works fine.
I am getting en error "jose.exceptions.JWEError: Algorithm RSA_OAEP not supported". I've tried all the algorithms and encryption, but getting the same error for algorithms not supported for all of them.
What am I doing wrong here?
The key management and the content encryption algorithms are specified wrongly. Use hyphens instead of underscores:
encrypted = jwe.encrypt(signed, pubkey, algorithm='RSA-OAEP', encryption='A256CBC-HS512')
However, it is more robust to apply the provided constants defined in the class jose.constants.Algorithms:
from jose.constants import Algorithms
...
encrypted = jwe.encrypt(signed, pubkey, algorithm=Algorithms.RSA_OAEP, encryption=Algorithms.A256CBC_HS512)

How to create RsaSecurityKey.KeyId with IdentityServer4

I'm using IdentiyServer4 to generate tokens, I'm using the AddDeveloperSigningCredential() method to generate my RSA key with a KeyId.
But, in production, I'm using AddSigningCredential(CreateSigningCredential()), to generate a key like this :
private SigningCredentials CreateSigningCredential()
{
var signinkey = new RsaSecurityKey(RSA.Create());
signinkey.KeyId = "abcdefghijklmnopqrstuvwxyz";//How to generate KeyId ??
var credentials = new SigningCredentials(signinkey,
SecurityAlgorithms.RsaSha256);
return credentials;
}
How can I generate a KeyId? Can I set it to any arbitrary value?
You don't need to set the keyId and also creating the RSA key youself in code, sounds like bad practice. Then you can just as well use the AddDeveloperSigningCredential method.
You can actually look at the source for that method here to see how they do it in code:
https://github.com/DuendeSoftware/IdentityServer/blob/main/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs
But, in production you should generate the key externally and pass it in to IdentityServer, so the key is the same across redeployment/restarts. Otherwise previously issued tokens will not be valid anymore.
You can for example store the key in Azure Key Vault, or using some other configuration/secret system. Or in a database or as a file somewhere.
If you want to create one manually, using OpenSSL, then you can write
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes256 -out rsa-private-key.pem

How to create a crypto:PublicKey record by a string/file in Ballerina

I have public/private key files and I am reading them as strings. So now I want to call some crypto functions using it. For that I need to have a crypto:PublicKey record[1]. I cannot find a way to convert my string to a crypto:PublicKey in the API docs of crypto module [2] or in learn-by-example.[3] How to do this?
[1] https://ballerina.io/learn/api-docs/ballerina/crypto/records/PublicKey.html
[2] https://ballerina.io/learn/api-docs/ballerina/crypto/index.html
[3] https://ballerina.io/learn/by-example/crypto.html
You can use crypto:decodePublicKey API to get the crypto:PublicKey by providing crypto:KeyStore and string key-alias [1].
crypto:KeyStore keyStore = {
path: "/home/ballerina/keystore.p12",
password: "keystorePassword"
};
crypto:PublicKey|crypto:Error publicKey = crypto:decodePublicKey(keyStore, "keyAlias");
NOTE: Ballerina crypto:KeyStore supports only PKCS12 type. Other formats has to be converted to PKCS12 type. PEM file support will be added in future [2].
[1] https://ballerina.io/learn/api-docs/ballerina/crypto/functions.html#decodePublicKey
[2] https://github.com/ballerina-platform/ballerina-lang/issues/13293

how to set chain parameter in X509_STORE_CTX_init?

I am trying to use openssl for certificate chain verification using X509_verify_cert. I am getting all certificates- root certificate, intermediate certificate and child certificate which actually needs to be verified.
Using openssl I am able to convert certificate in X509 format.
X509_STORE_CTX *ctx;
ctx = X509_STORE_CTX_new();
X509_STORE *store = X509_STORE_new();
X509_STORE_CTX_init(ctx, store, certificateX509, ?);
int status = X509_verify_cert(ctx);
while reading documentation about X509_STORE_CTX_init and X509_verify_cert, we need to pass certificate chain at ? in the code. In the documentation, X509_STORE_CTX_init method is defined like this:
int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
X509 *x509, STACK_OF(X509) *chain);
# define STACK_OF(type) struct stack_st_##type
How to pass certificate chain if I have all certificates in X509 format?
Any code snippet may help more.
Thanks in advance!
This seems to work:-
STACK_OF(X509) *chain = sk_X509_new_null();
sk_X509_push(chain, x509);
X509_STORE_CTX_init(x509_ctx, x509_trusted, x509, chain);
sk_X509_pop_free(obj->chain, X509_free);

Verify user identity on my site, using SSL Certificates

I need to register companies on my site for an electronic procurement system. Up to now these were local companies I could meet physically and give credentials to, but now they can be companies based anywhere in the world.
The solution is to have an online registration process whereby they submit a third party certificate. So say Verisign says they are 'Company X' so I register them as Company X and issue them credentials.
How can I implement this on my site? Do I simply give them a field in the registration form where they upload their certificate file? Do I then manually check these certificates in my back office? How does one check this manually? Is there a way to automate this process?
Once they have an account, should I simply request the credentials I issue them with to log in, or can all future logins request the same certificate file? In these a particular format for certificates I can request or should I allow a number of common formats that different certificate vendors provide?
Thanks in advance.
Being able to provide a certificate does unfortunately not prove anything. A certificate is completely public, and anyone can get a hold of the SSL certificate for any website. The certificate contains a public key. Proving ownership of the corresponding private key is what's required.
This is possible to do, but it requires that your users are technical enough to know how to run scripts and/or OpenSSL terminal commands so that they can sign something with their private key. Having the users upload their private key is of course a big no-no, as it means you can now act as the user, and that would require an enormous amount of trust in you to discard the private key after you've verified it.
From a technical perspective, you can do the verification by creating some kind of challenge, for example a random string, and have the user encrypt this string with their private key. If you decrypt this string with the public key in the certificate, and get the original string back, then you know that they have possession of the corresponding private key.
Here's a self-contained Ruby script that demonstrates this, with comments indicating which part of it is run on your side, and which part is run on their side.
require "openssl"
## This happens on the client side. They generate a private key and a certificate.
## This particular certificate is not signed by a CA - it is assumed that a CA
## signature check is already done elsewhere on the user cert.
user_keypair = OpenSSL::PKey::RSA.new(2048)
user_cert = OpenSSL::X509::Certificate.new
user_cert.not_before = Time.now
user_cert.subject = OpenSSL::X509::Name.new([
["C", "NO"],
["ST", "Oslo"],
["L", "Oslo"],
["CN", "August Lilleaas"]
])
user_cert.issuer = user_cert.subject
user_cert.not_after = Time.now + 1000000000 # 40 or so years
user_cert.public_key = user_keypair.public_key
user_cert.sign(user_keypair, OpenSSL::Digest::SHA256.new)
File.open("/tmp/user-cert.crt", "w+") do |f|
f.write user_cert.to_pem
end
## This happens on your side - generate a random phrase, and agree on a digest algorithm
random_phrase = "A small brown fox"
digest = OpenSSL::Digest::SHA256.new
## The client signs (encrypts a cheksum) the random phrase
signature = user_keypair.sign(digest, random_phrase)
## On your side, verify the signature using the user's certificate.
your_user_cert = OpenSSL::X509::Certificate.new(File.new("/tmp/user-cert.crt"))
puts your_user_cert.public_key.verify(digest, signature, random_phrase + "altered")
# => falase
puts your_user_cert.public_key.verify(digest, signature, random_phrase)
# => true
## On your side - attempting to verify with another public key/keypair fails
malicious_keypair = OpenSSL::PKey::RSA.new(2048)
puts malicious_keypair.public_key.verify(digest, signature, random_phrase)
Note that this script does not take into account the CA verification step - you also obviously want to verify that the user's certificate is verified by a CA, such as Verisign that you mentioned, because anyone can issue a certificate and hold a private key for foo.com - it's the CA signature of the certificate that provides authenticity guarantees.