Sign CSR from client using CA root certificate in python - python-2.7

I am new to python and still learning it so my question can be little naive. Please bear with it ;)
The problem is client will be sending CSR and I want to sign it with my CA root certificate and return the signed certificate back to client.
I have been using this command to do it using command line
openssl x509 -req -in device.csr -CA root.pem -CAkey root.key -CAcreateserial -out device.crt -days 500
same thing I want achieve using python. I have come across python library for openssl pyopenssl
is it possible using this library ? How ? or shoudl I go for M2Crypto ?

You can indeed go with pyOpenSSL. As you are saying you already have CA root certificate and a private key, and CSR will be sent by a client then you can use functions of crypto to read all those ( CA cert, private key and Device CSR ) from file or manage to have them in buffer.
Use below functions to start with. Check dir(crypto) and crypto.function_name.__doc__on python interpreter for more info :) You need to import crypto from pyOpenSSL
crypto.load_certificate_request() - to get device CSR obj
crypto.load_privatekey() - to get private key obj for CA private key
crypto.load_certificate() - to get CA root certificate
then you can write simple funcation to return certificate
def create_cert():
cert = crypto.X509()
cert.set_serial_number(serial_no)
cert.gmtime_adj_notBefore(notBeforeVal)
cert.gmtime_adj_notAfter(notAfterVal)
cert.set_issuer(caCert.get_subject())
cert.set_subject(deviceCsr.get_subject())
cert.set_pubkey(deviceCsr.get_pubkey())
cert.sign(CAprivatekey, digest)
return cert
where caCert , deviceCsr and CAprivatekey are values from above three funcations.
Now that you have certificate with you, you can write this to a file using crypto.dump_certificate(crypto.FILETYPE_PEM, cert) with file name of your choice.
You can modify this function as per your requirement. After this you can verify generated device certificate with CA root certificate with openssl command e.g. openssl verify -CApath <CA cert path> <name of device cert file>
You can also go through few examples from github.
M2Crypto Example , pyOpenSSL example
Hope this gives you idea about the implementation

The maintainer of pyOpenSSL recommends to use cryptography module for X509 manipulation (see note on top of the documentation page: https://www.pyopenssl.org/en/stable/api/crypto.html).
Here is the code to create a certificate from a CSR signed by a CA:
def sign_certificate_request(csr_cert, ca_cert, private_ca_key):
cert = x509.CertificateBuilder().subject_name(
csr_cert.subject
).issuer_name(
ca_cert.subject
).public_key(
csr_cert.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.utcnow()
).not_valid_after(
# Our certificate will be valid for 10 days
datetime.utcnow() + timedelta(days=10)
# Sign our certificate with our private key
).sign(private_ca_key, hashes.SHA256())
# return DER certificate
return cert.public_bytes(serialization.Encoding.DER)
csr_cert is the cryptography CSR certificate object - can be loaded from a file with x509.load_der_x509_csr()
ca_cert is the cryptography certificate object - can be loaded from a file with x509.load_pem_x509_certificate()
private_ca_key is the cryptography private key object - can be loaded from a file with serialization.load_pem_private_key()

Related

OpenSSL 3.0.2 PKCS12_parse Failure

We're migrating to Openssl 3.0.2, currently experiencing connection issues between a 3.0.2 server and a 1.1.1g client.
According to the logs collected we seem to be having an issue with the loading of the legacy providers.
We are loading both the default and legacy providers programmatically as per the steps outlined in the Wiki for OpenSSL 3.0 - 6.2 Providers without issue.
We are seeing the following error..
error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto\evp\evp_fetch.c:346:Global default library context, Algorithm (RC2-40-CBC : 0), Properties ()
PKCS12_parse() failed = 183. (Using GetLastError from errhandlingapi.h, the 183 error code is obtained)
Worth mentioning that we are only seeing this issue occur when the server is a Windows 2012 server.
Both default and legacy providers are loaded without issue at start.
As pointed by Liam, OpenSSL 3.0 does not support *by default * legacy algorithm RC2-40-CBC.
Fortunately, the legacy library is included in the bin folder in my distribution (https://slproweb.com/products/Win32OpenSSL.html ; full list https://wiki.openssl.org/index.php/Binaries).
So my steps to resolve were
Set OPENSSL_MODULES
Add -legacy option
Set the variable OPENSSL_MODULES
SET OPENSSL_MODULES=C:\Program Files\OpenSSL-Win64\bin
Without -legacy option:
D:\sources\en.Resilience_Temy\config\certificates>openssl pkcs12 -in server.p12 -out saxserver.crt
Enter Import Password:
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
Error outputting keys and certificates
58630000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto\evp\evp_fetch.c:349:Global default library context, Algorithm (RC2-40-CBC : 0), Properties ()
With -legacy option:
D:\sources\en.Resilience_Temy\config\certificates>openssl pkcs12 -in server.p12 -out server.crt -legacy
Enter Import Password:
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
And file is generated successfully.
This is likely due to RC2-40-CBC more specifically RC2 being disabled by either AD Policy or the OpenSSL library because it is deemed too weak a cipher.
A very simple way to test this:
open wordpad and create a file with hello world in it
run openssl enc -e -rc4-40 -K 1234567890 -in hellowworld.txt -out hellowworld.txt
Assuming this generates an error then this would be stronger evidence that RC2-40 is disabled. From there you can either:
Determine if AD or the OpenSSL library (compile flags) is blocking its use and enable it
Use stronger ciphers in your P12 generation (suggested route)

How to resolve Unable to find valid certification path to requested target for AWS Java Application?

My network is behind ZScaler Proxy. I have installed AWS CLI. I have added all the Amazon Root CA Certificates along with ZScaler CA Root Certificate in a pem file. I have setup AWS_CA_Bundle and my aws cli command for fetching secretsmanager worked.
But when on the same machine, I am trying to fetch SecretManagers using AWS SDK, it gives exception - Unable to find valid certification path to requested target.
Can someone guide me what needs to be done?
Below is the source code
public class AwsSecretManager {
public static AWSSecretManagerPojo getRedshiftCredentialsFromSecretManager(String secretName) throws JsonUtilityException, AwsSecretException {
String secret = getSecret(secretName);
// Gaurav added this.
System.out.println("secret \n" + secret);
if (!StringUtility.isNullOrEmpty(secret)) {
AWSSecretManagerPojo AWSSecretManagerPojo = GsonUtility.getInstance().fromJson(secret, AWSSecretManagerPojo.class,
EdelweissConstant.GSON_TAG);
return AWSSecretManagerPojo;
} else {
throw new AwsSecretException("unable to get redshift credentials from aws secret manager");
}
}
private static String getSecret(String secretName) {
// Gaurav commented below and manually supplied the secret as SSL issue is there.
String region = EdelweissConstant.AWS_SECRET_MANAGER_REGION;
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
String secret = null;
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult = client.getSecretValue(getSecretValueRequest);
if (getSecretValueResult.getSecretString() != null) {
secret = getSecretValueResult.getSecretString();
}
return secret;
}
}
This question is a bit old but other answers to this topic were all programmatic and on a per-application basis which I didn't like, since I wanted this to work for all my applications. To anyone stumbling upon this, these are the steps that I took to fix this issue in my application.
Download the AWS Root CAs from here. CA1 was good enough for me, but you may need others as well. Java requires the .der format one. You can also get the .pem file, but you'll need to convert it to .der. Note that the certificate from Amazon had a .cer extension, but it's still compatible.
Verify that the certificate is legible for the Java keytool:
$ keytool -v -printcert -file AmazonRootCA1.cer
Certificate fingerprints:
SHA1: <...>
SHA256: <...>
Optionally, verify the public key hash if you'd like (outside the scope of this answer, have a look here if you want).
Import the certificate file to your Java keystore. You can do this for your entire JRE by using the keytool command as follows and answering yes when prompted:
keytool -importcert -alias <some_name> -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -file AmazonRootCA1.cer
If you've changed the default Java keystore pass (changeit), you'll obviously need to use that one instead.
After this your application should be able to connect to AWS without any certificate issues.
I removed all the Amazon Certificates and just added ZScaler Root Certiicate. And it resolved the issue.

How to point to/access a cert file `CertCreateCertificateContext` function? (wincrypt api)

A little new to windows programming/C++. I'm trying to install a .p7b root certificate file to the Trusted Root Certificate Store. I want to use the Windows Wincrypt library. Specifically, these are the suggested steps that I got from an old forum:
Call CertCreateCertificateContext using your certificate content bytes
in order to obtain a PCCERT_CONTEXT
Call CertOpenSystemStore with szSubsystemProtocol set to "ROOT" in
order to obtain a HCERTSTORE
Call CertAddCertificateContextToStore using the above HCERTSTORE and
PCCERT_CONTEXT.
[Here's] the api documentation for CertCreateCertificateContext. Not sure how to just point pbCertEncoded to my actual cert file. Should I just point it to the path? Do I have to load the cert in? What should the type be?
From Simon Rozman's answer in this post: We have to use CertOpenStore() instead of
CertCreateCertificateContext(), which supports one certificate only, whereas PKCS #7 file can contain many.
After the certificate store is open, you can use CertEnumCertificatesInStore() to retrieve certificate context of individual certificates from store.
So from my original steps to successfully install a p7b into the root store:
Call CertOpenStore() for the root store and for the actual certificate itself. This will give you two HCERTSTORE handles.
Have a while loop that will add the certificate contexts to the opened root store (using CertAddCertificateContextToStore()) as long as the certificate context exists (check using CertEnumCertificatesInStore() on the opened certificate store).

Unable to import certificate for AWS Data Migration Service

We want to replicate our on-site MySQL database to AWS. Needless to say we need a secure connection, and setting that up has proven to be surprisingly difficult. The AWS Data Migration Service looked like the right thing, and it has the option to import a certificate.
After trial and error I discovered that AWS needed the .pem extension to even try to upload the file. According to the documentation, it expects a chained certificate file, which I have. But I get a validation error every time, with absolutely no helpful details as to what is wrong. I ensured that my private key is only 2048 bits long, as AWS seems to have trouble with larger ones. I have run the certificate through various online tools and they say it is OK. It seems that the first part of the certificate is the problem, Data Migration is happy to import the second part all by itself. Also worth noting, AWS Certificate Manager was willing to take the entire thing (though there it forces you to split the file). The certificate was generated by Let's Encrypt/Zero SSL with my CSR. I have confirmed there are no extra whitespaces in uploading the file.
-----BEGIN CERTIFICATE-----
MIIFBTCCA+2gAwIBAgISA9w/KQIPfQ7G1qbtWy6eruVwMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNzEwMTUxMjU2MzZaFw0x
ODAxMTMxMjU2MzZaMBwxGjAYBgNVBAMTEWVxLnNlbnRyeWxpbmsuY29tMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4GXdQB1u967ZpUuSpaBpzVWFsIXk
YYvDVbX+1DoygaIhqAAoL+s8RgZf8jz49tbFlBc06eXhDH9qL47ZcdLUahY3TY0G
Aksl1uUpxivt7Am3WvzoeTuCO8vhObVNpVLLcyKQ7H543jswLehhSgcPKiSF3ffw
qHJMst4bw4bmzHeTp6ZX83xek8YbXE48PUktpE4sGxwHbQVTuWLDCmMJZr/Pwz6i
fpbkxoUhv4jzlwsAtyPmRIa/XTYhGhnRuPD5m1ZX2LkAuKCH4crYuXPp+F+lMc1R
K0DGoQYk0QjP2nuLmqmJPByHRaTBMb+UwvJn1Ady7qyyS+3nIzG87fzvmwIDAQAB
o4ICETCCAg0wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQFXP07WkX+3XSghpNPYr0W
3Q6LBjAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB
AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw
dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw
dC5vcmcvMBwGA1UdEQQVMBOCEWVxLnNlbnRyeWxpbmsuY29tMIH+BgNVHSAEgfYw
gfMwCAYGZ4EMAQIBMIHmBgsrBgEEAYLfEwEBATCB1jAmBggrBgEFBQcCARYaaHR0
cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwgasGCCsGAQUFBwICMIGeDIGbVGhpcyBD
ZXJ0aWZpY2F0ZSBtYXkgb25seSBiZSByZWxpZWQgdXBvbiBieSBSZWx5aW5nIFBh
cnRpZXMgYW5kIG9ubHkgaW4gYWNjb3JkYW5jZSB3aXRoIHRoZSBDZXJ0aWZpY2F0
ZSBQb2xpY3kgZm91bmQgYXQgaHR0cHM6Ly9sZXRzZW5jcnlwdC5vcmcvcmVwb3Np
dG9yeS8wDQYJKoZIhvcNAQELBQADggEBAFxM5i3VlCpyrK0/Dnw5vtkV5TG+o/fJ
TG8elTQD8NDEuuZUee0u0jdcvP3CSGkRJo7tF1lBih8ns7Dhu2wxouM9r+3nP+7F
CYRF1BRAzj6gsTPpHX1XOv98Nq+s8NXQNFe+WxPlYtUQ4ZoJ+gVcNpm8zQY1GaMA
vb6osuru0WoOA3YeNiuRUSvMFnUMt0hO9DuknUdbbr/i9OphOz6xiWCLFPUtNNos
79yoanGZs9Kt40Ou4yhW1gZLHJfp461r0bzoh848f3+R2fwVaBUGBEYLxPNBCu7U
CK0Iualw5hRhh6620f79Lv2Z2FNrPq5kMIySaLpDWgaj5pQjXAjAVXM=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
UPDATE: The answer, which was to focus on the intermediate certificate, is correct. Using just the intermediate was enough to get a data load completed. However, to get data load with continuing replication, you will indeed have to add in the root certificate from your certificate authority as well. Convert it to PEM format and add that to the end of the intermediate bundle, and give the resulting file to AWS.
Data Migration is happy to import the second part all by itself.
The second part is what DMS needs, not the first.
To assign a certificate to an endpoint, you provide the root certificate or the chain of intermediate CA certificates leading up to the root (as a certificate bundle), that was used to sign the server SSL certificate that is deployed on your endpoint.
https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Security.SSL.html#CHAP_Security.SSL.Limitations
DMS wants to be able to validate the cert on your endpoint, so it only wants the cert of the authority that signed it -- and apparently not the actual endpoint cert itself.
The "validation failed" is probably related to the fact that your first cert is your endpoint cert, which is not a CA cert.
If you check your certs with openssl x509 you note that the first file isn't a CA file...
X509v3 Basic Constraints: critical
CA:FALSE
...but the second one is.
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
I suspect that is all you need.
However, what you have here is an intermediate. If you want to build the full chain, then you need to append the root. According to Let's Encrypt, their Intermediate CA that signed your cert was in turned signed by IdenTrust DST Root CA X3.
This checks -- confirmed out by comparing the Authority Key Identifier of Let's Encrypt Authority X3 to the Subject Key Identifier of DST Root CA X3 (again, using openssl x509).
So, remove your first cert, and upload only the second.
If this isn't sufficient, add the body of the IdentTrust DST Root CA X3 certificate to the end of your file. It's at the URL linked above, and also pasted below:
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

OpenSSL client not sending client certificate

I am struggling with a client certificate problem and hope somebody here can help me. I'm developing a client/server pair using boost asio but I'll try to be unspecific. I'm on windows and using openssl 1.0.1e
Basically, I want to have client authentication by using client certificates. The server shall only accept clients that have a certificate signed by my own CA. So I have setup a self signed CA. This has issued two more certificates. One for the client and one for the server. Both signed by the CA.
I have done that quite a few times now and I am confident that I got it.
My server side also works fine. It requests client certificates and if I'm using s_client and give those certs everything works. Also if I'm using a browser and have my root CA installed as trusted and then import the client certs.
The only thing that I can't get to work is the libssl client. It always fails during the handshake and as far as I can see it will not send the client certficate:
$ openssl.exe s_server -servername localhost -bugs -CAfile myca.crt -cert server.crt
-cert2 server.crt -key private/server.key -key2 private/server.key -accept 8887 -www
-state -Verify 5
verify depth is 5, must return a certificate
Setting secondary ctx parameters
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
SSL3 alert read:warning:no certificate
SSL3 alert write:fatal:handshake failure
SSL_accept:error in SSLv3 read client certificate B
SSL_accept:error in SSLv3 read client certificate B
2675716:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a
certificate:s3_srvr.c:3193:
ACCEPT
I'm using this s_server as debugging tool but against my real server the same thing occurs.
s_client will work fine with the same certificates. Also, if I disable "-Verify" in the server the connection works. So it really seems just the client refusing to send it's certficate. What can be the reason for that?
Since I'm using boost asio as an SSL wrapper the code looks like this:
m_ssl_context.set_verify_mode( asio::ssl::context::verify_peer );
m_ssl_context.load_verify_file( "myca.crt" );
m_ssl_context.use_certificate_file( "testclient.crt", asio::ssl::context::pem );
m_ssl_context.use_private_key_file( "testclient.key", asio::ssl::context::pem );
I have also tried to bypass asio and access the SSL context directly by saying:
SSL_CTX *ctx = m_ssl_context.impl();
SSL *ssl = m_ssl_socket.impl()->ssl;
int res = 0;
res = SSL_CTX_use_certificate_chain_file(ctx, "myca.crt");
if (res <= 0) {
// handle error
}
res = SSL_CTX_use_certificate_file(ctx, "testclient.crt", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
res = SSL_CTX_use_PrivateKey_file(ctx, "testclient.key", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
I can't see any difference in behavior. It should be mentioned that I am using a very old boost 1.43 asio which I cannot update but I suppose all relevant calls go more or less directly to OpenSSL anyway and the server works fine with that version so I think I can rule that out.
If I start forcing client and server to specific versions, the error messages change but it never works and still always works with the s_client test. Currently it is set to TLSv1
If I switch it to TLSv1 for example there is more chatter between client and server and eventually I get the error:
...
SSL_accept:SSLv3 read client key exchange A
<<< TLS 1.0 ChangeCipherSpec [length 0001]
01
<<< TLS 1.0 Handshake [length 0010], Finished
14 00 00 0c f4 71 28 4d ab e3 dd f2 46 e8 8b ed
>>> TLS 1.0 Alert [length 0002], fatal unexpected_message
02 0a
SSL3 alert write:fatal:unexpected_message
SSL_accept:failed in SSLv3 read certificate verify B
2675716:error:140880AE:SSL routines:SSL3_GET_CERT_VERIFY:missing verify
message:s3_srvr.c:2951:
2675716:error:140940E5:SSL routines:SSL3_READ_BYTES:ssl handshake failure:s3_pkt.c:989:
ACCEPT
I have found an older bug entry posted on the openssl mailing list that refereed to this. Apparently a wrong CRLF in the handshake that has been fixed two yrs ago. Or has it?
I have been debugging this for almost a week now and I'm really stuck. Does anyone have a suggestion on what to try? I'm out of ideas...
Cheers,
Stephan
PS: Here is what the above s_server debug out would be with s_client and the same certficate:
$ openssl s_client -CAfile ca.crt -cert testclient.crt -key private/testclient.key -verify 2 -connect myhost:8887
ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
depth=1 C = DE, // further info
verify return:1
depth=0 C = DE, // further info
verify return:1
SSL_accept:SSLv3 read client certificate A
SSL_accept:SSLv3 read client key exchange A
SSL_accept:SSLv3 read certificate verify A
SSL_accept:SSLv3 read finished A
SSL_accept:SSLv3 write session ticket A
SSL_accept:SSLv3 write change cipher spec A
SSL_accept:SSLv3 write finished A
SSL_accept:SSLv3 flush data
ACCEPT
... handshake completes and data is transferred.
All right, after much suffering, the answer has been found by Dave Thompson of OpenSSL.
The reason was that my ssl code called all those functions on the OpenSSL context after the socket object (SSL*) was created from it. Which means all those functions did practically nothing or the wrong thing.
All I had to do was either:
1. Call SSL_use_certificate_file
res = SSL_use_certificate_file(ssl, "testclient.crt", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
res = SSL_use_PrivateKey_file(ssl, "testclient.key", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
(notice the missing CTX)
2. Call the CTX functions
Call the CTX functions upon the context before the socket was created. As asio seemingly encourages to create the context and socket right afterwards (as I did in the initializer list) the calls were all but useless.
The SSL context (in lib OpenSSL or asio alike) encapsulates the SSL usage and each socket created from it will share it's properties.
Thank you guys for your suggestions.
You should not use both SSL_CTX_use_certificate_chain_file() and SSL_CTX_use_certificate_file(), as SSL_CTX_use_certificate_chain_file() tries to load a chain including the client certificate, not just the CA chain. From SSL_CTX_use_certificate(3):
SSL_CTX_use_certificate_chain_file() loads a certificate chain from file into ctx. The certificates must be in PEM format and must be sorted starting with the subject's certificate (actual client or server certificate), followed by intermediate CA certificates if applicable, and ending at the highest level (root) CA.
I think you should be fine using only SSL_CTX_use_certificate_file() and SSL_CTX_use_PrivateKey_file(), as the client does not care much for the CA chain anyway.
I think you need to call SSL_CTX_set_client_CA_list on the server side. This sets a list of certificate authorities to be sent together with the client certificate request.
The client will not send its certificate, even if one was requested, if the certificate does not match that CA list sent by the server.