A simple question but trick for me to know how to do it.
I received a Public Key from the client and I'd like to know which Standard this public key is following.
I.e: PKCS#1, PKCS#2, PKCS#3 and so on.
Why do I need it?
Because I want validate a signature given that I already have the signature and the text encrypted.
Most PKCS don't involve public keys at all. All are listed in wikipedia.
PKCS1 defines ASN.1 formats for publickey and privatekey for RSA (only). These are pretty much the only keys described as conforming to any PKCS.
PKCS2-4 and 6 no longer exist, although OpenSSL has caused the PKCS3 format for DH to persist -- but DH is not used for signature.
PKCS5 doesn't involve publickey.
PKCS7 can use publickeys for encryption and/or signature verification of a message and/or can convey one or more X.509 certificate(s) (each) containing a publickey. Such certificate(s) can be for any algorithm.
PKCS8 doesn't involve publickey.
PKCS9 doesn't directly involve any key, although some attributes can be used in an X.509 certificate for a publickey.
PKCS10 is used to request an X.509 certificate, and represents a publickey in the same way as that certificate, which can be for any algorithm.
PKCS11 is an interface; although many types of keys can be transferred over it, those keys are not 'PKCS11' keys.
PKSC12 in practice contains a publickey only as an X.509 certificate, for any algorithm.
So if a key is described as 'PKCS' it's almost certainly PKCS1 and thus RSA. But many publickey algorithms now exist and are more or less widely used (DSA/DH/MQV/EG some, ECDSA/ECDH/ECIES and EdDSA/X a lot, GOST a little, postquantum little but growing) that are not covered by any PKCS.
Java has a mode called RSA/ECB/OAEPWithSHA-256AndMGF1Padding. What does that even mean?
RFC3447, Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1, section 7.1.2 Decryption operation says Hash and MGF are both options for RSAES-OAEP-DECRYPT. MGF is it's own function, defined in Section B.2.1 MGF1 and that has it's own Hash "option" as well.
Maybe the Hash "option" in RSAES-OAEP-DECRYPT and MGF1 are supposed to be the same or maybe they're not, it is unclear to me. If they are then I guess when you have RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING that means sha256 should be used for both. But if they're not supposed to be the same then you could have sha256 used for RSAES-OAEP-DECRYPT and, for example, sha1 used for MGF1. And if that's the case then what function is sha256 supposed to be used for? And what hash algorithm is supposed to be used for the other function?
And what does ECB mean in this context? ECB is a symmetric block cipher mode. Electronic Code Book. Maybe it's supposed to mean how Java deals with plaintext's that are larger than the modulo? Like maybe splits the plaintext into chunks that are as big as the modulo and then encrypts each one with RSA and concatenates them together? I'm just guessing..
The default for OAEP is to use SHA-1 for MGF1 (but see the edit on the end of this answer). Note that the hash chosen doesn't have that much impact on the security of OAEP, so mostly it will be left to this default.
We can easily test this by testing it against "OAEPPadding" and OAEPParameterSpec:
// --- we need a key pair to test encryption/decryption
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024); // speedy generation, but not secure anymore
KeyPair kp = kpg.generateKeyPair();
RSAPublicKey pubkey = (RSAPublicKey) kp.getPublic();
RSAPrivateKey privkey = (RSAPrivateKey) kp.getPrivate();
// --- encrypt given algorithm string
Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
oaepFromAlgo.init(Cipher.ENCRYPT_MODE, pubkey);
byte[] ct = oaepFromAlgo.doFinal("owlstead".getBytes(StandardCharsets.UTF_8));
// --- decrypt given OAEPParameterSpec
Cipher oaepFromInit = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSpecified.DEFAULT);
oaepFromInit.init(Cipher.DECRYPT_MODE, privkey, oaepParams);
byte[] pt = oaepFromInit.doFinal(ct);
System.out.println(new String(pt, StandardCharsets.UTF_8));
The code will fail with a padding related exception if you substitute "SHA-256" for the MGF1 as parameter.
The reason why the extended algorithm is needed at all is compatibility with other Cipher algorithms. Code written for e.g. "RSA/ECB/PKCS1Padding" doesn't use any parameters, let alone OAEP parameters. So without the longer string OAEP cannot function as drop in replacement.
The mode of operation "ECB" doesn't mean anything in this context, it should have been "None" or it should have been left out completely. You can only encrypt a single block using the RSA implementation of the SunRSA provider.
If you want to encrypt more data, create a random (AES) symmetric key and encrypt that using OAEP. Then use the AES key to encrypt your specific data. This is called a hybrid cryptosystem as it uses both asymmetric and symmetric primitives to encrypt data.
Note that OAEP is not supported in JDK 7 (1.7) or earlier. OAEP is included in the implementation requirements for Java runtimes since Java 8:
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
Some protocols may require you to use SHA-256 or SHA-512 within the padding, as SHA-1 is being deprecated for most use - even if it is not directly vulnerable for this kind of purpose.
EDIT: this was written mostly with Java in mind. By now many other libraries seem to take a somewhat different approach and use the same hash for the (mostly empty) label and MGF1 - which does make more sense. If you have an invalid OAEP ciphertext you should first make sure that the right "default" is being used. It is impossible to wrong any library implementation for choosing their own default; in the end it is up to the protocol to define the hashes used. Unfortunately no mandatory default exists - which is especially a problem if protocol owners forget to fully specify a configuration for the algorithms.
Java 7 comes with SunEC, which provides ECDH and ECDSA operations. I was attempting to do basic EC operations (point addition, scalar multiplication).
I start with
ECParameterSpec p256 = NamedCurve.getECParameterSpec("secp256r1");
ECPoint generator = p256.getGenerator();
BigInteger scalar = new BigInteger("23");
But from there, I do not see the next step. There is no ECPoint.scalarMultiply() or ECPoint.add() or EllipticCurve.multiply().
Am I missing something, or is the answer just "you can't do it without a third-party library?"
You can't do it directly without a third-party library. I think the situation with elliptic curves in the JCE is basically analogous to that of RSA. The classes represents instances of various keys and encodings. You can go between encodings and key specs using KeyFactory, you can generate public and private keys using a KeyPairGenerator, etc. But just like there is no RSAPublicKey.exponentiate() there is also no ECPoint.add(). These things happen under the hood in the Signature, KeyAgreement, and Cipher classes.
I've been given a challenge and it has to do with testing a friend's encryption process.
It's a Diffie-Hellman exchange process, and here are the known variables / constants:
P, G
my generated private key (variable)
my generated public key(variable)
the recipients public key (constant).
When looking at my private key - P and G are both within it. For example, the first 'x' bytes seem to have no relation to anything, then the next 'y' bytes are P, the next two bytes are static, and the next 'z' bytes are G, the remainder are variable.
The process is to encrypt a file, and send it to a device, which will in turn decrypt it - my ideas of attack are this:
try to duplicate the secret shared key. The problem here is that is fine as long as I know my generated private key, at which case - I don't for the files he's given me.
Try to find the recipients private key. Here, I could brute force my way in - but would take forever unless I had some sort of supercomputer.
Are there any other options to look at when trying to attack this?
I probably should keep my mouth shut, but it is also an opportunity for those interested in Diffie-Hellman to learn something:
Simple implementation of Diffie-Hellman to generate the shared key is vulnerable to man-in-the-middle attacks. However, most implementation of DH tackle this issue properly by adding authentication between Alice and Bob.
If your implementation of DH allows declaring a new set of PQG, you could request the other peer to use a new weak set. If Bob does not verify the quality of this set, then it is vulnerable to attacks.
DH requires Alice to send X = g^x, if Bob does not check the quality of X, he is vulnerable, since the space of possible values of the secret key can significantly be reduced by Eve in the middle.
If your implementation does not remember compromised keys, they can be re-used by Eve.
If your implementation does not remember compromised certificates, they can be re-used by Eve.
If your implementation does not check certificates, Eve will have fun for sure.
Talking about javax.crypto.Cipher
I was trying to encrypt data using Cipher.getInstance("RSA/None/NoPadding", "BC") but I got the exception:
ArrayIndexOutOfBoundsException: too much data for RSA block
Looks like is something related to the "NoPadding", so, reading about padding, looks like CBC is the best approach to use here.
I found at google something about "RSA/CBC/PKCS#7", what is this "PKCS#7"? And why its not listed on sun's standard algorithm names?
Update:
I'm wondering, if is a padding problem, why this example run just fine?
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
/**
* Basic RSA example.
*/
public class BaseRSAExample
{
public static void main(
String[] args)
throws Exception
{
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };
Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
// create the keys
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
new BigInteger("d46f473a2d746537de2056ae3092c451", 16),
new BigInteger("11", 16));
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(
new BigInteger("d46f473a2d746537de2056ae3092c451", 16),
new BigInteger("57791d5430d593164082036ad8b29fb1", 16));
RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec);
RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(privKeySpec);
// encryption step
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherText = cipher.doFinal(input);
// decryption step
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] plainText = cipher.doFinal(cipherText);
}
}
Update 2:
I realized that even if I use just Cipher.getInstance("RSA", "BC") it throws the same exception.
If you use a block cipher, you input must be an exact multiple of the block bit length.
In order to encipher arbitrary length data, you need first to pad you data to a multiple of the block length. This can be done with any method, but there are a number of standards. PKCS7 is one which is quite common, you can see an overview on the wikipedia article on padding.
Since block cipers operate on blocks, you also need to come up with a way of concatenating the encrypted blocks. This is very important, since naive techniques greatly reduce the strength of the encryption. There is also a wikipedia article on this.
What you did was to try to encrypt (or decrypt) data of a length which didn't match the block length of the cipher, and you also explicitly asked for no padding and also no chaining mode of operation.
Consequently the block cipher could not be applied to your data, and you got the reported exception.
UPDATE:
As a response to your update and GregS's remark, I would like to acknowledge that GregS was right (I did not know this about RSA), and elaborate a bit:
RSA does not operate on bits, it operates on integer numbers. In order to use RSA you therefore need to convert your string message into an integer m: 0 < m < n, where n is the modulus of the two distinct primes chosen in the generation process. The size of a key in the RSA algorithm typically refers to n. More details on this can be found on the wikipedia article on RSA.
The process of converting a string message to an integer, without loss (for instance truncating initial zeroes), the PKCS#1 standard is usually followed. This process also adds some other information for message integrity (a hash digest), semantical security (an IV) ed cetera. With this extra data, the maximum number of bytes which can be supplied to the RSA/None/PKCS1Padding is (keylength - 11). I do not know how PKCS#1 maps the input data to the output integer range, but
my impression is that it can take any length input less than or equal to keylength - 11 and produce a valid integer for the RSA encryption.
If you use no padding, your input will simply be interpreted as a number. Your example input, {0xbe, 0xef} will most probably be interpreted as {10111110 +o 11101111} = 1011111011101111_2 = 48879_10 = beef_16 (sic!). Since 0 < beef_16 < d46f473a2d746537de2056ae3092c451_16, your encryption will succeed. It should succeed with any number less than d46f473a2d746537de2056ae3092c451_16.
This is mentioned in the bouncycastle FAQ. They also state the following:
The RSA implementation that ships with
Bouncy Castle only allows the
encrypting of a single block of data.
The RSA algorithm is not suited to
streaming data and should not be used
that way. In a situation like this you
should encrypt the data using a
randomly generated key and a symmetric
cipher, after that you should encrypt
the randomly generated key using RSA,
and then send the encrypted data and
the encrypted random key to the other
end where they can reverse the process
(ie. decrypt the random key using
their RSA private key and then decrypt
the data).
RSA is a one-shot asymmetric encryption with constraints. It encrypts a single "message" in one go, but the message has to fit within rather tight limits based on the public key size. For a typical 1024 bit RSA key, the maximum input message length (with RSA as described in the PKCS#1 standard) is 117 bytes, no more. Also, with such a key, the encrypted message has length 128 bytes, regardless of the input message length. As a generic encryption mechanism, RSA is very inefficient, and wasteful of network bandwidth.
Symmetric encryption systems (e.g. AES or 3DES) are much more efficient, and they come with "chaining modes" which allow them to process input messages of arbitrary length. But they do not have the "asymmetric" property of RSA: with RSA, you can make the encryption key public without revealing the decryption key. That's the whole point of RSA. With symmetric encryption, whoever has the power to encrypt a message also has all the needed information to decrypt messages, hence you cannot make the encryption key public because it would make the decryption key public as well.
Thus it is customary to use an hybrid system, in which a (big) message is symmetrically encrypted (with, e.g., AES), using a symmetric key (which is an arbitrary short sequence of random bytes), and have that key encrypted with RSA. The receiving party then uses RSA decryption to recover that symmetric key, and then uses it to decrypt the message itself.
Beyond the rather simplistic description above, cryptographic systems, in particular hybrid systems, are clock full of little details which, if not taken care of, may make your application extremely weak against attackers. So it is best to use a protocol with an implementation which already handles all that hard work. PKCS#7 is such a protocol. Nowadays, it is standardized under the name of CMS. It is used in several places, e.g. at the heart of S/MIME (a standard for encrypting and signing emails). Another well-known protocol, meant for encrypting network traffic, is SSL (now standardized under the name of TLS, and often used in combination with HTTP as the famous "HTTPS" protocol).
Java contains an implementation of SSL (see javax.net.ssl). Java does not contain a CMS implementation (at least not in its API) but Bouncy Castle has some code for CMS.
This error indicates that the input data size is greater than the key modulus size. You will need a bigger key size to encrypt the data. If changing the key length is not an option, alternatively you may need to investigate if you are really expecting that big input data.
RSA can only be used to encrypt, when the number of bits being used to encrypt is greater then the size of the thing you are tying to encrypt + 11 bytes
Public-Key Cryptography Standards - PKCS
Scroll down a bit and you'll see it. It's not an cypher algortihm (like RSA) or a cypher mode like CBC, but a description of the way the certificate is encoded as bytes (i.e., a data structure syntax). You can find the spec for it here.
PKCS#7 is listed (referring to your link). It's encoding is PKCS7
Description
A PKCS#7 SignedData object, with the
only significant field being
certificates.
Use java.security.cert.CertificateFactory or CertPath when using PKCS7.
RSA is a block cipher. It encrypts block of the same key size.
Therefore, BouncyCastle RSA gives an exception if you try to encrypt a block
which is longer than the key size.
That's all I can tell you so far.
You should not encrypt your data using RSA directly. Encrypt your data with a random symmetric key (i.e. AES/CBC/PKCS5Padding) and encrypt the symmetric key using RSA/None/PKCS1Padding.