I'm wondering how I should approach a homework assignment.
I must develop a simple server and client in Java, which I have done. Next, I must encrypt the traffic between this server and client by using RSA with the following given keys.
Client Public Key: (5, 10142789312725007)
Client Private Key: (8114231289041741, 10142789312725007)
Server Public Key: (5, 10142701089716483)
Server Private Key: (6085620532976717, 10142701089716483)
Does Java have built-in functionality that will allow me to do this easily?
Thanks!
Yes, Java has built-in support for RSA that would let you do this easily.
It also has support for arbitrary-precision integers, including a modPow operation that make it pretty easy for you to implement RSA encryption yourself.
Honestly, simply implementing RSA using BigInteger is probably easier than learning enough about the Java Cryptography Architecture to use the built-in RSA implementation.
Here are some links that might help:
A Java implementation of RSA
Java Class - RSA Implementation
Generate an N-bit public and private
RSA key and use to encrypt and decrypt
a random message.
Using RSA encryption with Java - tutorial
You should be able to use the Java Cryptographic Extension that comes with Java.
For making the given numbers an RSA key convert them to BigInteger and "feed" them into a RSAPrivateKeySpec resp. RSAPublicKeySpec.
Using a KeyFactory you can then make the *Spec to regular Public and private keys that can be used with all cryptographic functions within Java.
Related
Preface: I don't know whether it's more appropriate to ask this question here or on the Crypto site. Feel free to move or delete or whatever the appropriate SE action is.
I've been asked to help update some encryption software. Broadly speaking, the software already does the following steps, none of which are particularly unusual. I've left out the error handling and Provider arguments for simplicity of posting:
1) Generates a random symmetric secret key for use with AES-128 (or AES-256, mutatis mutandis):
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init (128, a_SecureRandom_instance);
SecretKey sessionKey = keygen.generateKey();
2) Wraps the symmetric secret key, depending on whether the user is using...
2a) ...an RSA public key from a keypair:
// OAEP wasn't used in this software for hysterical raisins
Cipher wrapper = Cipher.getInstance("RSA/ECB/PKCS1Padding");
wrapper.init (Cipher.WRAP_MODE, user_RSA_PublicKey);
2b) ...a passphrase:
SecretKey stretched = ...passphrase stretched through a PBKDF like bcrypt...;
// I don't remember whether it's specified as "AES" or "AESWrap" here
Cipher wrapper = Cipher.getInstance("AES or AESWrap/ECB/NoPadding");
wrapper.init (Cipher.WRAP_MODE, stretched);
2c) Either route, the session key is wrapped:
byte[] wrapped = wrapper.wrap(sessionKey);
3) The session key is used to create a Cipher using Cipher.ENCRYPT_MODE along with a random IV, and then shedloads of data are run through it. That part is pretty standard but I can post it if you really want to see CipherInputStream usage. The wrapped session key is stored along with the encrypted data and a bunch of HMACs of everything under the sun.
Later at decryption time, the user provides either the RSA private key, or the passphrase for stretching; the software unwraps the symmetric key and decrypts the data.
All of that has been working for some time. But of course RSA keypairs are getting big and slow, so they'd like to support an additional possibility for step #2 above, in which the public keys are generated using an elliptic curve algorithm (P384 ECDH is the usual case). And this is where we're getting confused.
There doesn't seem to be a JCE replacement algorithm/transformation for elliptic curves in the Cipher wrapper = Cipher.getInstance("RSA/ECB/PKCS1Padding") invocation. The only one listed in the Java documentation is "ECIES", which seems(?) to be aimed more towards multiple party key agreement?
All of the APIs that I can find for the Java builtin JCE, or even looking over Bouncy Castle, only mention ECDH keys in the context of key agreement and transport, where they're used to generate a symmetric secret key instead of wrapping an existing one.
I feel we're missing something here, possibly because of poor assumptions. Is Cipher.wrap() genuinely not an option with ECDH keys? Or it is, but we need to do something funky in order to create the ECIES Cipher instance?
RSA is an algorithm (or depending on how you look at it, two very similar but distinct algorithms) for encryption and signature. RSA encryption can encrypt dat which is a (lower-level) key, in which case it is called wrapping.
DH is a key agreement algorithm, in both its classical (aka integer, Zp, modp, or finite-field/FF) form and its elliptic-curve form (ECDH). Note that at least for classic DH the raw agreement value g^a^b mod n = g^b^a mod n has enough mathematical structure people aren't comfortable using it directly as a key, so we run it through a key derivation function, abbreviated KDF. It's not clear to me that ECDH values really need KDF, at least for the commonly used X9/NIST curves including P384 (you might look or ask on crypto.SX if you care), but using a KDF is cheap and well-established so we do so.
I've never heard anyone competent call (EC)DH key transport, because it is specifically NOT. It is included in key exchange, a term created to cover the (useful) union of transport and agreement.
You can create an encryption scheme using (EC)DH by using the agreed (and derived) value as the key for symmetric encryption -- and that is (EC)IES. I don't know what made you think IES is something else. Modern practice is that the symmetric encryption should be authenticated, and the standardized forms of IES as a separate scheme use CBC encryption with HMAC authentication, although you could validly design a scheme that uses e.g. GCM instead.
As you saw, the BouncyCastle provider provides implementations of DH (classic) or EC IES "with{AES,DESEDE}-CBC" (in versions before 1.56 the -CBC was sometimes omitted) which use KDF2 from P1363a with SHA1, the indicated CBC cipher, and HMAC-SHA1. The 'standard' (Sun/Oracle/Open) providers do not, but you can combine the raw agreement operation, the KDF, plus symmetric encryption and MAC to produce the same result.
This is similar (though different in some details) to the operation of CMS-formerly-PKCS7 with classic DH and ECDH and PGP with ECDH. (PGP does not support classic DH; it used ElGamal encryption instead as the alternative to RSA.) Not to mention TLS (sometimes through 1.2, always in 1.3) and SSH which use either classic or EC DH 'exchange' (here interactive, and usually ephemeral-ephemeral instead of having at least the receiver static) to produce a secret which is derived and used as key material for symmetric encryption with authentication of the data.
I would like to use asymmetric encryption based with a private key based on a password. The requirement is the security level provided must be the same of (1) using password-based symmetric encryption, (2) using asymmetric encryption in a "regular" way.
I will have to use it in Java, but the answer can be generic.
I am aware that I can generate a keypair and encrypt the privateKey with a password-based symmetric key, however, in this way I will need a server (or other storage) to store this encrypted key. I would like to avoid that. If I could generate a keypair from a password, it is not needed.
Any suggestions?
If you base the private key solely on a password, it will only be as strong as the password, i.e. whoever can guess the password can get the private key.
This is comparable to generating a private/public key pair, encrypting the private key with a symmetric cipher and then publishing both together.
This of course makes the whole system weaker, since you no longer need to have the secret token - you only need to know the password.
Every time I see this question or a variant of it asked it's usually the result of bad design decisions. Almost always, the correct answer is to generate a random RSA keypair and protect the private key using standard password-based encryption like PBKDF2 or argon2. I've only seen one use case where this made at least a little sense and that was a cryptographic token back in the day with absolutely no nonvolatile storage. You won't find it around because there's no reason to build such a token, nonvolatile storage is not exotic in 2018.
In general you can do this: java's RSA key generation code accepts an instance of SecureRandom which the Oracle providers use to generate the candidate primes for RSA. You can subclass SecureRandom (I think) to provide a class that uses the password to seed a deterministic, repeatable sequence of random numbers such that every time you call KeyPairGenerator. generateKeyPair() the same keypair (including the private key) is generated. The bouncycastle library includes an example, FixedSecureRandom, that can be used as a model. Note that the deterministic RNG still has to be cryptographically secure, apart from the fact that it will not have enough entropy. FixedSecureRandom is not secure, it simply returns the pre-supplied bytes directly as output. Perhaps you can merge FixedSecureRandom with one of the other CSPRNGs in the org.bouncycastle.crypto.prng package.
Would it make sense to encrypt the RSA keys of an encryption program I am writing with the java random number generator and a password as a seed? How secure would that make them?
It may make your RSA keys safer than you could expect. If the random number generator ever changes or if the password is only added as a seed then you lose the encrypted data.
You can always encrypt private keys with a password, but you should be using a known password based key derivation function (PBKDF) such as PBKDF2. PBKDF2 is specified in RFC 2898: PKCS #5: Password-Based Cryptography Specification v2.0.
So yes, encrypting a private key can provide a layer of protection. It should however not be performed with a random number generator. A well known protocol that encrypts the private key using a password is PGP. Password encryption can also be performed for the PKCS#12 container format (which actually refers back to PKCS#5).
How security it is depends on the system. A weak password will certainly hurt security though.
I am trying to decrypt an encrypted message that is encrypted in a Ruby web app using the encrypted_strings RubyGem library.
The encryption client code looks like this:
cipher = EncryptedStrings::SymmetricCipher.new(:passphrase => "abcdefgh"*2)
cipher.encrypt("howdy")
=> "jEUQrH58Ulk=\n"
The default symmetric cipher algorithm appears to be DES-EDE3-CBC (although the documentation for the RubyGem disagrees, but I will go with what the code says). So on the Java side I tried the following which I found online as an example of DES-EDE3-CBC usage of the Java Cryptography API:
import javax.crypto.spec.DESedeKeySpec
import javax.crypto.spec.IvParameterSpec
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
...
DESedeKeySpec k;
Cipher c;
...
k = new DESedeKeySpec("abcdefghabcdefgh".getBytes());
c = Cipher.getInstance("DESede/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, k);
decrypted = c.doFinal("jEUQrH58Ulk=\n".getBytes());
When I do this on the Java side I get the following:
Wrong key size
I also tried using the Java Crypto API with an initialization vector but didn't know what I should set the bytes to since I am not doing this on the Ruby side via the encrypted_strings library and it appears to be set in the C code interfacing with OpenSSL.
Any pointers would be much appreciated.
I am using the bouncy castle JCA provider. I also tried DES/ECB/PKCS5Padding (which corresponds with the documented default algorithm in the RubyGems documentation eventhough the code appeared to be referencing the previously mentioned algorithm, DES-EDE3-CBC).
I have tried reading around the Java crypto API, but the documents all seem to have the same code samples and not very many new clues. My sources include:
http://www.angelfire.com/tx4/cus/notes/javaxcrypto.html
http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html
I'll post it as an answer. With DESede you can use either 192 bit DES ABC keys or 128 bit ABA keys. Many versions of Java only accept 192 bit (24 byte) DES ABC keys. DES ABA keys is where the first and last key of the encrypt, decrypt, encrypt (EDE) operation is the same; in other words, it's the same as DESede with ABC, where C=A.
So to create such a key, you can copy the first 8 bytes (in your case 8 ASCII characters - using characters directly as key is wrong) of your key and concatenate them at the end. This would result in "abcdefghabcdefghabcdefgh".getBytes("ASCII"). Note that you should always indicate the character encoding, as the platform might as well use UTF-16 as default character encoding, resulting in a key of double the size.
What's happening in this line of code ?
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
I specially don't understand getInstance("PBKDF2WithHmacSHA1") part
This funky looking string defines the secret-key algorithm to be used. It is:
PBKDF2WithHmacSHA1
PBKDF2 With Hmac SHA1
the PBKDF2 function (from PKCS#5 version 2.0)
which will be using SHA-1 HMAC for its pseudo-random number generator
References:
We find similar algorithm names in Java Crypto Extension Guide Appending A, somehow PKCS5 version 2 may not have been available/documented then (or indeed as suggested by brianegge, may be a matter of export restriction, a common issue with cryptographic items).
The algorithm name does show up in RFC3962 (AES for Kerberos) which may not be the very application you have in mind, but defined, all the same)
Different distributions of Java contain different crypto. This is due to export restrictions and patents. The line of code is requesting a factory which can create that type of key.
Specifically, PBKDF2WithHmacSHA1 constructs secret keys using the Password-Based Key Derivation Function function found in PKCS5 v2.0.
"PBKDF2" is a function defined in PKCS #5 used to derive key material from a password.
PBKDF2 requires a pseudo-random function, and in this case, a message authentication code based on the SHA-1 hash is used—"HmacSHA1".
So, this line is creating a factory. The factory might produce SecretKey objects that can be used to key a Cipher instance for a symmetric encryption algorithm or a Mac algorithm. Or, it can be used to make a "transparent" specification of an existing SecretKey.
One important thing to note about PBKDF2 is that it doesn't produce secret keys for any particular algorithm. It's a deterministic way to generate key "material" from a seed (a password), in such a way that the seed cannot be recovered from the generated key. Once the required number of bytes are generated, they are usually wrapped in a SecretKeySpec with the correct algorithm name.
You can see other standard names for secret key factories in the Java Crypto Architecture Standard Names documentation.