I'm just developing a software with a java client and a php server. The java client generates the 2 RSA keys. The public key is sent via post request to the php server side. The server encrypts the response with the public key and sends it back to the java client. But I get this Error:
Exception in thread "main" org.bouncycastle.crypto.InvalidCipherTextException: unknown block type
at org.bouncycastle.crypto.encodings.PKCS1Encoding.decodeBlock(Unknown Source)
at org.bouncycastle.crypto.encodings.PKCS1Encoding.processBlock(Unknown Source)
As far as I got through googling, the public and private key don't match together.
The error comes up at this line: (Where the response should be decrypted)
byte[] encodedCipher = asymmetricBlockCipher.processBlock(messageBytes, 0, messageBytes.length);
So in fact of this I'm thinking there must be a problem with the Base64 encoding of the public key or with the transmission of it, but I couldn't find one. If I echo the encoded key on the server, it's the same like the encoded on the client. But I haven't found anything useful about the base64 encryption in the past hours.
For Base64 in java I use this 2 classes:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
Parts of my java client:
The keys are generated: (they are saved as fields)
From here: http://www.mysamplecode.com/2011/08/java-generate-rsa-key-pair-using-bouncy.html
private String publicKey;
private byte[] privateKey;
private void generateKeys() throws NoSuchProviderException, NoSuchAlgorithmException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder base64Encoder = new BASE64Encoder();
SecureRandom rnd = new FixedRand();
generator.initialize(2048, rnd);
KeyPair keyPair = generator.generateKeyPair();
this.publicKey = base64Encoder.encode(keyPair.getPublic().getEncoded()).replaceAll("(?:\\r\\n|\\n\\r|\\n|\\r)", "").trim();
this.privateKey = keyPair.getPrivate().getEncoded();
}
The Post request: (with apache http client)
private String readUrl(String urlString, String publicKey) throws Exception {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(urlString);
List<NameValuePair> nameValuePairs = new ArrayList<>(1);
nameValuePairs.add(new BasicNameValuePair("key", publicKey));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = "";
StringBuffer responseString = new StringBuffer();
while ((line = rd.readLine()) != null) {
responseString.append(line);
}
return responseString.toString();
}
The decryption part:
private String decrypt(String crypted) throws IOException, InvalidCipherTextException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
AsymmetricBlockCipher asymmetricBlockCipher = new RSAEngine();
asymmetricBlockCipher = new org.bouncycastle.crypto.encodings.PKCS1Encoding(asymmetricBlockCipher);
BASE64Decoder base64Decoder = new BASE64Decoder();
crypted = new String(base64Decoder.decodeBuffer(crypted));
AsymmetricKeyParameter asymmetricKeyParameter = PrivateKeyFactory.createKey(this.javaKeyToBouncycastle(privateKey));
asymmetricBlockCipher.init(false, asymmetricKeyParameter);
byte[] messageBytes = crypted.getBytes();
byte[] encodedCipher = asymmetricBlockCipher.processBlock(messageBytes, 0, messageBytes.length);
return new String(encodedCipher);
}
private PrivateKeyInfo javaKeyToBouncycastle(byte[] key) throws IOException {
ASN1InputStream pkstream = new ASN1InputStream(key);
return PrivateKeyInfo.getInstance(pkstream.readObject());
}
And part of my php server: (with phpseclib)
$key = trim(base64_decode($_POST['key'], true));
$rsa = new Crypt_RSA();
$rsa->loadKey($key);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$crypt = $rsa->encrypt("Hello World");
echo base64_encode($crypt);
Thank you :-) Unfortunately I don't know much about encryption atm and most of the code parts I found with google and customized them, but I'm working on it. (private project only)
Cipher). You should not usesuninternal classes like base 64, Java 8 has ajava.util.Base64class. Note that your code is not secure against man in the middle attacks; anybody may send you the public key. This is only secure over TLS to provide application level security (e.g. to store messages encrypted server side). - Maarten Bodewessunclasses? The next thing I'll add is a https connection, Java just have to accept my startssl certificate. But this doesn't have to be 100% secure, nevertheless would it be nice if it is. - jhnssunclasses in the JRE are internal implementation classes. They are not part of the official API. They may change or even disappear without warning. If you build against an "Execution Environment" in e.g. Eclipse they may even result in compilation errors. You can configure Java JSSE to accept custom key stores (with custom certificates) as well. Currently all an attacker has to do is to send the server his public key and it happily encrypts information for him. That's pretty far from secure. - Maarten Bodewes