I am having problem using the exemple provided by OpenSSL to create a certificate Request with v3 extensions. This entire code can be found in the mkreq.c in Openssl/demos/x509/
Adding some x509v3 extensions to a cert request is working good.
I can add the Key usage or a subject alt name
add_ext(exts, NID_key_usage, "critical,digitalSignature,keyEncipherment");
add_ext(exts, NID_subject_alt_name, "email:steve#openssl.org");
but when I try to add an AuthorityKeyIdentifier this is not working...
add_ext(exts, NID_authority_key_identifier, "keyid,issuer");
The add_ext is also provided in the mkreq :
int add_ext(STACK_OF(X509_REQUEST) *sk, int nid, char *value)
{
X509_EXTENSION *ex;
ex = X509V3_EXT_conf_nid(NULL, NULL, nid, value);
if (!ex)
return 0;
sk_X509_EXTENSION_push(sk, ex);
return 1;
}
Do somebody have a clue why some extensions are working and some not ? When I add the same extensions for self-signed its working well...
After reasearching for some time, it appears that this is not possible since you don't know the CA when you are creating a certificate request...
I find the following worthwhile to share, though I am not sure this is relevant to your situation.
When generating self signed certs on the command line, the order of some extensions is important. If you want the keyid to be used as the authority key id, you must declare the subjectKeyIdentifier first.
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
In that case authorityKeyIdentifier will be filled with the keyid, and issuer will not be used.
If you declare in the opposite order, authorityKeyIdentifier will be filled with the issuer instead. Possibly because the program tries to init authorityKeyIdentifier based on subjectKeyIdentifier.
If you declared authorityKeyIdentifier = keyid:always, then a hard error is thrown because keyid is not known.
I hope it will help someone. It took me a while to tackle that one.
Also avoid CA constraint
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = #alt_names
[alt_names]
DNS.1 = localhost
Related
I am new to the JWT tokens, and I am trying get information from a jwt token. The thing is I don't have issues when I am the one generating the token, but, for some reason, when I generate the token at JWT.io with exactly the same information, the token is different and, therefore, the validation fails. I guess the problem may come from the key I am using, as, when using a simple key like "HELLO", this disparity does not happen. This is my code:
<?php
namespace App\Controller\Component;
use Cake\Controller\Component;
use \Firebase\JWT\JWT;
use Cake\ORM\TableRegistry;
class JWTComponent extends Component
{
public function check_token($token){
$decoded = [];
$key = openssl_pkey_get_details(openssl_pkey_get_private('file://'.APP.'private.pem'))['key'];
try {
$decoded = JWT::decode($token, $key, array('HS256'));
$decoded = (array) $decoded;
} catch (Exception $e) {
$decoded = ['error' => $e->getMessage()];
}finally{
return $decoded;
}
}
public function get_token($data) {
$key = openssl_pkey_get_details(openssl_pkey_get_private('file://'.APP.'private.pem'))['key'];
return JWT::encode($data, $key);
}
}
Your intuition is good. The integrity of the token is verified by checking the signature. Tokens are signed by the party which issued them. You can use different algorithms to sign those tokens. As #jps pointed out you can have symmetric and asymmetric signing. In symmetric signing the same key is used to sign and verify the key. HS256 is a symmetric signing algorithm. You can use a certificate to do that (like in your code), but it's a bit of an overkill in my opinion. Anyway, if you want the key generated at JWT.io to be valid in your code, you will have to paste the private key in JWT.io so that it can be used for signing. Then the token should be valid in your code. That's why it worked when you used a simple string as the key.
The token that you generate in your code and in JWT.io can, in the end, look a bit differently. That is, they will both be long strings, with three parts separated by dots, but the strings does not have to be equal. This does not mean that this is a different token. The encoded JWT can differ depending on whether you used line breaks in the input, or how many spaces you used. Even though, the encoded final JWTs may look differently, these tokens have still the same value. If you decode them, you will get the same JSON, maybe slightly differently formatted.
As for the use of the symmetric algorithm, it's usually better to use asymmetric signing, so if you are able to go with that option I would definitely recommend it. Also, have a look at some libraries for PHP to issue and validate JWT, unless you write the code to learn more about JWT itself. You can find a list of libraries on JWT.io.
If you're planning to secure your APIs with JWTs, have a look at this security best practices article I wrote, to learn about the dos and don'ts of JWTs.
I'm trying to perform certificate chain validation for Windows executable files, which also includes check for revoked certificates, using OpenSSL 1.0.2 C API.
I have the CRL files stored locally and I want to load them during verification (as opposed to download the CRL via "CRL Distribution Points" URL from certificates which have it).
Here's my simplified example of loading a single CRL file (omitting any error checking):
X509_STORE *store = NULL;
X509_STORE_CTX *ctx = NULL;
X509_VERIFY_PARAM *params = NULL;
X509_CRL *crl = d2i_X509_CRL_fp(fc, NULL); // fc is a file pointer to CRL file
X509_STORE_add_crl(store, crl);
X509_STORE_CTX_init(ctx, store, NULL, NULL);
params = X509_STORE_CTX_get0_param(ctx);
X509_VERIFY_PARAM_set_purpose(params, X509_PURPOSE_ANY);
X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_CRL_CHECK); // only want to check end entity
X509_STORE_set1_param(store, params);
// assume p7 is properly initialized PKCS7*
// assume bio is properly initialized BIO*
int ret = PKCS7_verify(p7, p7->d.sign->cert, store, bio, NULL, 0);
Above code will return ret == 0 with error: unable to get certificate CRL, which from my understanding means that OpenSSL is still trying to search CRL from the certificate itself instead of using the one I load locally.
What is the proper way of achieving this task?
Actually the code above is already correct to achieve my goal in performing CRL check.
One potential pitfall for someone new to X509 certificate structure is that the "CRL Distribution Points" URL for the certificate of interest is contained within that certificate itself, and not on the issuer's certificate. This was my mistake which led to the error I mentioned. I hope this may help people who just get started in understanding the X509 standard.
I am using the xmlsec library to verify the signature of an SAML assertion. My code is almost identical to the verify4.c example provided on the xmlsec web page.
I am linking against the xmlsec-openssl lib, so using openssl as the crypto engine.
I was expecting that xmlsec would consider the signature valid only if it was signed with one of the specific certificate(s) I had loaded into the key manager.
However, the signature seems to be considered valid if signed with ANY certificate that can be verified by openssl. This means that someone could forge an SAML response just by buying a certificate from a trusted root CA and using that to sign whatever response they want.
Not only that, but the xmlsec1 command line tool provided with the library seems to do the same thing:
xmlsec1 --verify --dtd-file saml.dtd --pubkey-cert-pem my_cert.cer sample_saml_assertion.xml
...
OK
SignedInfo References (ok/all): 1/1
Manifests References (ok/all): 0/0
Actually, in an ideal world, I would be happy for any valid signing key to be used as long as I could identify the subject of the key and therefore confirm it was signed by the entity I expected. That would simplify matters when the sender of the SAML responses changed their signing key. But I have not been able to find a simple way to extract the details of the cert that was used to verify the signature.
Failing that, can I make it accept only the certificate(s) I have specified when verifying the signature?
While writing the question I realized I had not tried the --print-debug option for xmlsec1. When I tried that I found that it does indeed print the subject and issuer of the cert that was used to verify the signature.
That led me to realize that the information must be present, so it is a question of how to access it. Tracing through the code, I was able to write this little snippet which does the trick:
/* If signature is valid, then the list dsigCtx->signKey contains
the signing key, data dsigCtx->signKey->dataList contains the certificate */
xmlSecPtrListPtr keyDataList = dsigCtx->signKey->dataList;
/* Iterate through the data list to find the X509 cert */
xmlSecSize n = xmlSecPtrListGetSize(keyDataList);
xmlSecSize i;
for (i=0; i<n; i++) {
xmlSecKeyDataPtr item = xmlSecPtrListGetItem(keyDataList, i);
if (xmlSecKeyDataIsValid(item) && xmlSecKeyDataCheckId(item, xmlSecOpenSSLKeyDataX509Id)) {
/* Extract openssl cert */
X509* cert = xmlSecOpenSSLKeyDataX509GetKeyCert(item);
char cn_buff[256];
if(cert != NULL) {
/* Get the CN */
X509_NAME * subject_name = X509_get_subject_name(cert);
int nid_cn = OBJ_txt2nid("CN");
X509_NAME_get_text_by_NID(subject_name, nid_cn, cn_buff, 255);
/* Here you would compare it to the expected certificate */
fprintf(stdout, "CN=%s\n", cn_buff);
} else {
fprintf(stdout, "Failed to obtain signing key cert\n");
}
}
}
This seems a very complex way to obtain something so fundamental, so I am sure there must be an easier way.
I'm generating a SAML2 token from ADFS, signed by certificate. Now I'm trying to verify that signature, using the same certificate.
X509Certificate2 cert = LoadCert();
XmlDocument token = LoadXmlToken(); //SAML2 token
XmlElement signature = GetSignatureElement(token);
SignedXml signedXml = new SignedXml(token);
signedXml.LoadXml(signature);
bool result1 = signedXml.CheckSignature(); //true
bool result2 = signedXml.CheckSignature(cert, false); //false
CheckSignature() verifies signature against the public key contained in the token.
CheckSignature(cert, [true/false]) verifies signature against the private key from the certificate.
How can it be that one works and the other doesn't?
The method signedXml.CheckSignature() evaluates the xml signature integrity against the certificate contained inside the own signature.
The method SignedXml.CheckSignature(X509Certificate2, Boolean) evaluates the xml signature integrity against the certificate passed as first parameter, and optionally if the second parameter is false it checks also the validity of the certificate in the first parameter.
Probably the second method returns false because you are specifying a wrong certificate: is not the certificate which performs the signature or its state is revoked or expired or it is issued by an untrusted certificate authority.
We had to enable IP address and/or URL's on our outbound firewall for the checksignature method when using the certificate check. In our case it tried to communicate with the root CA and the sub CA's website. With the firewall closed the method failed, but once we identified the URL's being accessed and opened up the firewall it started to work as expected.
The difference is in the second parameter (boolean). If you look at documentation of parameterless CheckSignature method you can find this:
This method also computes the digest of the references and the value of the signature.
The second method has this documentation. If the second parameter is set to
false then verify both the signature and certificate.
To verify certificate this method will probably build whole certificate chain and check revocation information of all certificates in this chain.
I'm trying to authenticate against the user db of my website (CMS based) and it uses a slightly different approach at storing hashed passwords. It uses a randomly generated salt for each user. The salt is stored in the user db along with the hashed passwords. Hence, direct field-mapped authentication (as the External DB plugin does) won't work for me.
To start off, I just mirrored the DB plugin and modified the user_login() procedure to read the hashed password and the salt from the database and then hash the entered password again with the salt and match it up with the password in the database. Here's the code for my user_login() function
function user_login($username, $password) {
global $CFG;
$textlib = textlib_get_instance();
$extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->extencoding);
$extpassword = $textlib->convert(stripslashes($password), 'utf-8', $this->config->extencoding);
$authdb = $this->db_init();
// normal case: use external db for passwords
// Get user data
$sql = "SELECT
*
FROM {$this->config->table}
WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ";
$authdb->SetFetchMode(ADODB_FETCH_ASSOC);
// No DB Connection
if ( !$rs = $authdb->Execute( $sql ) ) {
$authdb->Close();
print_error('auth_dbcantconnect','auth');
return false;
}
// No records returned
if( $rs->EOF ) {
$rs->Close();
$authdb->Close();
return false;
}
// Get password
$db_password = $rs->fields['user_password'];
$salt = $rs->fields['user_salt'];
// Close DB Conn
$rs->Close();
$authdb->Close();
// Return match
return sha1( $extpassword . $salt ) == $db_password;
}
But when I try to login, username / passwords corresponding to the website (CMS) database are failing. However, the password (for the same user) that was stored in Moodle earlier on (before I tried using this custom plugin) is getting me through.
That means, either my authentication routine is failing or moodle's internal db based auth mechanism is taking precedence over it.
I've enabled ADODB debug mode - but that isn't helping either. When I enable the debug output from Server settings, the error messages are being sent prior to the page headers. Thus the login page won't display at all.
I have all other forms of authentication turned off (except for Manual which can't be turned off) and my own.
Any ideas on how to solve this issue?
Can you confirm the order that the authentication pluggins are displayed? This will determine the order in which they are used. See..
http://docs.moodle.org/en/Manage_authentication
Either way, the behaviour you're seeing suggests that your code is returning false and the fall through logic described here...
http://moodle.org/mod/forum/discuss.php?d=102070
... and here...
http://docs.moodle.org/en/Development:Authentication_plugins
... is kicking in.
Have you tried returning "true" always from your plugin to ensure that it's being called. Then, you can start returning "true" based upon other things (hard coded usernames etc). This approach will allow you to get to the point where you are either continuing to fail or seeing more targetted failures. Are you sure, for example, that it's the user_login function and not the subsequent call to update_user_record that is failing?
Finally, are you sure you're generating the salted password in the exact same way that it was created in the first place? This would be, for me, the most likely cause of the problem. Can you take control of the creation of the salted password so that you own both creation of new users and authentication of users - this would ensure that you were in sync with how the salted password and hash were generated.