OpenSSL 3.0.2 PKCS12_parse Failure - c++

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)

Related

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 do I troubleshoot "Encryption type not permitted" error in Kerberos?

Keytab file is created with "Crypto ALL".
The user in AD with which SPN is created has DES, AES128, AES256 encryption algorithms enabled.(user properties -> Accounts).
The domain user with which I login to my application has no encryption type enabled
When I do a klist, the ticket has DES-CBC-MD5 encryption.
In the client machine(from where I access my application), when I execute gpedit.msc, all encryption types are enabled.
I get the following error when authentication fails:
krbAuthenticate: Major Error: Unspecified GSS failure. Minor code may provide more information and Minor Error: Encryption type not permitted
Can you please let me knod if I had missed out any configuration related to encryption types?
Updating krb5.conf file in my server machine to include all the encryption types resolved the problem.

Sign CSR from client using CA root certificate in python

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()

Boost, asio, https, and host/certificate verifcation

I'm looking at Boost's SSL Client. There's a reference to OpenSSL in the comments (sorry, no line numbers):
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
Proper OpenSSL use and verification can be tricky. From experience, I know I have to perform the following to use the library correctly:
Disable SSLv2, SSLv3, and Compression on the Context object
Provide the proper root certificate for chain building and checking
Call SSL_get_peer_certificate and verify the certificate is non-NULL
Call SSL_get_verify_result and verify the result is X509_V_OK
Perform name matching (CN or SAN must match requested host)
OpenSSL 1.1.0 will provide name checking, but its only in HEAD at this point in time. From the OpenSSL Change Log:
Integrate hostname, email address and IP address checking with certificate
verification. New verify options supporting checking in opensl utility.
And:
New functions to check a hostname email or IP address against a
certificate. Add options x509 utility to print results of checks against
a certificate.
I don't see where Boost is performing any of the configurations or checks in the client code.
What precisely is Boost configuring, and what is it checking or verifying in its asio library component when using SSL?
Short answer: The Boost callback function, from the link you cited, doesn't verify anything. It returns whatever preliminary verification result was supplied to it by OpenSSL (via bool preverified). If there is any fine grained verification required (like the CN match, etc.), it has to be done explicitly by the callback.
Long answer: By the time the OpenSSL (or the Boost wrapper for OpenSSL) calls the verification function, in this case, bool verify_certificate(bool preverified, boost::asio::ssl::verify_context& ctx), a set of preliminary (or mandatory) verification is already done by OpenSSL. This is explained in the documentation.
The certificate chain is checked starting with the deepest nesting level (the root CA certificate) and worked upward to the peer's certificate. At each level signatures and issuer attributes are checked. Whenever a verification error is found, the error number is stored in x509_ctx and verify_callback is called with preverify_ok=0. By applying X509_CTX_store_* functions verify_callback can locate the certificate in question and perform additional steps (see EXAMPLES). If no error is found for a certificate, verify_callback is called with preverify_ok=1 before advancing to the next level.
The documentation also cites an example of how a more fine-grained verification callback could be written. You can draw inspiration from that depending on what your needs are.
EDIT: To be sure that Boost's internal callback function doesn't do anything special other than calling the application callback function, I took a look at engine.ipp, the C++ module that invokes OpenSSL's SSL_set_verify to set up callback functions. Take a look at how verify_callback_function is implemented. It simply invokes the application callback.

SSLSniff error: "SSL Accept Failed"

I'm trying to use SSLSniff's tool, and I have some technical issues... I've been looking for any similar problems, but the only results are from Twitter feeds, with no public useful answer. So, here it is:
(My version of SSLSniff is 0.8) I'm launching sslsniff with args:
sslsniff -a -c cert_and_key.pem -s 12345 -w out.log
where: cert_and_key.pem file is my authority's certificate concatenate with my unencrypted private key (in PEM format of course), and 12345 is the port where I redirect traffic with my iptables rule.
So sslsniff is correctly running:
INFO sslsniff : Certificate ready: [...]
[And anytime I connect with a client, there are these 2 following lines:]
DEBUG sslsniff : SSL Accept Failed!
DEBUG sslsniff : Got exception: Error with SSL connection.
On my client' side, I've register my AC as a trusted CA (with FF). Then when I connect through SSL I'm having the error:
Secure Connection Failed.
Error code: ssl_error_bad_cert_domain
What is super strange (moreover the fact that the certificate is not automatically accepted since it should be signed by my trusted CA) is that I cannot accept the forged certificate by clicking on "Add exception..." : I am always returning to the error page asking me to add an(other) exception...
Moreover, when I try to connect to for example: https://www.google.com, SSLSniff's log is completed with a new line :
DEBUG sslsniff : Encoded Length: 7064 too big for session cache, skipping...
Does anyone know what I'm doing wrong?
-- Edit to summer up the different answers --
The problem is that SSLSniff is not taking care of alternive names when it forges certificates. Apparently, Firefox refuses any certificate as soon as the Common Name doesn't match exactly the domain name.
For example, for Google.com : CN = www.google.com and there is no alternative name. So when you connect to https://www.google.com, it's working fine.
But for Google.fr : CN = *.google.fr, with these alternative names: *.google.fr and google.fr. So when you connect to https://www.google.fr, FF is looking for alternative names and, since it obviously doesn't find any, refuses the malformed certificate.
... So a solution would be to patch/commit... I don't know if Moxie Marlinspike has intentionally forgot this functionnality because it was too complicated, or if he was just not aware of this issue. Anyway, I'll try to have a look at the code.
The session encoded length error message: When caching the SSL session fails, it means that SSL session resumption on subsequent connections will fail, resulting in degraded performance, because a full SSL handshake needs to be done on every request. However, despite using the CPU more heavily, sslsniff will still work fine. The caching fails because the serialized representation of the OpenSSL session object (SSL_SESSION) was larger than the maximum size supported by sslsniff's session cache.
As for your real problem, note that sslsniff does not support X.509v3 subjectAltNames, so if you are connecting to a site whose hostname does not match the subject common name of the certificate, but instead matches only a subjectAltName, then sslsniff will generate a forged certificate without subjectAltNames, which will cause a hostname verification mismatch on the connecting client.
If your problem happens only for some specific sites, let us know the site so we can examine the server certificate using e.g. openssl s_client -connect host:port -showcerts and openssl x509 -in servercert.pem -text. If it happens for all sites, then the above is not the explanation.
Try a straight MITM with a cert you fully control , and make sure you don't have some OCSP/Perspectives/Convergance stuff meddling with things. Other than that, maybe add the cert to the OS trusted roots. I think FF on windows uses the windows cert store (start->run->certmgr.msc). It may also be worth trying with something like Burp to see if the error is localized to SSLSniff or all MITM attempts.