I'm designing a system where users will be able to register and afterward authenticate with client certificates in addition to username/password authentication.
The client certificates will have to be valid certificates issued by a configured list of certificate authorities and will be checked (validated) when presented.
In the registration phase, I need to store part(s) of the client certificate in a user repository (DB, LDAP, whatever) so that I can map the user who authenticates with client certificate to an internal "user".
One fairly obvious choice would be to use certificate fingerprint; But fingerprint itself is not enough, since collisions may occur (even though they're not probable), so we need to store additional information from the certificate. This SO question is also informative in this regard.
RFC 2459 defines (4.1.2.2) that certificate serial number must be unique within a given CA.
With all of this combined, I'm thinking of storing certificate serial number and certificate issuer for each registered user. Given that client certificates will be verified and valid, this should uniquely identify each client certificate. That way, even when client certificate is renewed, it would still be valid (serial number stays the same, and so does the issuer).
Did I miss something?
You have several solutions:
Storing a fingerprint.
Yes you are right, a collision is theoretically possible but the probability is really really low and you can consider it does not happen : 2 users in your system will not have accidentally the same certificate fingerprint. However hash algorithms are getting weaker over time and an attacker may try to forge a certificate whose fingerprint matches a registered one. This attack is called second preimage attack and is pretty hard to do since the attacker does not try to forge some random data matching the fingerprint but a real X.509 certificate which can pass the initial validation phase (i.e. hacking the PKI). Pretty hard :) But if you really want to prevent yourself against collision you can store 2 fingerprints with 2 distinct algorithms (e.g. SHA-1 and SHA-256).
Storing the certificate issuer and serial number. Yes it can be used as a unique certificate identifier. As you wrote, the standard (RFC 5280 obsoletes RFC 2459) indicates [The serial number] MUST be unique for each certificate issued by a given CA. However it also means that when the certificate is renewed the serial number changes since a new certificate is issued by the CA.
A final remark: you want to handle certificate renewal and it is a good idea, a lot of software editor forget that certificates have to be renewed. But you must be aware that almost everything may change in the certificate: the subjet name (people may change their name, women get married...), the issuer name (the certificate supplier company may change...), the key algorithm, the key size, the extensions... In your system the certificate renewal process will probably be pretty close to the initial user certificate registration.
The best way to uniquely identify a user is by email address. In the registration process a valid email address must be mandatory. Then you associate the certificate serial number and issuer or perhaps a hash of the the certificate itself with the email and the user id. Then, when the certificate expires or the user changes the certificate he/she will have a "renew certificate link" where he enters the email address and he receives a link to upload the new certificate. You can then replace the old serial/issuer with the new one.
I decided to concatenate the issuer name, a delimiter |, and the DN.
Hopefully, this solves the problem of using serial numbers which change on renewal.
Related
I'm signing JWT's using the RS256 algorithm. To verify those tokens on the client, I somehow need to access the public key.
Are there security concerns (spoofing, ...) when I set up an unprotected API route ('/api/certificate') that returns the certificate containing the public key. And do I need to take any extra security measurements?
Several concepts are often mixed up, maybe not for you, but let me try to explain a few things in this answer.
Assymetric cryptography obviously needs a public and a private key, both are basically just numbers. The private key is kept secret, the public is, well, public, anybody can have it. When signing stuff, you use the private key to sign and then anybody can verify using the public key that the signature was made by somebody that had the corresponding private key (ie. you).
But the question is then how you distribute your public key, or in your jwt example, how clients get it. As you correctly pointed out in the question, simply downloading the public key over an insecure channel is not good enough as an attacker could replace it with his own, resulting in the attacker being able to sign tokens.
One solution to this could be getting it over https as you proposed, which practically means using a second set of public-private keypair (keys of the webserver) to secure sending the first one. The theoretical question is still the same btw, it's just inherently solved in the background for you: how does the browser know that the public key it receives from the server upon connection actually belongs to the server. There is no secure channel yet between them.
Enter certificates.
A certificate is a document that essentially ties a public key to its owner, and that is excactly what you want. When a browser connects to a website, the server sends its public key along with its certificate, so that the browser can verify that the public key actually belongs to the server (the domain name in this case) that sent it. How that verifies it is beyond the scope of this post, the point is that the certificate is signed by another public key, the certificate to which may be signed by another public key, etc., and the chain is terminated by a list of so called trusted root certificates already set up for your computer and/or browser by your OS/browser vendor.
And you too should verify the public key with the certificate the same way. You don't even need the burden of SSL (https) transport for this, verifying that a public key belongs to a particular subject is the main purpose of certificates.
So all you have to do is not just get the public key from the API, but get it along with its certificate. You are probably already doing this, bare public keys are very rarely used. You are most probably already receiving a pfx or cer or crt or whatever from the server. Depending on the technology stack that you are developing on, you can for sure use built in mechanisms to fully verify a certificate and make sure it's valid. Please don't implement your own validation though, as that's tricky business and quite hard to get right. If the certificate passes validation, you can trust that the public key you received from the API is authentic and belongs to whatever it claims to belong to. There may be caveats though (like for example make sure that beyond basic validation, you check a combination of fields from the certificate that others can't have).
As an additional security measure, you can also implement certificate pinning to make it even more secure against certain types of attacks by having a list of fingerprints for valid certificates in the client (less so in a browser client, but still the concept is the same).
Edit (what fields to check in the certificate after it passed general validation of expiry, etc):
In the general case it depends on who signed the certificate and what kind of certificate it is.
A server certificate signed by a real certificate authority (CA) can only have the server domain as its common name (CN) field, a real CA won't normally sign anything else, and they also won't sign a certificate for yourdomain.com unless you can prove you control yourdomain.com. So in this case it may be enough to check CN after the cert passed validation. You do need to check CN though, as anybody can have a valid certificate from say GlobalSign or Thawte or other trusted CAs, it just costs money. What they can't have is a certificate for yourdomain.com.
If you sign your own certificates, you also won't sign anything for anyone, so in that case it could be enough to check the issuer (that you signed it) and the CN (for whom). If the certificate otherwise passed validation (meaning a trusted root certificate signed it) it should be ok, as an attacker won't normally be able to have his CA certificate as trusted on your computer.
The point in general is that you want to check something that others can't have. It's easier, if you are relying on real CAs, and it's usually best to check the fingerprint.
I can sign the new (public) key with the old (private) key to securely transfer from one to the other. But far as I can tell, there's nothing stopping anyone who gets the old key from signing a different new key as official, even much later after I've given the old one up.
If I revoke the old private key, nobody can make that bogus signature, which is good. But doesn't revoking a key mean its signature of the new one would be invalidated? (As well as every single signature it's made?)
So someone who knows me as "Alice" would be able to tell I'm the same, even if I'm "Bob." But then they see "Alice"'s revocation, in which case they have to throw all of their knowledge about me away, whether Alice or Bob?
The usual way to deal with this is to use a different key for signing keys and for signing “useful” data.
The data signing key lives on an online server where it's invoked automatically, and thus is at risk of misuse. This key is rotated regularly. In principle, it wouldn't need to be rotated: just revoke it if something bad happens. But in practice revocation is hard: it's often difficult to ensure that every party that relies on the signatures will see the revocation message in a timely way. Expiration ensures that even if a party doesn't see the revocation message, it won't keep accepting compromised signatures for long.
The key signing key lives on an offline server with stringent access control, and since it's a lot better protected, it has a very low risk of being compromised.
A system with keys that sign other keys is called a public key infrastructure. Many systems have multiple levels of signing keys. The best known PKI ecosystem is the one used for HTTPS, with servers having a certificate signed by an intermediate certificate authority, which in turn has a certificate signed by another CA until you reach a root CA. Intermediate CAs are online, but run specialized systems which do nothing but sign certificates. The keys to root CA certificates are split among multiple employees who all get together into a vault with no Internet connection once every few years to generate a new intermediate key.
Running your own CA with OpenSSL is not trivial, but it's doable, and you'll find many tutorials on the web.
We are currently designing a smartphone application that needs an authentication protocol.
We will use HTTPS for all the messages. The idea is the following :
The client contacts the server and authenticates himself with his user/password combination.
The servers replies with a ramdom-generated token that is stored in the database.
To contact the server the client now uses his/her user/token combination.
In each message he sends, the server has a certain probability to regenerate a new token that it includes in the message it sends.
The question is : will we have security issues using this protocol ?
Note : passwords and tokens are stored hashed in the database.
The security bases on the certificate you use for encryption. In general this is enough, you may also check if it is the expected certificate. In the case that you check yourself the fingerprint of the certificate you can be sure (if you use sha1 or better) that the certificate is from you and not a successful man in the middle attack. E.g. the NSA could simple create valid certificates for your domain, but AFIK it is impossible to generate a second certficate with the same fingerprint.
By the way I hope that the passwords and tokes are also salted. That is important so it is impossible to see that two customers uses the same password and also it increases the complexity of the hash, that means that it will take much more time to crack such a password with a rainbow table.
I've been reading a few sites on the internet on how SSL works, but I don't understand how exactly it makes things secure. Probably because I don't understand completely how it works.
Let me begin with the core idea of SSL. It is used to encrypt HTTP connections, but for the client and the server to communicate with encrypted data, surely an encryption key needs to be shared. If someone is eavesdropping on your connection, wouldn't they just be able to grab this key and continue listening while decrypting the data? I can image this technique would work if we're talking about a long term connection, but HTTP requests are often completed within half a second.
Let's assume this is somehow taken care of. The other utilisation of SSL is to verify if a server is exactly who it says it is. What prevents a rogue server from faking a certificate signed by a root certificate provider? In none of the descriptions I've read, the browser actually contacted one of these authorities to verify the certificate with them. Let's assume the certificate is encrypted with a private key by the root certificate authority, how is the browser able to verify the data in this certificate without knowing the decryption key? Or is the decryption key different from the encryption key?
One solution to these problems I can imagine is if the certificate and key are only sent once and are stored along with the domain and IP address in your browser.
Thanks for explaining in advance.
First, some basic concepts about public key cryptography:
This relies on a pair of keys. One is the public key (which can be distributed); the other one is the private key, intended to be kept private.
You can encrypt data using the public key, which the private key can decrypt/decipher.
You can sign data using the private key, and this signature can be verified using the public key.
To make sure you're communicating with the right entity, you need to bind an identity to a key-pair. This is where certificates come in. A public key certificate is a signed document containing both the subject's identity (name) and the subject's public key.
For example, the certificate for www.google.com contains its public key and the name www.google.com. It has been signed using the private key of a Certification Authority (in this case, Thawte). In the X.509 terminology (the common standard for certificates used for HTTPS), the CA is the issuer of the certificate, and it puts its name in the certificate too, alongside the subject's name, the subject's public key (and other attributes). The issuers are meant to verify the identity of who they issue a certificate for.
The reason you don't necessarily see your browser fetching information from the CAs is that a number of commercial (or governmental) CA certificates are bundled with your browser or your OS. You trust them by default. This can be considered as a "leap of faith", but any trust mechanism needs this sort of starting point.
You may want to read more about the TLS handshake, but in short:
The client gets the server's public key by looking into its certificate.
The client encrypts a secret using this public key and sends it to the server. The details of this depend on the cipher suite (could be Diffie-Hellman based), but the result of this should be a list of shared encryption keys (using symmetric cryptography, not public key cryptography).
These shared keys are only known to the client and the server, and they're used for encryption/decryption.
For SSL/TLS to be secure, you need at least 3 points:
A suitable cipher suite, and a successful handshake.
Verifying that the client trust the server certificate (typically, via a known CA in the PKI model).
Verifying that the certificate belongs to the server the client intended to contact (hostname verification).
(This is the case for the vast majority of usages of SSL/TLS (in particular HTTPS), but it's also possible to use other mechanisms than X.509 certificates with TLS, for example OpenPGP certificate or Kerberos cipher suites. This is less common as far as I know.)
In order to encrypt a connection you have to agree to some shared secret. This can be done with diffie-hellman. To prevent man in the middle attacks, so you also need a certificate mechanism.
For encrypting or signing (certificates) you can use asynchronous keys. This means you have two different keys (public and private key) to encrypt/decrypt. Usually you encrypt your data with a public key, and someone can decrypt it with his private key. Signing is done with your private key, and someone else can check it with a public key.
So you see, faking a certificate is not that easy, since you don't have the private key from a root certificate provider.
surely an encryption key needs to be shared. If someone is eavesdropping on your connection, wouldn't they just be able to grab this key
No. The key is never transmitted. It is computed at both ends independently via a key-agreement algorithm.
What prevents a rogue server from faking a certificate signed by a root certificate provider?
The certificate is sent along with its digital signature which is made with the private key, and verified by the peer via the certificate's own public key. The server would need the private key of the server it is spoofing.
When using protocols such as Diffie-Hellman key exchange, the two parties to a communication each generate a random number, transform it in some way, and send the transformed version to the other party. The transformation is such that combining the first number with the transformed version of the second will yield the same result as combining the second number with the transformed version of the first. An adversary who only had the transformed numbers, however, would have no way of finding the un-transformed version of either, nor a way of computing what the result would be if the (unavailable) untransformed version of one number were combined with the (available) transformed version of the other.
Diffie-Hellman key exchange by itself would be sufficient to protect against all forms of passive attack or historical attacks (meaning if an attacker hadn't taken steps to intercept a communication before it took place, it cannot later be compromised except by performing some calculations which could not, with anything resembling today's technology, be computed in any remotely feasible time). The problem with it is that it cannot very well protect against the situation where an attacker (e.g. Z) can intercept all communications between the participants (e.g. X and Y) and substitute his own. In that scenario, X would establish a connection with Z--thinking him to by Y--which nobody but he and Z could decode. Z would then--pretending to be X--establish a connection with Y.
If X and Y have any pre-existing means of sharing information with each other in such a way that they can decode it much faster than Z, even if it's not terribly secure, this may suffice to prevent the above-described man-in-the-middle attack. All that needs to happen is for X and Y to ask each other something about the key they're using. If Z can recognize that question and substitute its own answer, it would be able to continue the ruse. On the other hand, if the question were asked in such a way that a legitimate party would be able to respond much more quickly than an imposter, Z might be stumped. As an example, if a voice-phone application displayed for each participant information about the negotiated key, and one party asked the other "read off digits 12 to 18 of your key, doing your best impression of Elmer Fudd" (selecting, on the spot, the digits to read and the voice to use) a legitimate participant would be able to respond immediately, but an attacker would need time to produce a phony recording of the person speaking as indicated).
I'm making a server-client to use ssl for sign up and login process.
(and this is for iphone if it matters)
I just started looking at what ssl is and how to use it, and
saw there is a certificate in the process which can be bought or self-signed.
If I use self-signed certificate in web server, web browser would alert that cert is self-signed, that I understand.
But what would happen if I use self-signed certificate in regular application with tcp(not http), specifically iphone.
I just want to make the signup/login
info(their password) to be secure,
and hoping that using self-signed
certificate would be ok for this
purpose. But I also need to make
sure this won't cause "not trusted
certificate - alert" type of
interruption when used in application
other than a web browser.
Edit
I understand that "not trusted certificate alert" is saying client shouldn't trust this server.
But in my situation, client doesn't need to authenticate with the server.
The server just needs to get client's password in a secure way.
To answer your question: You can, but you shouldn't!
First, using SSL only for authentication isn't secure at all. The authentication process probably produces some kind of session (e.g. cookie) which is then transfered without encryption. Therefore, the session can be stolen (see Session hijacking).
Second, using a self-signed certificate allows man-in-the-middle attacks. So, someone can steal the user's password and he probably won't even notice it. The user doesn't know the difference between the alert that pops up when the client receives your self-signed certificat and the pop up that shows when the attackers self-signed certificate is used.
My advice: Don't use self-signed certificates. When an attack happens it's bad for you and your customers.
When you use an SSL connection to encrypt a login dialogue with a password the server sends the client a public key (in the form of a certificate), and the client generates a one-off session key, encrypts it using the server's public key, and sends it to the server. The server can then decrypt the session key because it has the private key.
The user then encrypts his password using the session key and sends that to the server, which can decrypt it because it knows the session key.
Now, without PKI if an attacker wanted to learn your password he could spoof the server. He'd send you his public key and you'd generate a session key, etc., in the usual way and send him your password which he would be able to decrypt because you'd be using his key without knowing whether you can trust it.
PKI protects you against this kind of attack by requiring that public keys are distributed as certificates. If you trust the CA that signed the certificate you can tell that the public key really does belong to the server and that it's safe to use it to encrypt your password. If you don't use a certificate -- or if you use an untrusted certificate -- you generally have no idea who you are sending your password to.
You don't give enough information about your own particular use case to say for certain whether you can use a self-signed certificate ... For example: It may be that you have one fixed certificate that is distributed in advance by some trusted channel and that you can check that the correct certificate is being used when you begin your SSL conversation. If that's the case then your client already knows that it has the correct public key and doesn't need to be able to check a signature. In general, though, you need a proper certificate signed by a trusted CA or else you have no security.
That's the entire point of trusted signing authorities - anything signed by someone else is supposed to give a security alert. So, no, there's no useful way to override this (unless you have control over the client computers - e.g. a self-signed certificate used for company-internal sites, when you can add your own CA into the clients' list), either for web browsers or anything else.
With a self-signed certificate, how can a user know whether the certificate is yours or an attacker's? He can't.
If you completely control both ends of the process (server and client), you can of course instruct the client to always trust "certificate from Eugene with a fingerprint of A01AABB546AC", for example, but then you need to build your own certificate infrastructure (expiration/revocation).
You will add no theoretical security by using a self-signed certificate, because of the possibility of man in the middle. The counterparts (your client and your server) in this communication will have no additional information about who is talking or listening, whereas the point of this kind of encryption is to make sure that there are only two participants in the communication and that the identity of at least one of them is known.
In your case, the password will not be transferred to you securely, because you don't know if it has passed through a third party on the way. Likewise, the user won't know who he sends the password to.
In practice, a man in the middle attack will be a bit of work to set up, and maybe that obstacle is some kind of security, but contrast that to the annoyance of forcing your users to accept a security warning with unclear consequences, and indeed the risk of "false sense of security".
There are companies that offer free certificates with the lowest form of validation (they will only check that you "own" the e-mail address hostmaster#domain). That way you won't have to do with the warning, either.
Unless there is a way for you to package your certificate or its fingerprint with the app, as Piskvor said.
Moved to answer - for this type of thing you should be fine. The only thing the users won't get is a way to confirm the trust level of the cert (like you could do with a signed cert in a browser for example) but as per your comment to #Piskvor that doesn't sound like an issue: you aren't using it for that.