Authentication with public/private key signatures: What's a good message (digest)? - java

I'm build an application with a client/server infrastructure and would like to implement an authentication mechanism using the public/private key method.
Let's assume that a client owns the private key and the server only has the public key. During authentication the client signs a message with the private key, sends it to the server where it's validated with the public key. If validation succeeds the client is authenticated.
Here's some JUnit test code where I made myself familiar with the concepts:
#Test
public void testSignature() throws Exception {
final String message = "Hello world is a stupid message to be signed";
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
final Signature privSig = Signature.getInstance("SHA1withRSA");
privSig.initSign(keyPair.getPrivate());
privSig.update(message.getBytes());
byte[] signature = privSig.sign();
final Signature pubSig = Signature.getInstance("SHA1withRSA");
pubSig.initVerify(keyPair.getPublic());
pubSig.update(message.getBytes());
assertTrue(pubSig.verify(signature));
}
Of course in order for this to work both server and clients must be in possession of the plain message (digest).
Now my question is: What is a good message (digest) to be used for the signature? For example can this be a static, hardcoded string (which is used for all clients) or would this impose some kind of security issue on this concept? If a static string is bad would it be a good idea to negotiate some random string before authentication? This random string could be used as a "session" key for example and invalidated after some time.

The static String would be bad since its susceptible to a repeat attack (The signed string would be the same every time).
Although, you seem to be reinventing what has been done before. Using certificates is the trusted way. See this example for more info on it: Java HTTPS client certificate authentication
If you want to implement it yourself you'll probably need to read up on how SSL works and mimic that. Any other solution is likely to have some flaws in it, unless it was very bespoke (e.g. your client would keep a list of 1000's of shared static strings which it never reused, and the server had that same list and kept track of what had been used. or alternatively, keep track of a shared number which increments, as suggested in the comments below.)

Related

Storing verifaction code

I am building a web app which runs react on the front end and Java/Spring on the back end.
I am using a RESTful API to communicate with my client (client will communicate with an external API too).
I am facing a problem. When a user registers, I want to send an email verification code. My question is about practice.
Is it ok to send an email with a verification code, store the code in the database and then check if the code is correct?
Or is it better to create some static bean which would hold the code for a while and then check if it's correct?
I am not sure which is better in terms of back-end logic.
I appreciate all help.
It's a very common practice to store this in the DB. Just make sure to cleanup the expired tokens once in a while (maybe on timer, maybe when inserting new ones).
You can also store the tokens in an in-memory cache (EhCahe comes to mind) and set the expiration time accordingly, but this way you lose the cache if the application shuts down. Yes, you can make the cache persistent, but why not go the DB route then?
With that said, think about not storing the token anywhere and instead send the link with the email as an URL parameter and a salted hash as an additional parameter. Once the link is clicked, you can check if the hash matches and if it does, the email parameter wasn't tempered with, so you can mark it as validated. Maybe not Pentagon-level secure, but probably enough for email validation and makes everything easier.
In pseudo-code:
public String hash(String email) {
MessageDigest digester = MessageDigest.getInstance("SHA-256");
digester.update(email.getBytes(StandardCharsets.UTF_8)));
digester.update("RanDOmComplCatEdSalt647826583745".getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(digester.digest());
}
String email = "dude#example.com";
sendEmailWithLink("/verify?email=" + email + "&hash=" + hash(email));
On the way in, you just do the exact same thing to calculate and check if the hash matched:
String email = httpRequest.getParameter("email");
String receivedHash = httpRequest.getParameter("hash");
if (hash(email).equals(receivedHash)) {
//the user didn't do anything funny, mark email as valid
}
Or, taking this idea further but with more security, you can generate something like a JWT that contains the email and is properly encrypted.

How to secure the password used by TextEncryptor in spring boot

I need to encrypt some details for the users of my application (not the password, I am using bcrypt for that), I need to access the details at some point in the future, so i need to be able to decrypt these details, to do that, I have the following class in my spring boot application, my question is how to secure the password used to encrypt the text?
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
public class Crypto
{
final static String password = "How_to_Secure_This_Password?";
public static String encrypt(String textToEncrypt, String salt)
{
if (textToEncrypt != null && !textToEncrypt.isEmpty())
{
TextEncryptor encryptor = Encryptors.text(password, salt);
String encryptedText = encryptor.encrypt(textToEncrypt);
return encryptedText;
}
return null;
}
public static String decrypt(String encryptedText, String salt)
{
if(encryptedText != null && !encryptedText.isEmpty())
{
TextEncryptor decryptor = Encryptors.text(password, salt);
String decryptedText = decryptor.decrypt(encryptedText);
return decryptedText;
}
return null;
}
}
From my research so far I can suggest the following solutions:
1- Get the password from a properties file and use Spring Cloud Config for the encryption/decryption feature for the properties file (values prefixed with the string {cipher}), a good starting point is here. I don't like this solution as I don't need the client/sever config structure, and I don't feel good about using it for the sake of one feature only, I believe Spring framework should have similar feature.
2- Use Jasypt library, or its 'unofficial' support for spring boot from here. Again, not sure if the problem is a matter of encrypting this password in a properties file?
3- use the Vault which looks built for something similar to what I need here (API keys, secrets etc...) but it is too much overhead to build, maintain, and integrate ...
My argument here is that if an attacker was able to access my database machine/s then he is most likely will be able to access the application machine/s which means he may be able to revers-engineer the class and will be able to decrypt all the details which I want to secure! I feel confused here, what is best practice and the industry standard here?
The best solution so far is to use Spring Cloud Vault as I am already using spring boot, it can offer more than securing this password, in fact it can secure the password for many API keys, databases etc (it is RC release at the time of writing) .. however, I am not convinced yet that this is the ultimate solution as my application still need to authenticate against the Vault, but I have to say that this is done in a more advanced way and gives a one step further than keeping passwords in config files ...
The issue is chicken and egg problem, and it turns out that SO has so many many similar questions for similar scenarios (saving database password in config, hide it in code, hid password in PBE store etc etc).
This well explained by Mark Paluch in his getting started article
Encrypted data is one step better than unencrypted. Encryption imposes on the other side the need for decryption on the user side which requires a decryption key to be distributed. Now, where do you put the key? Is the key protected by a passphrase? Where do you put the passphrase? On how many systems do you distribute your key and the passphrase?
As you see, encryption introduces a chicken-egg problem. Storing a
decryption key gives the application the possibility to decrypt data.
It also allows an attack vector. Someone who is not authorized could
get access to the decryption key by having access to the machine. That
person can decrypt data which is decryptable by this key. The key is
static so a leaked key requires the change of keys. Data needs to be
re-encrypted and credentials need to be changed. It’s not possible to
discover such leakage with online measure because data can be
decrypted offline once it was obtained.
.......
Vault isn’t the answer for all security concern. It’s worth to check
the Vault Security Model documentation to get an idea of the threat
model.
Ironically enough, Vault storage backend needs to be configured with plain text passwords for most cases (MySQL,S3, Azure, ... I am not accepting this as an answer to my question yet, but this is what I have found so far, waiting for more input from fellow SO contributors with thanks!
Vault is a good solution, but one way to do it would be to provide the password manually when you initialise the component, so that it would be stored in memory and not typed in any config file.

Querying Views in Couchbase, Java Client

I'm using the 1.4.3 version of the java client and am attempting to connect to the Couchbase server I have running locally but I'm getting auth errors. After looking through the code (isn't open source great?) of how their client library is using the variables amongst their classes I've come to the conclusion that if I want to be able to connect to a "bucket" that I have to create a user for each "bucket" with the same user name as that bucket. This makes no sense to me. I have to be wrong. Aren't I? There has to be another way. What is that way?
For reference, here is what I'm using to create a connection (it's Scala but would look nearly identical in Java):
val cf = new CouchbaseConnectionFactoryBuilder()
.setViewTimeout(opTimeout)
.setViewWorkerSize(workerSize)
.setViewConnsPerNode(conPerNode)
.buildCouchbaseConnection(nodes, bucket, password)
new CouchbaseClient(cf)
which follows directly from their examples.
Their Code
If I look into the code in which they're connecting to the "view" itself I see the following:
public ViewConnection createViewConnection(
List<InetSocketAddress> addrs) throws IOException {
return new ViewConnection(this, addrs, bucket, pass);
}
which is then passed to a constructor:
public ViewConnection(final CouchbaseConnectionFactory cf,
final List<InetSocketAddress> seedAddrs, final String user,
final String password) //more code...
and that user variable is actually used in the HTTP Basic Auth to form the Authentication header. That user variable being, of course, equivalent to the bucket variable in the CouchbaseConnectionFactory.
You are correct - each bucket should be authenticated with the bucket name as the user. However, there aren't any users to 'create' - you're just using whatever (bucket) name and password you setup when you created the bucket on the Cluster UI.
Note that people usually use one bucket per application (don't think bucket == table, think bucket == database) and so you wouldn't typically need more than a couple of buckets for most applications.

Get AD Groups with kerberos ticket in Java

I am obtaining a kerberos ticket with the following code:
String client = "com.sun.security.jgss.krb5.initiate";
LoginContext lc = new LoginContext(client, new CallbackHandler() {
#Override
public void handle(Callback[] arg0) throws IOException, UnsupportedCallbackException {
System.out.println("CB: " + arg0);
}
});
lc.login();
System.out.println("SUBJ: " + lc.getSubject());
This code works fine, I get a subject that shows my user ID. The problem I'm having is now I need to know whether the user belongs to a certain group in AD. Is there a way to do this from here?
I've seen code to get user groups using LDAP but it requires logging in with a user/password, I need to do it the SSO way.
You cannot actually do this with the kind of ticket you get at login. The problem is that the Windows PAC (which contains the group membership information) is in the encrypted part of the ticket. Only the domain controller knows how to decrypt that initial ticket.
It is possible to do with a service ticket.
So, you could set up a keytab, use jgss to authenticate to yourself and then decrypt the ticket, find the PAC, decode the PAC and then process the SIDs. I wasn't able to find code for most of that in Java, although it is available in C. Take a look at this for how to decrypt the ticket.
Now, at this point you're talking about writing or finding an NDR decoder, reading all the specs about how the PAC and sids are put together, or porting the C code to Java.
My recommendation would be to take a different approach.
Instead, use Kerberos to sign into LDAP. Find an LDAP library that supports Java SASL and you should be able to use a Kerberos ticket to log in.
If your application wants to know the groups the user belongs to in order to populate menus and stuff like that, you can just log in as the user.
However, if you're going to decide what access the user has, don't log in as the user to gain access to LDAP. The problem is that with Kerberos, an attacker can cooperate with the user to impersonate the entire infrastructure to your application unless you confirm that your ticket comes from the infrastructure.
That is, because the user knows their password, and because that's the only secret your application knows about, the user can cooperate with someone to pretend to be the LDAP server and claim to have any access they want.
Instead, your application should have its own account to use when accessing LDAP. If you do that, you can just look up the group list.
I do realize this is all kind of complex.

Validate TLS server certificate with BouncyCastle's "lightweight" API

I'm using Java BouncyCastle so-called "lightweight" API to establish a TLS connection over a TCP socket.
I want to verify server-provided certificate chain being signed by one of trusted CAs. Sounds like a reasonably common task that every sane TLS client implementation out there does by default, so I expect this should be simple.
For a sake of simplifying the question, I don't ask about verifying anything other than the sign/trust chain, like matching hostname or checking expiration date. Such checks seem trivial to implement.
If I understand the documentation correctly, there's a TlsAuthentication interface, that users are supposed to implement. The sole provided implementation is LegacyTlsAuthentication, which adapts upon now-deprecated CertificateVerifyer interface, which has only AlwaysValidVerifyer implementation (that's just dummy "return true;" under the hood).
So, this is what I have for now:
DefaultTlsClient tlsClient = new DefaultTlsClient() {
#Override
public TlsAuthentication getAuthentication() throws IOException {
TlsAuthentication auth = new TlsAuthentication() {
#Override
public void notifyServerCertificate(Certificate serverCertificate) {
// Here I should validate certificate chain, but this far
// I only managed to print subjects for debugging purposes.
for (org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCerts()) {
System.out.println("Certificate: " + c.getSubject().toString());
}
}
#Override
public TlsCredentials getClientCredentials(CertificateRequest cr) throws IOException {
return null;
}
};
return auth;
}
};
socket = new Socket(hostname, port);
tlsHandler = new TlsProtocolHandler(socket.getInputStream(), socket.getOutputStream());
tlsHandler.connect(tlsClient);
However, I fail to understand or find any existing example that would check one org.bouncycastle.asn1.x509.Certificate for being correctly signed by another one. Could someone provide some pointers to me, please?
I'm using BounceCastle's proprietary API due to need to use ciphersuites that default Java installations do not allow due to being subject to US cryptographic policy jurisdiction restrictions. For example, AES256 encryption requires installing unlimited strength policy files, and I'd really like to avoid additional end-user installation steps, if possible.
The isSignatureValid method of the X509CertificateHolder class should work for you. This method takes in 1 parameter, a ContentVerifierProvider. You can create an X509CertificateHolder by passing a Certificate into the constructor.
The following code is taken from BC's version 2 API page and should give you a good idea in how to implement this in your solution.
ContentVerifierProvider contentVerifierProvider =
new BcRSAContentVerifierProviderBuilder(
new DefaultDigestAlgorithmIdentifierFinder()).build(lwPubKey);
if (!certHolder.isSignatureValid(contentVerifierProvider))
{
System.err.println("signature invalid");
}
"lwPubKey" is the public key of the signer. So depending on how long your certificate chain is, you would repeatedly call this method starting with the end entity certificate and going up the line to the self-signed root certificate.
Wow what a bunch of deprecated classes. I feel for ya.
Where you need to get to is
http://www.cs.berkeley.edu/~jonah/bc/org/bouncycastle/asn1/x509/KeyUsage.html#keyCertSign
The keyCertSign will be set for a CA issues cert.
You code above serverCertificate.getCerts() will return an array of X509CertificateStructure objects (your chain) for each of these you can call toASN1Object() to get the interface DerEncodable / KeyUsage
I hope this helps.

Categories