I am attempting to retrieve the subject alternative name from my client certificate. By running this command, I can see the SAN:
openssl x509 -noout -text -in certname.cert.pem
...
X509v3 Subject Alternative Name:
IP Address:10.10.10.10
In a C file, I am trying to retrieve the client SAN, so that I can validate it with the server IP. Here is my attempt:
cert = X509_STORE_CTX_get_current_cert(x509Ctx);
int i;
int san_names_nb = -1;
STACK_OF(GENERAL_NAME) *san_names = NULL;
// Try to extract the names within the SAN extension from the certificate
san_names = (GENERAL_NAME*)X509_get_ext_d2i((X509 *) cert, NID_subject_alt_name, NULL, NULL);
if (san_names == NULL)
{
return Error;
}
Right now, my code is returning the error because san_names is NULL. Any guidance would be much appreciated. Thank you!
The OpenSSL command itself gives the SAN as Null
X509v3 Subject Alternative Name: **<BLANK>**
IP Address:10.10.10.10
Can you just open the certificate and see if it contains the SAN. If not you will have to ask the team to add the SAN and create a new certificate again.
You are misusing X509_get_ext_d2i(). Per the OpenSSL documentation for X509_get_ext_d2i() (bolding mine):
If idx is NULL then only one occurrence of an extension is permissible otherwise the first extension after index *idx is returned and *idx updated to the location of the extension.
Depending on the version of OpenSSL you're using, the behavior might be slightly different. The above is the documented behavior for OpenSSL 1.1.0.
Since you are passing NULL as idx, if there's more than one SAN on the cert you'll get NULL from X509_get_ext_d2i().
You can get the OpenSSL error code with ERR_get_error().
Related
I'm hoping someone can help with my OpenSSL(1.1.1k) issue I'm having when trying to validate a certificate on the client against a server. The certificate contents are specified in text and not in a .PEM file on the client side. Some sample code is here:
bool setCert(SSL_CTX* ctx, LPCSTR cert)
{
//cert is the text of the certificate e.g. -----BEGIN CERTIFICATE----- blah -----END CERTIFICATE-----
bool bOk = true;
if (!cert.empty()) // no cert on disk
{
BIO *bioCert = BIO_new_mem_buf((void*)cert, -1);
X509* pCert = PEM_read_bio_X509(bioCert, NULL, 0, NULL);
if (SSL_CTX_use_certificate(ctx, pCert) != 1)
{
//log errror, "Invalid certificate"
bOk = false;
}
BIO_free_all(bioCert);
X509_free(pCert);
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); // other options not used atm SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
return bOk;
}
After this method returns, I create an SSL object based on the context, with SSL_new(ctx).
I then call SSL_Connect(ssl)
The handshake occurs and is accepted even if I pass in an invalid certificate.
My assumption here is that it's doing this because I do not set the verify method like:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
My understanding is that I cannot use SSL_VERIFY_PEER unless there is a cert / .PEM file on disk on the client side to validate against.
How can I validate the handshake when keeping the certificate contents in memory?
If I change the code to use a PEM file, call SSL_CTX_use_certificate_file instead of SSL_CTX_use_certificate, it works. In that case I did set SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
Any help here is greatly appreciated. Thanks
SSL_CTX_use_certificate is used for setting up the certificate which should be used locally to authenticate against the peer. It needs to be accompanied with a private key, setup with SSL_CTX_use_PrivateKey. But authenticating against a peer, i.e. using a client certificate, is not what you intend to do in your client.
Instead you want to setup a certificate for use as a trusted CA when validating the server certificate. The correct function for this would be X509_STORE_add_cert instead:
X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), pCert);
I have a certificate in PEM format. Let say I have a certificate that I copied from google.com.
So, the chain is that
Google Trust Services-GlobalSign Root CA-R2
->Google Internet Authority G3
-->*.google.com
Suppose that I have certificate *.google.com and I want a C program to verify this certificate with my local trust store in Linux. Let say /etc/ssl/certs.
I need to it offline without connecting to the server. What should I do?
The overall OpenSSL documentation around this topic is rather limited and has broken links all over the place, so my approach might not the only one or best one. As far as I can see, verifying a certificate (chain) is done with the following steps, unrolled in reverse order because I think that gives a better understanding. For the resulting code, see the end of this answer. All code has error checking omitted for the sake of brevity. Also, the loading of Certificate Revocation Lists (CRLs) is not explained, I think that is beyond the scope of your question.
The actual verification function
The functionality to verify a certificate (chain) is provided by the OpenSSL function X509_verify_cert(). A return value of 1 indicates successful verification, 0 indicates no success. As you can see in the documentation, the function only requires one parameter of the type X509_STORE_CTX, which is a structure holding the "context" (a rather vague and overused term in OpenSSL, IMO) of the collection of X509 certificates involved.
Setting up the certificate store context
The certificate store context contains information about trusted certificates, untrusted intermediate certificates and the certificate to be verified. It is constructed and initialized as follows:
store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(store_ctx, store, cert, intermediates)
The store parameter will be used to contain information about the trusted certificates, the cert parameter contains the certificate to be verified and the and intermediates parameter is a stack of untrusted intermediate certificates.
The store parameter
The X509_STORE type is able a contain a set of X509 certificates and for the purpose of verifying a certificate needs to be provided with information about trusted certificates. Since you indicated that you have trusted certificates in /etc/ssl/certs, this can be done as follows:
store = X509_STORE_new();
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
X509_LOOKUP_add_dir(lookup, "/etc/ssl/certs", X509_FILETYPE_PEM);
This assumes that your local trust store is set up properly
The cert parameter`
This parameter contains the actual certificate to be verified. It can be loaded from a file in several ways, one approach is as follows:
bio_in = BIO_new_file(certFileName, "r");
result = PEM_read_bio_X509(bio_in, NULL, NULL, NULL);
BIO_free(bio_in);
The intermediates parameter
OpenSSL provides a stack API to handle collections of objects. The intermediates parameter is a stack of X509 objects that contains the intermediate certificates between your certificate to be tested and your trusted certificate. In pseudo code, it can be filled as follows:
intermediates = sk_X509_new_null();
for (filename in certFilenames) do {
icert = readCert(filename);
sk_X509_push(intermediates, icert);
}
This concludes the explanation, this should give you all you need to verify the chain.
**About the certificate at the end of the downloaded chain
The certificate at the end of the downloaded chain is typically contained in your local trust store. Some experiments show that you can actually feed it into the verify function as if it is an untrusted intermediate or you can omit it. Both seemed to end in a properly verified chain.
A code example
Finally :-)
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
const char *trustedCertsPath = "/etc/ssl/certs";
int main(
int argc,
char **argv)
{
X509 *cert = NULL;
X509 *icert = NULL;
STACK_OF(X509) *intermediates = NULL;
X509_STORE *store = NULL;
X509_LOOKUP *lookup = NULL;
X509_STORE_CTX *store_ctx = NULL;
BIO *bio_in = NULL;
int currentArg = 1;
int result = 0;
store = X509_STORE_new();
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
X509_LOOKUP_add_dir(lookup, trustedCertsPath, X509_FILETYPE_PEM);
/* Certificate to be checked */
bio_in = BIO_new_file(argv[currentArg++], "r");
cert = PEM_read_bio_X509(bio_in, NULL, NULL, NULL);
BIO_free(bio_in);
/* Stack of untrusted intermediate certificates */
intermediates = sk_X509_new_null();
while (currentArg < argc) {
bio_in = BIO_new_file(argv[currentArg++], "r");
icert = PEM_read_bio_X509(bio_in, NULL, NULL, NULL);
BIO_free(bio_in);
sk_X509_push(intermediates, icert);
}
store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(store_ctx, store, cert, intermediates);
result = X509_verify_cert(store_ctx);
printf("Result from X509_verify_cert is %d\n", result);
sk_X509_pop_free(intermediates, X509_free);
X509_STORE_CTX_cleanup(store_ctx);
X509_STORE_CTX_free(store_ctx);
X509_STORE_free(store);
}
You can build and run it as follows (where the .pem arguments are the names of the files containing your certificate and intermediates in PEM format:
$ gcc main.c $(pkg-config openssl --libs) -o verify -Wall
$ ./verify \*.google.com.pem Google\ Internet\ Authority\ G3.pem
Result from X509_verify_cert is 1
I am trying to generate digital signature using "Microsoft RSA SChannel Cryptographic Provider". After acquiring the handle to container, I am generating a signature using CryptGenKey(). But this function returns FALSE.
The dwError for CryptGenKey() returns 80090008.
The same works for any other provider type. Also, when I am trying to create key exchange pair for the same provider, it is working fine. What am I doing wrong?
#include <Windows.h>
#include <wincrypt.h>
int main()
{
HCRYPTPROV phProv = 0;
LPTSTR pszContainer = NULL;
DWORD dwFlags = 0;
bool flag;
DWORD_PTR dwError;
HCRYPTKEY phKey;
flag = CryptAcquireContext(&phProv, pszContainer,
MS_DEF_RSA_SCHANNEL_PROV, PROV_RSA_SCHANNEL, dwFlags);
if (!flag)
{
flag = CryptAcquireContext(&phProv, pszContainer,
MS_DEF_RSA_SCHANNEL_PROV, PROV_RSA_SCHANNEL, CRYPT_NEWKEYSET);
}
dwError = GetLastError();
flag = CryptGenKey(phProv, AT_SIGNATURE, CRYPT_EXPORTABLE, &phKey);
dwError = GetLastError();
flag = CryptGetUserKey(phProv, AT_SIGNATURE, &phKey);
dwError = GetLastError();
return 0;
}
Thanks.
The SChannel provider doesn't support AT_SIGNATURE RSA keys, only AT_EXCHANGE. It's mainly a holdover from early TLS (back when it was still SSL) when keys were exchanged using RSA encryption instead of agreed upon using Diffie-Hellman Key Agreement signed with RSA signature... and then, presumably "well, everybody knows how the SChannel provider behaves, why change it?". (The closest I see to that being written down is https://msdn.microsoft.com/en-us/library/windows/desktop/aa387690(v=vs.85).aspx, which shows CALG_RSA_KEYX and doesn't talk about CALG_RSA_SIGN.)
In CAPI an AT_EXCHANGE key can do both encryption and signing, and an AT_SIGNATURE key can only do signing.
In general the Windows Cryptography team discourages new code written with CAPI (I don't have written evidence othen than what I just wrote; mainly this has come from being in meetings with them). CNG has a much more developer-friendly API, and is more powerful. CNG shipped back in in Windows Vista, and so it's available in every supported version of Windows. CAPI doesn't serve the purpose of "well, it has more coverage" anymore, it's just "the old, crufty, legacy API" (unless you're writing code for out-of-support OSes, like XP).
If you are using CAPI, I don't know why you'd want to use the SChannel provider. PROV_RSA_AES via MS_ENH_RSA_AES_PROV is the most functional RSA that CAPI has (SHA-2 based PKCS signatures). But it's out of date compared to the software provider in CNG (PSS signature, OAEP with SHA-2, and supports public exponent values larger than 2^32 (okay, that's not a common need, but it's something CNG fixed)).
I'm using libssh2 to make a networking program more secure.
I'd like my program to authenticate in as similar way to the OpenSSH client ssh(1) as possible. The OpenSSH client will only ask for passphrases for keys that are actually accepted by the server.
As I understand from this link, an ssh client sends a request to use a public key, and if that is accepted, it can unlock the private key using the passphrase.
libssh2 provides a function libssh2_userauth_publickey_fromfile which takes the private and public key file names and a passphrase. Using this function is very straight forward, but it means I have to obtain the passphrase for private keys even if the public key wouldn't have been accepted by the server in the first place. This is clearly a problem for users that have a lot of different keys (my program currently iterates through key files in the ~/.ssh directory).
I have tried reading the man pages for libssh2 functions, and most of them appear too brief for me to understand without a more detailed knowledge of the ssh protocol. In fact some of them haven't even been written yet.
Can anyone tell me how to only prompt for passphrases for keys that are actually accepted by an ssh server using libssh2?
After RTFM and doing some testing, I discovered that libssh2_userauth_publickey_fromfile will return a different error code depending on whether the key wasn't accepted by the server, or the passphrase was incorrect.
So, here is a pretty inefficient solution (because it calls libssh2_userauth_publickey_fromfile and hence all the key exchange parts of the protocol at least twice).
int nAttempts = 3; // number of attempts the user gets at entering the passphrase
// Try authenticating with an empty passphrase
int err = libssh2_userauth_publickey_fromfile(session, user, pub, priv,"");
if (err == 0)
{
fprintf(stderr, "You shouldn't use keys with an empty passphrase!\n");
}
else if (err == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
{
// if we get here it means the public key was initially accepted
// but the private key has a non-empty passphrase
char p[BUFSIZ];
for (int i = 0; i < nAttempts; ++i)
{
get_passphrase(p); // assume this gets the passphrase
err = libssh2_userauth_publickey_fromfile(session, user, pub, priv,p);
if (err != LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) break;
}
}
if (err != 0)
{
fprintf(stderr, "Authentication using key %s failed!\n", priv);
}
For completeness, the get_passphrase function uses the solution to this question to prompt the user for a passphrase.
I am trying to use OpenSSL in my application, and achieve a secure connection.
At first I tried:
create ssl struct
create socketbio for the tcp socket
create a sslbio
set socketbio to SSL strcut
SSL_accept(ssl)
BIO_push(ssl, socketbio)
This cause handshake to happen successfully, but application data wasn't properly decrypted.
Then I tweaked a little, and relaced 6 with
(new) BIO_ctrl(sslbio, SET_SSL, ssl)
and things worked fine.
I Wanted to know, what's wrong with previous approach, and what's causing the new apprach work?
It's hard to answer the question without knowing why you think BIO_push is all you need to do. At any rate, you shouldn't call BIO_ctrl directly. You should use the high-level wrapper BIO_set_ssl defined in bio.h:
#define BIO_set_ssl(b,ssl,c) BIO_ctrl(b,BIO_C_SET_SSL,c,(char *)ssl)
This macro sets the ssl member of the BIO object as you can see in bio_ssl.c:
case BIO_C_SET_SSL:
if (ssl != NULL)
ssl_free(b);
b->shutdown=(int)num;
ssl=(SSL *)ptr;
((BIO_SSL *)b->ptr)->ssl=ssl;
bio=SSL_get_rbio(ssl);
if (bio != NULL)
{
if (b->next_bio != NULL)
BIO_push(bio,b->next_bio);
b->next_bio=bio;
CRYPTO_add(&bio->references,1,CRYPTO_LOCK_BIO);
}
b->init=1;
break;
The important step in this function is not the BIO_push, but rather is where it sets the ssl pointer in the BIO_SSL object to your active SSL context, i.e., ((BIO_SSL *)b->ptr)->ssl=ssl;.