I am looking for a solution that meets the following requirements:
Let's assume, there are: the Application installed on a computing device and controlling it, Users that use this application, and Maintainers, that provide some support for the application. Application has the Configuration, for example in the file or database. Configuration is updated manually by Maintainers when required, for example weekly. Configuration contains, for example, list of emails, Application sends it's alerts to. Let's assume, that it is not possible for Users to modify the Application in any way. Although, Application is written in Java, so it is easy for Users to copy and debug it. Internally, the Application decrypts the Configuration in the Application's memory, in order to use the Configuration.
Users shall able to view the Configuration from inside the Application. Users shall be unable to change the Configuration, or to use their own (which is basically the same), for example to change any email or remove existing email or add a new one.
Additional requirement, that is not mandatory: It shall not be possible to directly view the Configuration without the Application. I understand it's hardly really possible, so, it shall be at least just difficult, like decryption necessary to view the Configuration without the Application.
Question: how to achieve this and is it possible at all?
Possible solutions I can realize, and attacks:
1) To use some signing. To sign each Configuration with some Digest and to check the Digest in the Application then. Attack: as I understand, App shall calculate the Digest using the public key stored in it. Then the Application shall compare calculated Digest with the one provided with the Configuration. So, attack is simple : Users will modify the Configuration, then debug the Application, put a breakpoint on the place where Application has already calculated Digest for comparing it with the stored one, then Users could dump the calculated Digest and replace provided Digest with this calculated one.
2) To use hybrid encryption. In this case the attack is the same: breakpoint in the place where decrypted symmetric key is available, dump this key, then to use it for the new Configuration encryption.
3) To use asymmetric encryption. Maintainers encrypt Configuration with the public key, then Application decrypts the Configuration with the private key. Attack is simple : Users could dump private key from the Application and derive a new public key, then use it for encryption.
Is there a solution, like "encrypt with the public key, then decrypt with private" for large chunks of data (up to 10 kb), or maybe any other possible way to achieve that?
Thank you
Colleagues,
I have an application that shall receive and store some read-only data. The data shall be available for users to read but not available to change. For example, assume that there are text files with data, that keep some texts available for end users to read but not available for edits. New files shall be periodically received by the application, and shall be available for users in the same read-only mode.
So, local content (for example files content) is accessible by users. Users can copy content from application, save decrypted copies and so on. The only thing I need to prevent, is to replace existing data with any other data, including changing the content or adding new by users. I do not need changes detection, I need to make changes impossible (ok, as hard as possible).
I suppose, that the easiest way to do that, is to encrypt data with some secret key and to include the public key in the app, so app could decrypt and show the data, but without the secret key users would not be able to change the content.
I know, that standard RSA supports just a small data blocks to encrypt, usually slightly less than the key length. (I made tests and found that for RSA 2048 Java throws an exception after 254 bytes) I also read, that it's not a good idea to split source data to chunks and to encrypt these chunks then. I read that it is advised to use symmetric key, like AES, for encryption and decryption, then to encrypt this AES key with RSA key pair.
I see a big (as I suppose) security risk in this scenario - as my app is written in Java, it's quite easy to debug it and to dump the decrypted AES key, then to use it for data modifications, even without any modifications of the application itself.
So, my question is: how to solve this problem and what is considered to be secure to use in such a case?
Thank you
Update:
Of course, users are able to copy the file and to use a copy as they want. The goal is, to disallow users to change data used by application, not a copy of these data. In case of asymmetric encryption it is easy to achieve - I encrypt data with my private key, pass to app, app in runtime decrypts data with it's public key and use. In case someone would like to change data, app would not decrypt the data properly and data will be spoiled and app would fail to work till data will be reverted back.
As #President James K. Polk stated in one of his comments the only solution in my humble opinion is to sign the read-only data and use it only if the data is verified. In your "Possible solution & attack" section you write that the program compares some Digits that can easily been overwritten. Usually the signature is done with the (SHA256-)
hash of the data, but you can sign the complete data without hashing it first and 4 KB of data does not bring performance issues on my desktop Java.
I setup a full working example that simulates the maintainer-side and the app-side and as a little goodie I encrypt the plaintext with AES CBC (key generated out of signature). I know that this mode of encryption is not the "best way" as the data does not need to be kept totally secret but not direct visible it's a good solution.
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Arrays;
import java.util.Random;
public class Cyptosystem {
public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
System.out.println("Cryptosystem for \nhttps://stackoverflow.com/questions/62361876/asymmetric-cryptographic-algorithm-for-large-text-data/62398723#62398723");
System.out.println("Warning: this program is experimental and has no proper exception handling");
byte[] plaintext = new byte[4000]; // content to get secured, provided by maintainers
byte[] ciphertext = new byte[0]; // encryped plaintext
byte[] dataForApp = new byte[0]; // initvector | ciphertext
new Random().nextBytes(plaintext);
// generate rsa keypair
System.out.println("generate the RSA keypair");
KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom random = new SecureRandom();
rsaGenerator.initialize(4096, random);
KeyPair rsaKeyPair = rsaGenerator.generateKeyPair();
PrivateKey rsaPrivateKey = rsaKeyPair.getPrivate(); // for signature
PublicKey rsaPublicKey = rsaKeyPair.getPublic(); // for verification, implemented in app resources
System.out.println("sign & encrypt the plaintext");
// signature done by maintainers
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(rsaPrivateKey);
sig.update(plaintext);
byte[] signature = sig.sign(); // provide to app as byte array, hexstring or base64 as you like
// encrypt plaintext with signature
byte[] initvector = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(initvector); // random initvector
// you can use another aes mode for encryption e.g. gcm
// you can use a hmac as key derivation ...
// i'm using sha256 to get a 32 byte long key
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] aeskey = md.digest(signature);
SecretKeySpec keySpec = new SecretKeySpec(aeskey, "AES");
IvParameterSpec ivKeySpec = new IvParameterSpec(initvector);
Cipher aesCipherEnc = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, ivKeySpec);
ciphertext = aesCipherEnc.doFinal(plaintext);
// copy iv | ciphertext
dataForApp = new byte[ciphertext.length + 16]; // initvector length 16 byte
System.arraycopy(initvector, 0, dataForApp, 0, initvector.length);
System.arraycopy(ciphertext, 0, dataForApp, initvector.length, ciphertext.length);
// send the dataForApp to the app (as byte array, hex string, base64 as you like
System.out.println("dataForApp length: " + dataForApp.length);
// app side, receive dataForApp & signature, already has public key
byte[] dataForAppApp = dataForApp.clone();
byte[] signatureApp = signature.clone();
System.out.println("decrypt and verify the signature");
// get initvector & ciphertext
byte[] initvectorApp = new byte[16];
byte[] ciphertextApp = new byte[(dataForAppApp.length - 16)];
System.arraycopy(dataForAppApp, 0, initvectorApp, 0, 16);
System.arraycopy(dataForAppApp,16, ciphertextApp, 0, (dataForAppApp.length - 16));
// decrypt data
MessageDigest mdApp = MessageDigest.getInstance("SHA-256");
byte[] aeskeyApp = md.digest(signature);
SecretKeySpec keySpecApp = new SecretKeySpec(aeskeyApp, "AES");
IvParameterSpec ivKeySpecApp = new IvParameterSpec(initvectorApp);
Cipher aesCipherDec = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aesCipherDec.init(Cipher.DECRYPT_MODE, keySpecApp, ivKeySpecApp);
byte[] decrypttext = aesCipherDec.doFinal(ciphertextApp);
System.out.println("plaintext equals decrypttext: " + Arrays.equals(decrypttext, plaintext));
// don't use the ciphertext as the signature is not verified
Signature sigApp = Signature.getInstance("SHA256withRSA");
sigApp.initVerify(rsaPublicKey);
sigApp.update(decrypttext);
boolean signatureVerified = sigApp.verify(signatureApp);
System.out.println("signatureApp verified: " + signatureVerified);
System.out.println("if verified == true we can use the decrypttext");
}
}
Firstly, don't roll your own crypto. Cryptography is very hard, and if you make any mistake, it will have vulnerabilities you could have avoided by using a well-established library to do the heavy lifting. You could, for example, use libsodium. It has many abstractions, and probably has a solution for what you need.
With that out of the way, let's discuss how that would make it safer: the user needs to be able to read the contents, but not edit it. What exactly do you mean by "cannot edit"? Can he not be able to modify anything locally, or just not be able to upload it to your server as if he was authorized to do so?
If the former, encryption can't help you much - you need to be able to decrypt it locally, so an attacker can always dump your process' memory to get to the data - sure it would be hard, but definitely possible. Just not allowing people to edit/save/download in your application would be the strongest guarantee you can get.
If the latter, then using authentication would be the way to go - be that a simple method like HTTP basic authentication with user and password, or signing the file to be uploaded. Dealing with authentication on your application's side would be the more practical way.
Asymmetric encryption is done with the Public Key and the decryption is performed with the Private Key. As the app has to be capable to decrypt the data the app needs to know the Private Key and that's the problem with the common used algorithms, because for RSA or ECIES the Public key can get derived from the Private key. Therefore it's not a real problem to derive the Public key and store changed/appended data after encryption with the Public key.
Second thing is - you did not specify how "large" your text will be - some KB, MB, GB?
Some months ago I tested some "new" algorithms that are "Post quantum safe" and as an example I used the McEliece Fujisaki algorithm that is available with the Bouncy Castle Crypto provider (I used version 1.65, bcprov-jdk15to18-165.jar).
The program creates a 50 MB large byte array that gets encrypted with the Public key and decrypted with the Private key.
At the moment I did not find any Public key deriving methods so you definitely need to know the Private and the Public key.
I did not test larger byte arrays because this parameter depends on the memory of the target system (you need the double memory
as the complete data is captured in ciphertextByte and then again in decryptedtextByte).
Edit June 16th 2020: President James K. Polk programmed a method that easily retrieves a public key from a given private key. The source is available in his GitHub-Repo (https://github.com/james-k-polk/McEliece/blob/master/McElieceRecoverPublicFromPrivate.java) and for later convenience shown at the end of this answer. So everyone that has access to a private McEliece key is been able to encrypt data with the retrieved public key! Thanks to President James for his help.
Here are the outputs on the console:
McEliece Fujisaki Pqc Encryption
key generation
PrivateKey length: 4268 algorithm: McEliece-CCA2 format: PKCS#8
PublicKey length: 103429 algorithm: McEliece-CCA2 format: X.509
initialize cipher for encryption
pt length: 52428800 (50 mb)
ct length: 52429056 (50 mb)
initialize cipher for decryption
dt length: 52428800 (50 mb)
compare plaintext <-> decryptedtext: true
class McElieceFujisakiPqcEncryptionLargeData.java
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.mceliece.*;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PrivateKey;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PublicKey;
import java.security.*;
import java.util.Arrays;
import java.util.Random;
public class McElieceFujisakiPqcEncryptionLargeData {
public static void main(String[] args) throws InvalidCipherTextException {
System.out.println("McEliece Fujisaki Pqc Encryption");
if (Security.getProvider("BCPQC") == null) {
Security.addProvider(new BouncyCastlePQCProvider());
// used Bouncy Castle: bcprov-jdk15to18-165.jar
}
System.out.println("key generation");
SecureRandom keyRandom = new SecureRandom();
McElieceCCA2Parameters params = new McElieceCCA2Parameters();
McElieceCCA2KeyPairGenerator mcElieceCCA2KeyGen = new McElieceCCA2KeyPairGenerator();
McElieceCCA2KeyGenerationParameters genParam = new McElieceCCA2KeyGenerationParameters(keyRandom, params);
mcElieceCCA2KeyGen.init(genParam);
AsymmetricCipherKeyPair pair = mcElieceCCA2KeyGen.generateKeyPair();
AsymmetricKeyParameter mcEliecePrivateKey = pair.getPrivate();
AsymmetricKeyParameter mcEliecePublicKey = pair.getPublic();
PrivateKey privateKey = new BCMcElieceCCA2PrivateKey((McElieceCCA2PrivateKeyParameters) pair.getPrivate()); // conversion neccessary only for key data
PublicKey publicKey = new BCMcElieceCCA2PublicKey((McElieceCCA2PublicKeyParameters) pair.getPublic()); // conversion neccessary only for key data
System.out.println("PrivateKey length: " + privateKey.getEncoded().length + " algorithm: " + privateKey.getAlgorithm() + " format: " + privateKey.getFormat());
System.out.println("PublicKey length: " + publicKey.getEncoded().length + " algorithm: " + publicKey.getAlgorithm() + " format: " + publicKey.getFormat());
// generate cipher for encryption
System.out.println("\ninitialize cipher for encryption");
ParametersWithRandom param = new ParametersWithRandom(mcEliecePublicKey, keyRandom);
McElieceFujisakiCipher mcElieceFujisakiDigestCipher = new McElieceFujisakiCipher();
mcElieceFujisakiDigestCipher.init(true, param);
// random plaintext
byte[] plaintext = new byte[52428800]; // 50 mb, 50 * 1024 * 1024
new Random().nextBytes(plaintext);
System.out.println("pt length: " + plaintext.length + " (" + (plaintext.length / (1024 * 1024)) + " mb)");
byte[] ciphertext = mcElieceFujisakiDigestCipher.messageEncrypt(plaintext);
System.out.println("ct length: " + ciphertext.length + " (" + (ciphertext.length / (1024 * 1024)) + " mb)");
System.out.println("\ninitialize cipher for decryption");
mcElieceFujisakiDigestCipher.init(false, mcEliecePrivateKey);
byte[] decryptedtext = mcElieceFujisakiDigestCipher.messageDecrypt(ciphertext);
System.out.println("dt length: " + decryptedtext.length + " (" + (decryptedtext.length / (1024 * 1024)) + " mb)");
System.out.println("\ncompare plaintext<-> decryptedtext: " + Arrays.equals(plaintext, decryptedtext));
}
}
Public key Retrieval class by President James K. Polk, available under MIT-Licence:
package com.github.jameskpolk;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.mceliece.*;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PrivateKey;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PublicKey;
import org.bouncycastle.pqc.math.linearalgebra.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
public class McElieceRecoverPublicFromPrivate {
private static final SecureRandom RAND = new SecureRandom();
public static AsymmetricCipherKeyPair generateKeyPair() {
McElieceCCA2KeyPairGenerator kpg = new McElieceCCA2KeyPairGenerator();
McElieceCCA2Parameters params = new McElieceCCA2Parameters();
McElieceCCA2KeyGenerationParameters genParam = new McElieceCCA2KeyGenerationParameters(RAND, params);
kpg.init(genParam);
return kpg.generateKeyPair();
}
public static McElieceCCA2PublicKeyParameters recoverPubFromPriv(McElieceCCA2PrivateKeyParameters priv) {
GF2mField field = priv.getField();
PolynomialGF2mSmallM gp = priv.getGoppaPoly();
GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
Permutation p = priv.getP();
GF2Matrix hp = (GF2Matrix) h.rightMultiply(p);
GF2Matrix sInv = hp.getLeftSubMatrix();
GF2Matrix s = (GF2Matrix) sInv.computeInverse();
GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp);
GF2Matrix m = shp.getRightSubMatrix();
GoppaCode.MaMaPe mmp = new GoppaCode.MaMaPe(sInv, m, p);
GF2Matrix shortH = mmp.getSecondMatrix();
GF2Matrix shortG = (GF2Matrix) shortH.computeTranspose();
// generate public key
return new McElieceCCA2PublicKeyParameters(
priv.getN(), gp.getDegree(), shortG,
priv.getDigest());
}
public static void main(String[] args) throws Exception{
// generate a McEliece key pair
AsymmetricCipherKeyPair bcKeyPair = generateKeyPair();
McElieceCCA2PrivateKeyParameters bcPriv = (McElieceCCA2PrivateKeyParameters) bcKeyPair.getPrivate();
BCMcElieceCCA2PrivateKey priv = new BCMcElieceCCA2PrivateKey(bcPriv);
// get the first public key
McElieceCCA2PublicKeyParameters bcPub1 = (McElieceCCA2PublicKeyParameters) bcKeyPair.getPublic();
BCMcElieceCCA2PublicKey pub1 = new BCMcElieceCCA2PublicKey(bcPub1);
// Now generate a second public key for the private key
McElieceCCA2PublicKeyParameters bcPub2 = recoverPubFromPriv(bcPriv);
BCMcElieceCCA2PublicKey pub2 = new BCMcElieceCCA2PublicKey(bcPub2);
// print some info about sizes
System.out.printf("Size of encrypted messages in bits(bytes): %d(%d)\n",
priv.getEncoded().length, priv.getEncoded().length / 8);
System.out.printf("private key length: %d\n", bcPriv.getK());
System.out.printf("public key1 length: %d\n", pub1.getEncoded().length);
System.out.printf("public key2 length: %d\n", pub2.getEncoded().length);
// now encrypt different messages with each public key.
String message1 = "Deposits should be made to account # 3.1415929";
String message2 = "Deposits should be made to account # 2.71828";
ParametersWithRandom params1 = new ParametersWithRandom(bcPub1, RAND);
ParametersWithRandom params2 = new ParametersWithRandom(bcPub2, RAND);
McElieceFujisakiCipher mcElieceFujisakiDigestCipher1 = new McElieceFujisakiCipher();
McElieceFujisakiCipher mcElieceFujisakiDigestCipher2 = new McElieceFujisakiCipher();
mcElieceFujisakiDigestCipher1.init(true, params1);
mcElieceFujisakiDigestCipher2.init(true, params2);
byte[] ciphertext1 = mcElieceFujisakiDigestCipher1.messageEncrypt(message1.getBytes(StandardCharsets.UTF_8));
byte[] ciphertext2 = mcElieceFujisakiDigestCipher2.messageEncrypt(message2.getBytes(StandardCharsets.UTF_8));
System.out.println("ct1 length: " + ciphertext1.length + " (" + (ciphertext1.length / (1024 * 1024)) + " mb)");
System.out.println("ct2 length: " + ciphertext2.length + " (" + (ciphertext2.length / (1024 * 1024)) + " mb)");
mcElieceFujisakiDigestCipher1.init(false, bcPriv);
mcElieceFujisakiDigestCipher2.init(false, bcPriv);
byte[] decryptedtext1 = mcElieceFujisakiDigestCipher1.messageDecrypt(ciphertext1);
byte[] decryptedtext2 = mcElieceFujisakiDigestCipher2.messageDecrypt(ciphertext2);
System.out.printf("Decrypted message 1: %s\n", new String(decryptedtext1, StandardCharsets.UTF_8));
System.out.printf("Decrypted message 2: %s\n", new String(decryptedtext2, StandardCharsets.UTF_8));
}
}
I know, that standard RSA supports just a small data blocks to encrypt,
That's why we use a hybrid cryptosystem. Data are encrypted using a symmetric cipher (data key), and the symmetric data key is encrypted using an asymmetric cipher.
I do not need changes detection, I need to make changes impossible (ok, as hard as possible).
If you are unable to enforce any read-only input/filesystem, then detecting changes is the best you can do. Either it's failed decryption or signature.
Actually to ensure data integrity I'd really use signing, not pure encryption. I see you don't want that, but at the end it will there. Some ciphers / cipher modes are malleable - data can be changed even when encrypted and without any authentication (mac, signature) the decryption is valid and you won't be able to detect the integrity failure.
If you would just rely on application to detect that data are corrupted after failed decryption, you are creating a perfect decryption oracle (breaking security)
I suppose, that the easiest way to do that, is to encrypt data with some secret key and to include the public key in the app, so app could decrypt and show the data, but without the secret key users would not be able to change the content.
Anything hardcoded in your app you can consider as revealed/public. You correctly identified the risk. If you have a dedicated user, nothing prevents the user to change the key in the app and pass invalid data. So - for anything that runs at the client, you can make the integrity stronger, but not perfect. At the end - you have to make some assumptions about adversary's abilities.
I encrypt data with my private key, pass to app, app in runtime decrypts data with it's public key and
In theory (mathematically) you can do that, but most of the current libraries will not let you use the key pairs wrong way (private key is intended for decryption or signing, public for encryption or validation). If you want to code such a solution yourself, you are in risk of creating weaknesses you may not be aware of (proper padding, timing,..)
I believe there are even some weaknesses in the scheme (encrypting using the private key), but I cannot recall details, there are people with deeper knowledge in the topic (e. g. James Polk from comments)
Edit:
Examples to create a signature or MAC : https://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html
btw - using aes-gcm the Java Cipher implementation automatically appends the mac tag to the end of the ciphertext
An interesting question. As far as I can see from your question, the data itself is not secret. Your problem is that the user should not be able to change the data (or that you should be able to detect that he or she has changed it). In this case, a hash function (including possibly a cryptographic hash function) might well be a better approach. See https://en.wikipedia.org/wiki/Hash_function and https://en.wikipedia.org/wiki/Cryptographic_hash_function. If you use a hash function, then you can always detect whether the user has tried to change the data.
The cryptographic hash functions are one-way, so that you do not need to store any key in your program.
I am implementing an Hybrid Encryption project and I have generated 2 key pairs for sender and receiver keys (private and public). I have these keys in a file.
KeyPair Generation Code
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import Decoder.BASE64Encoder;
public class GenerateRSAKeys{
public static void main(String[] args)
{
String publicKeyFilename = null;
String privateKeyFilename = null;
publicKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_publicKey";
privateKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_privateKey";
GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();
generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);
}
private void generate (String publicKeyFilename, String privateFilename){
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Create the public and private keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder b64 = new BASE64Encoder();
SecureRandom random = createFixedRandom();
generator.initialize(1024, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));
BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
out.write(b64.encode(pubKey.getEncoded()));
out.close();
out = new BufferedWriter(new FileWriter(privateFilename));
out.write(b64.encode(privKey.getEncoded()));
out.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
private static class FixedRand extends SecureRandom {
MessageDigest sha;
byte[] state;
FixedRand() {
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("can't find SHA-1!");
}
}
public void nextBytes(byte[] bytes){
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}
off += state.length;
sha.update(state);
}
}
}
}
Now, I need to secure these keys (probably not on any disk directly).
I have searched for this case on the internet; saving keys into key stores is the way to secure keys and use the same keystore when reading keys for the use in encryption and decryption.
Can someone suggest me how to save public and private key in key store and how to read it in java?
The Java KeyStore API (and underlying providers) does not support storing a keypair -- that is, a privatekey and publickey (which can then be used for publickey operations like encrypt and verify). It does support storing a privatekey and a certificate chain for the publickey. The certificate chain can consist of only one certificate, especially if that certificate is a self-signed certificate (which by definition is a chain by itself). That is exactly what commandline keytool -genkeypair does: it generates a keypair and stores the privatekey plus a self-signed certificate for the publickey in the (specified or defaulted) keystore file.
If you were using only 'standard' JRE, creating a self-signed (or other) certificate in code is not very easy.
However, if you have BouncyCastle added, as you obviously do or the code you posted wouldn't work, that has numerous applicable capabilities. For a simple example of generating a cert with only bcprov (the old, deprecated X509V3CertificateGenerator) see my JAVA API to create a keystore and attaching a csr and keypair to it -- but do as I recommended there but didn't show: don't create the unneeded CSR first, instead create the cert using the desired name and publickey.
Better, if you also have or can get bcpkix, use X509v3CertificateBuilder. See examples at Self signed X509 Certificate with Bouncy Castle in Java and Generating X509 Certificate using Bouncy Castle Java .
For writing out, and reading back in, a keystore file containing this data, just see the Javadoc linked above. Note Java traditionally defaulted to JKS-format files, and you will find many older answers here and elsewhere on the web which assume that, but since Java 9 in 2017 it defaults to PKCS12-format, which is not only standard and thus (mostly) interoperable but also more secure (uses a much better PBKDF algorithm).
However, your code uses a completely bogus random generator, and as a result generates the same keypair every time. Given this, there is no real need to store it, since you can always re-generate it any time you want. In fact, there is no benefit to even having this keypairs since it provides no security at all.
I need to generate public/private key for RSA algorithm on IOS device and send public key to server with encrypted text. Server must read public key and decrypt user message.
I have code on swift:
func generateKeys(){
var publicKey: SecKey?
var privateKey: SecKey?
let publicKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"publicTag".data(using: String.Encoding.utf8)! as NSObject]
let privateKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"privateTag".data(using: String.Encoding.utf8)! as NSObject]
var keyPairAttr = [NSObject: NSObject]()
keyPairAttr[kSecAttrKeyType] = kSecAttrKeyTypeRSA
keyPairAttr[kSecAttrKeySizeInBits] = 4096 as NSObject
keyPairAttr[kSecPublicKeyAttrs] = publicKeyAttr as NSObject
keyPairAttr[kSecPrivateKeyAttrs] = privateKeyAttr as NSObject
_ = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey)
var error:Unmanaged<CFError>?
if #available(iOS 10.0, *) {
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString(options: .lineLength64Characters)
print("public base 64 : \n\(b64Key)")
}
if let cfdata = SecKeyCopyExternalRepresentation(privateKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString(options: .lineLength64Characters)
print("private base 64 : \n\(b64Key)")
}
}
let encrypted = encryptBase64(text: "test", key: publicKey!)
let decrypted = decpryptBase64(encrpted: encrypted, key: privateKey!)
print("decrypted \(String(describing: decrypted))")
self.dismiss(animated: true, completion: nil);
}
func encryptBase64(text: String, key: SecKey) -> String {
let plainBuffer = [UInt8](text.utf8)
var cipherBufferSize : Int = Int(SecKeyGetBlockSize(key))
var cipherBuffer = [UInt8](repeating:0, count:Int(cipherBufferSize))
// Encrypto should less than key length
let status = SecKeyEncrypt(key, SecPadding.PKCS1, plainBuffer, plainBuffer.count, &cipherBuffer, &cipherBufferSize)
if (status != errSecSuccess) {
print("Failed Encryption")
}
let mudata = NSData(bytes: &cipherBuffer, length: cipherBufferSize)
return mudata.base64EncodedString()
}
func decpryptBase64(encrpted: String, key: SecKey) -> String? {
let data : NSData = NSData(base64Encoded: encrpted, options: .ignoreUnknownCharacters)!
let count = data.length / MemoryLayout<UInt8>.size
var array = [UInt8](repeating: 0, count: count)
data.getBytes(&array, length:count * MemoryLayout<UInt8>.size)
var plaintextBufferSize = Int(SecKeyGetBlockSize(key))
var plaintextBuffer = [UInt8](repeating:0, count:Int(plaintextBufferSize))
let status = SecKeyDecrypt(key, SecPadding.PKCS1, array, plaintextBufferSize, &plaintextBuffer, &plaintextBufferSize)
if (status != errSecSuccess) {
print("Failed Decrypt")
return nil
}
return NSString(bytes: &plaintextBuffer, length: plaintextBufferSize, encoding: String.Encoding.utf8.rawValue)! as String
}
This code returns public key in PKCS1. I found the library: SwCrypt
This code helps me to convert PKCS1 into PKCS8 and read public key with java
SwKeyConvert.PublicKey.pemToPKCS1DER(publicKeyPEM)
But I can't decrypt user message. Can you help me with message decryption? I wrote small unit test.
import org.junit.Test;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import static org.junit.Assert.assertNotNull;
public class TestExample {
String publicKeyContent = "MIMAAiMwDQYJKoZIhvcNAQEBBQADgwACDwAwggIKAoICAQC4K4zr1jTi4SSypXbrNeGd2HbYlrDRIPsPcL5a4JwGUKXwi+Rpf8Xh0D4dcRRH+Rtd5F66aqdGnhCBKtU5XsmlT+QssIggihI0iF3LEPsMlKapDrDdSbWmuitVDSSlulReMcN3hEUl8AzlNyu817snZtYESiFxm87QV6xZAcrWzvIdyiStBbngCT/v76tOZDX56IIRGoLMi3WND7538PqqYheh2+oZk05O+Bf5LZc6YteTRLLOSyIIxesoABo8tvaFyIo2ihMcnDRnGAzOMNTLXiQdj2scAMCVr3oiLpU48+Iw8ptOUBDQioW15FsYd3ugZhUX+/mFtMFsYkJyYjyG5HCqAs2/wm6eIjjy1QQwUF2hB8Z7sqyF5KrVZOv6Q7+pB83tT02ZXcDXCdsiP10G3sA4kjc/r9TuQHjCIwZa1LO4tPaO8qAzlROHIkQ4FhdaAM9U9DUq3nBywQLcEVQmXeH1OA1ve96QbMQoN+SRPh0Kq6W0U4TbzvMskQ7bePKDjiWP2fdtgSfrnOsyJaLi04n+hDsgiMfd4N9tauSMpCY6H9l7yYPc5Z+3qG2ANhteZGa7wT1OZoGLkZV0OurnA4xkzwcB7h0RVEvABB9dtl6S60FK1NELQy6sC/HCcivo9sJ+C1g2Sln+8qEdiju86X5ja5pGiRhJAxwSp2ZKgwIDAQAB";
String encryptedMessage = "g81SOC9XOD9zq5qfyhkdP/7ronNb82g3ueDtEh711L43zPSgrFksLEdIud/1fiDcV6N97RD41vb/iXtCg2/Gu6XliEhCaoG28reetG1cBndKF9UzQw9cYChp54S1wnhBkAAZQ4Of3c77DtPBCL4gcgv2ilBTm7o+NR2wXunfJ7Olbbau+7C1pa+Qv/+sz45r4gJmQ1MfGjHtw9e/U/3vjL9BfCEPn9Mo2zAZhkI81S0Ewth+csHwb3YTlE8mtHni1fvLRVXjvHk+57U3keoYPZk+93ytFL6pqkWMk+9VbLuUFHXn1mpSMiEr9GRN6XKRvEbbPp5lI9WjwRvtWfmRm5gLY76QinTrPb0KJg7oWmEoQie5o9W6MOkD+8vYV/SkkLT855SB3O57QLKCZmlSPlccE6GWfglHhAwRwrcTDY1bO/xH38gvYYPaAJMtJKtOVrqGxNkIUPwCCkdBa9JQwDSyTYxeh8AxC0ACs9cYVjMPrmC9zIZuRbmcneIGSugtzMZmI9qbLtW1aMlWuGrVyVhJlcCZuTJXWyBgx8xj8coX9YwUXSi1A4dL/Hl5Sme+HhAQs7OcH6ZZpsPmIIozXxHgOMhUo8k++cWg6+pudSoB2tr4NhxX/ID2jd1ELsg1C6mbxaKaGgXwfU9w4ZngbRxGTBlKWXwUP/xBa5BARZ4=";
#Test
public void encryptTest() throws Exception {
PublicKey publicKey = convertPublicKey(publicKeyContent);
assertNotNull(publicKey);
String s = decryptString(publicKey, encryptedMessage);
assertNotNull(s);
}
private PublicKey convertPublicKey(String publicKey) throws RSAAlgorithmException {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key
byte[] publicBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RSAAlgorithmException("Unable to generate public key from string " + publicKey + " . " + e.getMessage());
}
}
private String decryptString(PublicKey publicKey, String value) throws Exception {
byte[] decodedBytes;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c.init(Cipher.DECRYPT_MODE, publicKey);
decodedBytes = c.doFinal(value.getBytes());
} catch (Exception e) {
System.out.println("Error = " + e);
throw new Exception(e);
}
return new String(decodedBytes);
}
}
I have next error:
java.lang.Exception: javax.crypto.IllegalBlockSizeException: Data must not be longer than 512 bytes
In an asymmetric cryptosystem, you have a key pair consisting of both a public and a private key.
You encrypt with the public key and you decrypt with the private key. The public key can be shared (publicly) with other parties, enabling them to send you encrypted messages. The private key is kept secret so that only you can decrypt messages encrypted with your public key.
You normally don't encrypt messages directly with RSA, since the message has to be shorter than the modulus and it might have security implications. What you do instead is, you generate a random key for a symmetric encryption scheme, for example AES-256-CTR (or AES-256-GCM if you need authentication in addition to secrecy), encrypt the message with the symmetric encryption scheme, encrypt the key for the symmetric cipher with the asymmetric encryption scheme and send both the (asymmetrically) encrypted key and the (symmetrically) encrypted message to the receiver.
The receiver will first use his/her private key to decrypt the key for the symmetric encryption scheme, then use that to decrypt the actual message. This is sometimes referred to as "hybrid encryption" and it enables the message to be (more or less) arbitrarily long.
So, what you have to do is the following.
You have to generate a key pair for the receiver of the encrypted message. Therefore, if your communication is one-way (iOS device sends data to server, but no data ever comes back), you need to generate a key pair for your server only. If your server needs to talk back, you need to generate a key pair for your client as well.
In order to send an encrypted message to the server, the client needs to have the public key of your server. Therefore, you have to somehow transfer it there. The problem is that this transfer needs to be secure, otherwise an attacker may impersonate the server, present you his/her public key instead (for which he/she knows the private counterpart), intercept all traffic, decrypt it with his/her private key, re-encrypt it with the server's public key and pass it on to the server. This is called a man in the middle attack and enables the attacker to intercept (and possibly manipulate) all communication between you and the server. Therefore, your best choice might be not to exchange public keys at all but rather to embed them into the application. This will prevent man in the middle attacks, as long as the application code can be shared by an authenticated means.
When you want to send a message to the server, generate a random symmetric encryption key (with a cryptographically secure random number generator - this is not your language's default "random" function), encrypt the message with it and an appropriate symmetric encryption scheme, which you choose according to your requirements (e. g. authentication required? then use AES-GCM - only secrecy required? then use AES-CTR). Most encryption schemes also require a random (unpredictable) initialization vector which you also generate with a CSPRNG and have to send along to the receiver since it's required for decryption, but needs not be kept secret.
Encrypt the key for the symmetric encryption scheme with an asymmetric encryption scheme and the server's public key. RSA-PKCS1 is "dated". I'd try to use RSA-OAEP instead since it has more desirable security properties. Send the encrypted key to the server.
The server decrypts the key for the symmetric encryption scheme with the asymmetric encryption scheme and his private key (which is kept secret). Then it decrypts the message with the symmetric encryption scheme.
Since most of this is complicated and a lot of subtle details can lead to security breaches, I'd suggest you do not implement this yourself. I'd suggest you just use TLS (possibly with a restricted parameter set) and implement your own certificate validator where you compare the server's public key to a known-good value to get rid of the entire PKI stuff, which costs money and also is not very secure in the first place. At least, that's how I would do it.
Alternatively, if you want to roll out your own, "proprietary" protocol, you can try to use one of the more "developer friendly" cryptographic libraries, especially NaCl. This abstracts away a lot of the "gory details" and chooses lots of sane defaults for you, which cannot be overridden, all of which makes it a lot harder to implement insecure protocols.
Keep in mind this is not to say you're "too dumb". It's just the proper way of doing these things. When it comes to crypto, the less "DIY", the better. The more widespread the crypto is, that you use, the more it gets reviewed and the quicker flaws will get fixed, so using something like NaCl, which is used in thousands of applications, is pretty neat. As long as other NaCl applications are secure, your application is (probably) secure as well. When a breach is found, NaCl will get updated, you just update the library in your application and are automatically safe, so you're left with (almost) no need for internal review and patching and your windows of vulnerability will (usually) be short.
I need to implement AES Encryption Algorithm in Cryptographic Message Syntax (CMS) standard to encrypt my data in Windows Universal App (found reference here). I have it implemented on Java using Bouncy Castle library using the following code(I need the same functionality in C# UWP):
private static final ASN1ObjectIdentifier CMS_ENCRYPTION_ALGO = CMSAlgorithm.AES256_CBC;
private byte[] encrypt(byte[] key, byte[] dataToBeEncrypted) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, CMSException {
final KeySpec keySpec = new X509EncodedKeySpec(key);
final KeyFactory factory = KeyFactory.getInstance("RSA");
final PublicKey publicKey = factory.generatePublic(keySpec);
final SubjectKeyIdentifier subjectKeyIdentifier = new JcaX509ExtensionUtils().createSubjectKeyIdentifier(publicKey);
final RecipientInfoGenerator recipientInfoGenerator = new JceKeyTransRecipientInfoGenerator(subjectKeyIdentifier.getEncoded(), publicKey);
final CMSEnvelopedDataGenerator generator = new CMSEnvelopedDataGenerator();
generator.addRecipientInfoGenerator(recipientInfoGenerator);
final OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMS_ENCRYPTION_ALGO).build();
final CMSProcessableByteArray content = new CMSProcessableByteArray(dataToBeEncrypted);
final CMSEnvelopedData envelopedData = generator.generate(content, encryptor);
return envelopedData.toASN1Structure().getEncoded(ASN1Encoding.DER);
}
Now I have Referenced Bouncy Castle V 1.8.1 in my UWP App, but I found many differences (some libraries used in Java but not exist in Windows) and couldn't implement such functionality in C#.
So kindly either guide me to implement the same using native UWP Cryptography Library Windows.Security.Cryptography (Preferred),
Or tell me how can I implement same functionality using Bouncy Castle 1.8.1 in C# UWP app.
Update:
Based on the following diagram from here, I understand that the required steps are:
1- Get the data and generate Symmetric Key to encrypt the data using algorithm AesCbcPkcs7.
2- Encrypt Symmetric Key using the public key
3- Generate Digitally enveloped message.
So I did the first two steps based on my understanding using the following c# code (Please correct me if I'm wrong) and I need help to do the third step:
public string EncryptAndEnvelope(string openText, string p_key)
{
// Step 1 Get the data and generate Symmetric Key to encrypt the data using algorithm AesCbcPkcs7
IBuffer cBuffer = CryptographicBuffer.GenerateRandom(32);
SymmetricKeyAlgorithmProvider provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
CryptographicKey m_key = provider.CreateSymmetricKey(cBuffer);
IBuffer bufferMsg = CryptographicBuffer.ConvertStringToBinary(AsciiToString(StringToAscii(openText)), BinaryStringEncoding.Utf8);
IBuffer bufferEncrypt = CryptographicEngine.Encrypt(m_key, bufferMsg, null);
// Step 2 Encrypt Symmetric Key using the public key
IBuffer publicKey = CryptographicBuffer.DecodeFromBase64String(p_key);
AsymmetricKeyAlgorithmProvider asym = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey ckey = asym.ImportPublicKey(publicKey, CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo);
IBuffer cbufferEncrypt = CryptographicEngine.Encrypt(ckey, cBuffer, null);
// Step 3 Generate Digitally enveloped message
// I need help here
}
private byte[] StringToAscii(string s)
{
byte[] retval = new byte[s.Length];
for (int ix = 0; ix < s.Length; ++ix)
{
char ch = s[ix];
if (ch <= 0x7f) retval[ix] = (byte)ch;
else retval[ix] = (byte)'?';
}
return retval;
}
private string AsciiToString(byte[] bytes)
{
return string.Concat(bytes.Select(b => b <= 0x7f ? (char)b : '?'));
}
Note: While I was looking for solution, I've found that the answer is available using the library System.Security.Cryptography
(but it is not supported in Universal Apps) and I'm pretty sure that
the implementation is available using Bouncy Castle (there are
tons of documentation for Java but unfortunately there is no
documentation at all for C#).
The BouncyCastle cryptography APIs allow for creating and verifying digital signatures using the regular java.security package objects, such as java.security.PublicKey, java.security.PrivateKey and their container java.security.KeyPair.
Suppose I use OpenSSL to create a .pem (or, if easier, a .der file) containing the elliptic curve private key I want to use in my application. For example, it looks like this:
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK
oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun
GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==
-----END EC PRIVATE KEY-----
How do I use the BouncyCastle APIs to obtain a java.security.KeyPair containing both this private key and a corresponding public key?
Please note I want to use the APIs available in BouncyCastle 1.50 (which is current at the time of writing) and no deprecated APIs. This unfortunately excludes the PEMReader class used in other SO answers. Furthermore, this question is specific to the format of elliptic curves; they contain additional parameters when compared RSA or DSA key files.
In addition to the standard JCE approach shown by divanov as long as you give it the correct input (see my comment thereto), or just using JCE in the first place like your selfanswer, BouncyCastle 1.48 up DOES still contain the old PEMReader functionality just organized a bit differently and for this case you can use something like:
static void SO22963581BCPEMPrivateEC () throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Reader rdr = new StringReader ("-----BEGIN EC PRIVATE KEY-----\n"
+"MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK\n"
+"oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun\n"
+"GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==\n"+"-----END EC PRIVATE KEY-----\n");
Object parsed = new org.bouncycastle.openssl.PEMParser(rdr).readObject();
KeyPair pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair)parsed);
System.out.println (pair.getPrivate().getAlgorithm());
}
In Java this will be pretty much the same code. After striping guarding strings away and decoding Base64 data give it to this utility method:
public static PrivateKey keyToValue(byte[] pkcs8key)
throws GeneralSecurityException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
KeyFactory factory = KeyFactory.getInstance("ECDSA");
PrivateKey privateKey = factory.generatePrivate(spec);
return privateKey;
}
Since I only need this for a quick and dirty demo, I solved it in the following way (in Scala). First, I generate a public private key pair in the REPL and print out its data:
Security.addProvider(new BouncyCastleProvider)
val SignatureScheme = "some signature scheme, eg ECDSA"
val RandomAlgorithm = "some random algorithm, eg SHA1PRNG"
val keygen = KeyPairGenerator.getInstance(SignatureScheme)
val rng = SecureRandom.getInstance(RandomAlgorithm)
rng.setSeed(seed)
keygen.initialize(KeySize, rng)
val kp = keygen.generateKeyPair()
println(kp.getPublic.getEncoded.toSeq) // toSeq so that Scala actually prints it
println(kp.getPrivate.getEncoded.toSeq)
Then using the generated data,
val hardcodedPublic = Array[Byte]( /* data */ )
val hardcodedPrivate = Array[Byte]( /* data */ )
val factory = KeyFactory.getInstance(SignatureScheme)
val publicSpec = new X509EncodedKeySpec(hardcodedPublic)
val publicKey = factory.generatePublic(publicSpec)
val privateSpec = new PKCS8EncodedKeySpec(hardcodedPrivate)
val privateKey = factory.generatePrivate(privateSpec)
The key thing you need to know here is that by default public key data uses X509 encoding and private key data uses PKCS8 encoding. It should be possible to get OpenSSL to output these formats and parse them manually, but I did not check how.
I used information from this blog post about SpongyCastle (which is Android's BouncyCastle alias) quite helpful. It is unfortunate that documentation is fragmented like this, and that BouncyCastle's wiki was down at the time of this question.
Update: the BouncyCastle wiki is up, and you can find the documentation here.