I'm writing a bit of a net utility, and I want it to have three protocols. PLAIN_TEXT, which, obviously, sends plain text/bytes over the network. ENCRYPTED_UNSECURED, which uses encryption, but doesn't verify any identities, and ENCRYPTED_SECURED, which uses SSL or TSL.
Right now, I've implemented PLAIN_TEXT, and I've partially implemented ENCRYPTED_UNSECURED, but I've hit a block. I'm using SSLSockets for ENCRYPTED_UNSECURED, but with self-signed certs and a fake trust manager, however, the self-signed certs need to come from a key manager, which comes from a keyfile, generated by the java key tool.
What I want to know is: would using the same key file in every instance of the server/client subtract from the security of the encryption? As it is ENCRYPTED_UNSECURED, I don't care about the security of the authentication.
Note: this is being done in java.
EDIT: Why this is not a duplicate?
I'm not trying to import the certificate, I know how to do that, I want to know if using the same certificate could decrease the security of SSL/TSL's encryption protocol.
First, don't use actual SSL; SSLv2 was broken long ago (before 2000), and SSLv3 is now broken in most situations by POODLE (see Wikipedia and/or about a dozen questions on security.SE and crypto.SE). (Note the pseudoprotocol SSLv2Hello in Java/JSSE is not actually SSLv2 and is not a security vulnerability; OTOH since maybe 2010 it is very rarely needed or useful and nowadays more likely to cause confusion.)
Caveat: Java uses an "architecture" for cryptography that allows the same function(s) to be performed by different "providers" depending on how (by whom) your JRE is built and whether your installation has been modified. I will assume you use an Oracle (or formerly Sun) version of Java, or another version that uses the same cryptoproviders, which may include OpenJDK (which is mostly the same source as Oracle/Sun). In particular #Michal's answer seems to assume IBM although I see nothing in your question indicating this; IBM Java although otherwise compatible with Oracle/Sun Java has its own set of cryptoproviders and I don't know if any of the IBM differences affect the issues here.
Finally(!) to your question: distributing one privatekey (keystore) to numerous parties increases the risk of compromise at least proportionally to the number -- perhaps more because people are usually less careful about things that they perceive as someone else's responsibility.
TLS (like SSL before it) supports several different keyexchange options.
Ephemeral Diffie-Hellman in either classic (Zp) form DHE or elliptic-curve form ECDHE. These are usually preferred, and in particular Java/JSSE client orders them first (except for bugs on some early updates of Java7) because they provide Perfect Forward Secrecy; treating now as the beginning of the future -- as the saying goes 'today is the first day of the rest of your life' -- this also preserves confidentiality even if the server privatekey is already compromised, at least assuming the DHE/ECDHE implementation is correct.
Be sure not to use (Zp) DHE if the server runs on Java7 or earlier; those versions used 768-bit DH group which is too small. Java8 defaults to 1024, which is enough except perhaps against top adversaries like NSA, see details at http://weakdh.org; and can be configured for 2048 which is definitely enough, see
How to expand DH key size to 2048 in java 8
or several others. Note Java7 and up (but not 6 out of the box) support both DHE and ECDHE and prefer ECDHE, and Java ECDHE uses adequate curves, so in practice this should not be a problem.
(Plain) RSA. This is not secure if the privatekey is compromised, so in your situation you should if at all possible disable all plain-RSA ciphersuites. Ciphersuites of the form {TLS,SSL}_DHE_RSA_.. and TLS_ECDHE_RSA_... use RSA only for authentication not encryption; it is {SSL,TLS}_RSA_... that you must avoid.
Anonymous. TLS also supports keyexchange with DH or ECDH ephemeral keys but NO AUTHENTICATION (i.e. no certificate at all); these suites are called 'anonymous' instead of ephemeral and have the form {SSL/TLS}_{DH,ECDH}_anon_.... This is actually the exact technical match to your goal of providing confidentiality but not authentication, and needs NO keystore on the server or truststore on the client, plus avoids the hostname issue below. The possible downside is that anonymous suites are disabled by default, precisely because most users expect 'Secure Sockets' or 'Transport Security' to be secure, so you must enable them (at both ends); also if your organization (or your user's if different) has actually thought about a security policy, that thinking fairly often prohibits anonymous TLS for the same reason, and unlike some policy items this one can almost always be enforced by scans or monitors.
Others. TLS also supports nonephemeral aka static DH and ECDH, but practically no one uses them. TLS also supports non-PKC schemes using Kerberos, SRP, or just an arbitrary key (PSK), but these generally are appropriate only in special situations. Of these Java supports static-ECDH and Kerberos; using static-ECDH in your situation would be as insecure as plain-RSA, and IME even in environments where Kerberos is available using it with Java is about as much fun as hitting yourself in the head with hammers.
Hostname check: finally, for (non-anonymous) suites using a certificate, Java SSL/TLS client normally checks that the name used to connect to the server (either a domainname or an IP address) matches the certificate; the certificate must contain either
the name/address of the server, of which there can be more than one using SubjectAltNames aka SAN extension which is supported by keytool beginning in Java7 -- but a full list of names is difficult to arrange if you are going to use one cert on many servers especially ones you didn't identify in advance;
or a name with a 'wildcard' in the first component only: i.e. *.fred.example.com can be used for test1.fred.example.com test9999.fred.example.com anything.fred.example.com but NOT test.john.example.com anything.example.net or even fred.example.com (one level higher).
This check can be overridden, although the details vary some depending on whether you are using HTTPS or not and the Java version. Using anonymous suite(s) as above avoids the issue entirely.
Related
I recently joined a project that has an application running in Tomcat that uses a single file as both the KeyStore and the TrustStore. In other words, it includes both entries of types trustedCertEntry and PrivateKeyEntry.
While upgrading from Tomcat 8.5.6 to 8.5.20, I realized catalina.out was giving me
java.lang.IllegalArgumentException: java.security.KeyStoreException: Cannot store non-PrivateKeys
The solution was to make to remove the trustedCertEntry entries from the keystore.
To me, this seems fairly obvious that you would want to keep these separate. My question is, are there any possible security consequences to using the same file as a keystore and truststore? If so, why does Java (or SSL) allow these to be kept in the same file?
SSL and TLS are interoperable protocols; by IETF policy and tradition they say nothing about storage of anything and everything at either or any endpoint. "That's a local matter."
Java historically used one file format (JKS) for both TrustedCert's and PrivateKey's, not only for SSL/TLS but for all public-key crypto (and optionally with JCEKS some symmetric crypto also), and Java9 is switching to PKCS12 for both. Using the same format doesn't mean you must use the same file, and I would say it's preferable to use separate files, but I don't see an actual security problem in using a single file as long as you keep any file containing a privatekey restricted to one system, or as few systems as absolutely necessary, plus appropriate backup; however that's not really a programming Q and you might try for better answers on security.SX.
Tomcat 8.5 sorta-kinda combines the previously separate and (often confusingly) different config for Java-JSSE and APR=OpenSSL stacks, and I believe this restriction that the keystore can only contain PrivateKey's is a result of that change.
We have a Java desktop product that our customers (small businesses) use (among other things) to communicate with larger insurers via a few different SOAP protocols. (We are the SOAP client). The point here is that the insurers are the gorillas in the room - we just enable the communication between the insurers and our customers.
We use AXIS1 as our SOAP client library. Ordinarily is works perfectly and it has for years.
One major insurer is still using TLS1.0 for their SOAP server. We have no influence over this any more than the international space station affects the orbit of the earth.
Unfortunately (for us) the latest Java version 8u60 automatically disables TLS1.0.
see JDK-8076221 : Disable RC4 cipher suites
at http://bugs.java.com/view_bug.do?bug_id=8076221
So now we have customers who cannot connect via 8u60. We can revert them to 8u51, but that is short term at best.
JDK-8076221 gives a few clues on how to re-enable TLS1.0 as follows ...
These cipher suites can be reactivated by removing "RC4" form
"jdk.tls.disabledAlgorithms" security property in the java.security
file or by dynamically calling Security.setProperty(), and also
readding them to the enabled ciphersuite list using the
SSLSocket/SSLEngine.setEnabledCipherSuites() methods.
Unfortunately for someone like me (who has relied on the security layer being abstracted away) this is not enough information.
Comments
TLS level control is not my thing - we have relied on AXIS etc to do
all of that behind the scenes so there is a large body of knowledge
that is not familiar to me.
We have no control on the insurer using a TLS1.0 interface.
If we don't get a work around here our customers will simply be forced to use other products
that will use TLS1.0 - so we can't play hard ball and save anyone
here. They will be using TLS1.0 until the insurer decides otherwise.
A dynamic (code based) solution is preferred to any command line solution because we are a desktop application that will find command line deployment extremely problematic.
Can anyone provide some more detailed clues on how to programatically enable TLS1.0 in Java 8u60?
Perhaps something like ...
Security.setProperty("jdk.tls.disabledAlgorithms", "SSLv3");
SSLContext sslCtx = SSLContext.getInstance("TLS");
SSLSocket.setEnabledCipherSuites("please help me!");
SSLEngine.setEnabledCipherSuites("please help me!");
Many thanks for your time,
-Damian
Check http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html for algorithm names.
TLS 1.0 is matched by "TLSv1" (section SSLContext Algorithms), hence something similar to the following will enable TLS 1.0 (note that this applies for the instance of SSLEngine returned by createEngine()).
SSLContext.getDefault().createSSLEngine().setEnabledCipherSuites(new String[] {"TLSv1"});
For enabling a cipher suite you must overwrite the current value with something differently. You code disables SSLv3 which is already disabled. Instead you would need something similar to
Security.setProperty("jdk.tls.disabledAlgorithms", "");
However before doing that check how these properties actually work. It would expect the Security property to contain the names of ciphersuites for example as comma separated list. So you should do something like
String disabledAlgorithms = Security.getProperty("jdk.tls.disabledAlgorithms");
Security.setProperty("jdk.tls.disabledAlgorithms", disabledAlgorithms .replace("RC4,", ""));
I have SSL working between two Android devices running the same app using a self-signed cert and key generated using openssl and stored in keystores.
The problem is that the private keystore must be embedded in the app package somehow, and therefore becomes available to any attacker. I believe this would allow an attacker to snoop on the session and decrypt the data between the two phones.
I'm not using or requiring any of the other features of PKI, I'm just providing two keystores because the SSL connection setup requires them.
Is there a secure SSL cipher that does not need predefined PKI and generates its own keys on the fly at runtime?
I have investigated generating my own keys at runtime - creating the keys is easily done in Java but the KeyStore.setEntry() requires an an X509 certificate chain not just the public key, and Android does not contain the JCE code to generate the X509. I can do that by including the BouncyCastle (Android compatible version is called SpongyCastle) library but that adds quite an overhead to my app package size.
There is no access to a third-party trust server on the internet, the two phones could be on a private WLAN with no internet access.
As a nice-to-have bonus I'd like to be able to trust that the app is communicating with itself, not someone sniffing the protocol from a PC, but I don't think that's going to be possible as the app package contents will always be available.
To ensure you are talking to something/someone you trust, you need a mechanism of authenticating the other party. I'm not aware of a way to achieve this without a piece of data remaining secret:
Asymmetric authentication (i.e. your current implementation) requires the private key data to remain private.
Symmetric authentication requires that the shared secret remains private.
In the future, TrustZone will allow applications to store secret data in the secure element of the handset. Until that time, however, you are always at risk of malware on your devices. Adding a password to your keystore (that the user knows, not the app) might add an additional hurdle to an attacker, however once the phone is infected then the password can be snooped.
To minimise your risk profile you should produce per-device keys, rather than a single cert/key-pair combo that you incorporate into your application. This will, of course, increase the effort required to add new users as some form of registration will be required (e.g. certifying their key). Alternatively you can push the problem out to your users and have them decide who to trust, PGP-style.
I need to make two java proceses on the same host to communicate securely. I do not need to authenticate the processes so I don't want to use certificates.
I want to generate a random key in the server and client, exchange the keys between the processes using Elgamal; establish common symmetric key across the processes; and then communicate securely.
As far as I can think of, this can be done by implementing RMIServerSocketFactory and RMIClientSocketFactory interfaces to establish symmetric key as discussed above.
Is there already an implementation to do that?
Or is there a way to configure SslRMIServerSocketFactory and SslRMIClientSocketFactory to start using ElGamal as the key exchange protocol
ElGamal is preferred choice over RSA as ElGamal will generate random symmetric keys for each handshake while RSA will generate static keys every time.
I need to make two java proceses on the same host to communicate
securely. I do not need to authenticate the processes so I don't want
to use certificates.
Your reasoning is flawed from the start unfortunately. However "hardened" a communication channel is, you'll always want to make sure you're communicating with the intended party if you want to exchange data secretly. Authentication in one form or another is necessary to do so.
In theory, you can do away with certificates and use PSK cipher suites (which would effectively include the authentication step). This isn't supported by default with the Oracle/OpenJDK JRE. In addition, if you're working on the assumption that your certificate's private key would be compromised (as suggested by your other question), the same problem could happen with the pre-shared keys anyway.
With Adobe Reader you can sign a document locally. Is it theorically more secure than if the document was transported to the server and signed on the server (not especially using adobe technology) ? Could the user contest that the document could have been tampered later on if done on server ? How to prove him wrong technically that it is impossible even when signed on server - for legal issue to be taken into account.
Are you living in the EU? I can describe the situation here. The legal aspects of signatures are sort of regulated by Directive 1999/93/EC. There will be an updated version of this, so there will be some changes in the details, but generally the Directive does distinguish between server-based signatures and signatures made by an individual locally, having sole control over the process.
Being in sole control of a local process has a lot of security advantages, among them:
Using what the Directive calls a Secure Signature Creation Device (SSCD), such as a smart card. Using a tamper-proof device is definitely considered an advantage, although it can still be exploited when the attacker is e.g. in control of the computer/OS the SSCD is attached to.
The "What you see is what you sign" principle that was vaguely described in the Directive. Ideally, you should be able to view the data you are about to sign on a trustworthy device. This is impossible to guarantee with server-side signatures.
Key escrow. If the server signs, the key is most likely also stored on the server. And it's very, very hard to implement a solution where a key is on the server where only clients may access it, it's much more often the case that you need to trust the party operating the server.
That said, it is possible to secure the transport from client to server using e.g. TLS and still having a reasonably secure service. But pertaining the law (at least in the EU), the notion of a "non-repudiation" signature, a signature which is meant to be issued by an individual person, is only possible in the context of "local signatures". Accredited CAs here won't issue non-repudiation certificates to legal entities for example, such a certificate will only be issued to a real person, typically on an SSCD.
The downside of SSCDs has been that it is very hard to roll out large-scale deployments of software that would make use of them, especially across company/state boundaries because there are still a lot of interoperability issues with the myriads of hardware, the cost and the plain and simple fact that it's just less convenient.
Anything could happen to the document on its way to the server. The connection could be MitM attacked, the server could have tampered with, etc. etc.
The #1 rule in crypto signatures is, that it must happen on a trusted machine in a trusted environment, preferrably before it even reaches a connected machine (i.e. offline, then transferred on a offline medium).
So in short: It should be signed on the client and nowhere else.
If the signature is made by the user (human operator), then it's a question of trust to the server where the key resides.
Normally "signature is made by the user on behalf on the user" means that the user owns the key which is used for signing. In this case it makes little sense to put the private key on the server. And if you need this scheme, then either the signature is made not by the owner or the signature is made not on behalf of the individual making it.
But technically signing the data on the server (as you describe) is possible and in properly implemented architecture the user should be able to get the signed copy back and manually validate the signed document to ensure that this is what he (or the server on his behalf) intended to sign.
Another question is whether the server doesn't (intentionally or due to security breach) use the private key of the user to sign anything besides the requested documents. This is extremely hard to ensure unless you have a server specifically crafted for exactly one operation (signing of something) and in this case you would probably deal with specialized hardware device (such as one offered by SafeNet), not with a generic Windows/Linux/... server operating system.
We have a distributed cryptography module in our SecureBlackbox product, which implements similar scheme to what you describe, but roles are usually reversed: the user possesses the key and uses it to locally sign the document which resides on the server and is not transferred to the client. In that module we use TLS to ensure security of the channel and signing is performed on the computer of the user, so the private key remains strictly secret. However, the scheme you describe is also possible to implement using that module.