Equivalent of pgp command in bouncy castle - java

Does anyone know what will be the equivalent of following command (GnuPG 2.17.2) in bouncy castle in Java?
gpg -e -r "recipient" --output output.gpg input.zip

Create a certificate and private key
public static byte[] encryptData(byte[] data, X509Certificate encryptionCertificate) throws CertificateEncodingException, CMSException, IOException {
byte[] encryptedData = null;
if (null != data && null != encryptionCertificate) {
CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator
= new CMSEnvelopedDataGenerator();
JceKeyTransRecipientInfoGenerator jceKey
= new JceKeyTransRecipientInfoGenerator(encryptionCertificate);
cmsEnvelopedDataGenerator.addRecipientInfoGenerator(transKeyGen);
CMSTypedData msg = new CMSProcessableByteArray(data);
OutputEncryptor encryptor
= new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC)
.setProvider("BC").build();
CMSEnvelopedData cmsEnvelopedData = cmsEnvelopedDataGenerator
.generate(msg,encryptor);
encryptedData = cmsEnvelopedData.getEncoded();
}
return encryptedData;}
Create a JceKeyTransRecipientInfoGenerator object using the recipient's certificate.
Then Create a new CMSEnvelopedDataGenerator object and added the recipient information generator into it.
Then use the JceCMSContentEncryptorBuilder class to create an OutputEncrytor object, using the AES CBC algorithm.
The encryptor is used later to generate a CMSEnvelopedData object that encapsulates the encrypted message.
Finally, the encoded representation of the envelope is returned as a byte array.
Get the final Byte array written to a file.

Related

convert RSA Publickey to base64 and vice versa

I have a publicKey/privateKey pair generated from this function:
public static void generateKey() {
try {
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(2048);
final KeyPair key = keyGen.generateKeyPair();
File privateKeyFile = new File(PRIVATE_KEY_FILE);
File publicKeyFile = new File(PUBLIC_KEY_FILE);
// Create files to store public and private key
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
}
privateKeyFile.createNewFile();
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
}
publicKeyFile.createNewFile();
// Saving the Public key in a file
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close();
// Saving the Private key in a file
ObjectOutputStream privateKeyOS = new ObjectOutputStream(
new FileOutputStream(privateKeyFile));
privateKeyOS.writeObject(key.getPrivate());
privateKeyOS.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Now I want to convert publicKey to base64 while writing and use that base64 decode to get publicKey back ,how can that be done?
Generally if you want to store a file in base 64 you can simply encode the byte array. You can even put a Base64 stream in between the ObjectOutputStream and FileOutputStream (helpfully provided by the Base64 class within Java 8).
However, public keys and private keys have default encodings which can be accessed using their getEncoded methods:
PublicKey publicKey = key.getPublic();
byte[] encodedPublicKey = publicKey.getEncoded();
String b64PublicKey = Base64.getEncoder().encodeToString(encodedPublicKey);
try (OutputStreamWriter publicKeyWriter =
new OutputStreamWriter(
new FileOutputStream(publicKeyFile),
StandardCharsets.US_ASCII.newEncoder())) {
publicKeyWriter.write(b64PublicKey);
}
This saves the public key in SubjectPublicKeyInfo format, something that can be read and written by multiple types of software and cryptographic libraries.
For instance, you can paste it in an online ASN.1 decoder (the online decoder will itself convert it to hex, but it will parse base 64 as well). The format of bytes are in so called ASN.1 / DER (which is a generic format, just like you can encode multiple types of files in XML).
If you want to have the key in OpenSSL compatible format (with a "PUBLIC KEY" header and footer) you can use a library such as Bouncy Castle (e.g. org.bouncycastle.openssl.jcajce.JcaPEMWriter).

Bouncy Castle works differently on Android and Console program. How to encrypt using public key in PEM file

I am encrypting using Bouncy Castle. I am using RSA and public key is stored in PEM file. I am finding that when I run the code in simple console project (not Android project) everything works fine - meaning the encrypted string can be decrypted using the private key. However, when I run the same code in Android app, the encrypted byte array is different for the same public key and is not recognized as valid encryption for the given key pair.
Details:
Here is the code that encrypts string using Bouncy Castle and taken out of the console project. This one works fine and produces the encrypted string which is valid encryption for the key pair and can be decrypted.
private static void encrypt() {
try {
//This example uses the Bouncy Castle library
Security.addProvider(new BouncyCastleProvider());
String plainText = "This needs to be encrypted";
String public_key_file = "PublicKey.pem";
//Load public key
PEMParser parser = new PEMParser(new FileReader(public_key_file));
Object key = parser.readObject();
parser.close();
PublicKey pubKey = null;
if (key instanceof SubjectPublicKeyInfo) {
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) key;
pubKey = KeyFactory.getInstance("RSA").generatePublic
(new X509EncodedKeySpec(spki.getEncoded()));
}
//Encrypt the plain text
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted_data = cipher.doFinal(plainText.getBytes());
String encoded_data = new String(Base64.encode(encrypted_data));
System.out.println("Encrypted Value:");
System.out.println(encoded_data);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
Now here is the code taken out of the Android app. The only important difference is that the key file is read from Assets folder. This code also produces encrypted string but it is not valid for the key pair and can't be decrypted.
public void encrypt(Context context) {
try {
//This example uses the Bouncy Castle library
Security.addProvider(new BouncyCastleProvider());
String plainText = "This needs to be encrypted";
String public_key_file = "PublicKey.pem";
//Load public key
InputStream inputStream = context.getAssets().open(public_key_file);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
// read from the input stream reader
PEMParser parser = new PEMParser(inputStreamReader);
Object key = parser.readObject();
parser.close();
PublicKey pubKey = null;
if (key instanceof SubjectPublicKeyInfo) {
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) key;
pubKey = KeyFactory.getInstance("RSA").generatePublic
(new X509EncodedKeySpec(spki.getEncoded()));
}
//Encrypt the plain text
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted_data = cipher.doFinal(plainText.getBytes());
String encoded_data =
new String(Base64.encode(encrypted_data));
Log.d("MyApp", "Encrypted Value:");
Log.d("MyApp", encoded_data);
} catch (Exception ex) {
Log.d("MyApp", ex.getMessage());
}
}
In both the cases, following things are same:
Public Key
Actual code written (with difference of reading from Assets folder and logging to Logcat)
JDK version (1.7)
Bouncy Castle libraries (bcpkix-jdk15on-152.jar and bcprov-jdk15on-152.jar)
What's different: The environment. One is console program and the other is Android App.
On further investigation while debugging
I observed that when running the console program the KeyFactory returns instance of "sun.security.rsa.RSAPublicKeyImpl". However when running android app, the KeyFactory returns instance of "com.android.org.conscrypt.OpenSSLRSAPublicKey". Not sure if that's the problem. The encrypted byte array is different for same plain text and public key.
Any help is greatly appreciated.
Thanks in Advance,
Sandeep

Which data from a CMSSignedData object must I pass to generate a valid Timestamp?

I have a valid PKCS7 file loaded into a CMSSignedData object.
This PKCS7 file includes a plain text message and a valid attached digital signature (all in the same file).
Now I want to timestamp this file. This is the code I'm using (source):
private static CMSSignedData addTimestamp(CMSSignedData signedData)
throws Exception {
Collection ss = signedData.getSignerInfos().getSigners();
SignerInformation si = (SignerInformation) ss.iterator().next();
TimeStampToken tok = getTimeStampToken();
ASN1InputStream asn1InputStream = new ASN1InputStream
(tok.getEncoded());
DERObject tstDER = asn1InputStream.readObject();
DERSet ds = new DERSet(tstDER);
Attribute a = new Attribute(new
DERObjectIdentifier("1.2.840.113549.1.9.16.2.14"), ds);
DEREncodableVector dv = new DEREncodableVector();
dv.add(a);
AttributeTable at = new AttributeTable(dv);
si = SignerInformation.replaceUnsignedAttributes(si, at);
ss.clear();
ss.add(si);
SignerInformationStore sis = new SignerInformationStore(ss);
signedData = CMSSignedData.replaceSigners(signedData, sis);
return signedData;
}
private static TimeStampToken getTimeStampToken() throws
Exception {
Security.addProvider (new
org.bouncycastle.jce.provider.BouncyCastleProvider());
PostMethod post = new PostMethod("http://My-TrustedTimeStampProvier.com");
// I'm omitting the part where I pass the user and password
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
//request TSA to return certificate
reqGen.setCertReq (true); // In my case this works
//make a TSP request this is a dummy sha1 hash (20 zero bytes)
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
byte[] enc_req = request.getEncoded();
ByteArrayInputStream bais = new ByteArrayInputStream(enc_req);
post.setRequestBody(bais);
post.setRequestContentLength (enc_req.length);
post.setRequestHeader("Content-type","application/timestamp-query");
HttpClient http_client = new HttpClient();
http_client.executeMethod(post);
InputStream in = post.getResponseBodyAsStream();
//read TSP response
TimeStampResponse resp = new TimeStampResponse (in);
resp.validate(request);
TimeStampToken tsToken = resp.getTimeStampToken();
return tsToken;
}
I can get a valid TimeStamp, and I could put it into my CMSSignedData object and save it to a file writting the bytes from signedData.getEncoded() to the harddisk. But when I validate my new shinny timestamped file with a third party software, this software tells the original signature is ok, but the Timestamp doesn't correspond with the signature. This software also can show me the original plain text message.
I think the problem is in this line:
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
I think I have to pass a digest instead of a dummy byte array, but I don't know which digest, or what are the right bytes I have to timeStamp.
I successfully could get and verify a SignerInformation object from my signedData. Then I tried to pass to the reqGen.generate() function the bytes from mySignerInformation.getSignature(). The timestamp verification failed. Then I passed a Sha1 digest of mySignerInformation.getSignature(), but my timestamp verification failed again.
The RFC3161 specification says:
2.4.1. Request Format
A time-stamping request is as follows:
TimeStampReq ::= SEQUENCE { version INTEGER
{ v1(1) }, messageImprint MessageImprint,
--a hash algorithm OID and the hash value of the data to be
(...)
The messageImprint field SHOULD contain the hash of the datum to be
time-stamped. The hash is represented as an OCTET STRING. Its
length MUST match the length of the hash value for that algorithm
(e.g., 20 bytes for SHA-1 or 16 bytes for MD5).
MessageImprint ::= SEQUENCE {
hashAlgorithm AlgorithmIdentifier,
hashedMessage OCTET STRING }
But it doesn't tell me where or how I get the MessageImprint data if I want to TimeStamp the bytes inside a CMSSignedData object.
I'm a newbie in this digital signature stuff.
You're right, the problem is that you're timestamping the incorrect data. The rest of the code seems correct to me.
So the thing is that you've to timestamp the hash of the signature. To get the signature from your CMSSignedData and hash it; you can use the follow code (supposing that you've only one signer in your PKCS7 and you're using SHA1 hash algorithm):
CMSSignedData signedData = ...
// get the signers of your CMSSignedData signedData
Collection ss = signedData.getSignerInfos().getSigners();
SignerInformation si = (SignerInformation) ss.iterator().next();
// hash the signature
byte[] signDigest = MessageDigest
.getInstance(TSPAlgorithms.SHA1, new BouncyCastleProvider())
.digest(si.getSignature()); // since you're adding the bc provider with Security.addProvider you can use "BC" instead of passing the new BouncyCastleProvider()
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
// generate the TSRequest
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, signDigest, BigInteger.valueOf(100));
...
Hope this helps,

PHP & Java Decrpytion error using PhpSecLib and BouncyCastle

I'm trying to encrypt stuff in java using the public key generated by my PHP:
PHP Code
$rsa = new Crypt_RSA();
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$keys = $rsa->createKey(1024);
extract($keys);
echo (base64_encode($publickey));
For testing purposes, I've set aside a keypair (base64) of the above format.
I retrieve my Public Key in java and base64 decode it.
String publicKeyDecoded = new String(Base64.decode(publicKey));
PEMParser pr = new PEMParser(new StringReader(publicKeyDecoded));
Object obj = pr.readObject();
pr.close();
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) obj;
AsymmetricKeyParameter askp = PublicKeyFactory.createKey(spki);
AsymmetricBlockCipher e = new RSAEngine();
e = new org.bouncycastle.crypto.encodings.PKCS1Encoding(e);
e.init(true, askp);
byte[] messageBytes = plainText.getBytes();
byte[] encryptedData = e.processBlock(messageBytes, 0, messageBytes.length);
byte[] encryptedDataBase = Base64.encode(encryptedData);
I send the Base64 encrypted plaintext back to PHP for decryption using the following:
$rsa->loadKey($privatekey) or die ("Cant load");
echo $rsa->decrypt($cipher);
It's unable to decrpyt my encoded message and throws me the error:
Decryption error in <b>/opt/lampp/htdocs/Crypt/RSA.php</b> on line <b>2120</b>
Can someone point me to the right direction? It's been hours since I'm trying to figure this out.
I'm using a hardcoded keypair so I guess there's no question of my keys being wrong...
To answer my own question:
Everything apart from the final decryption PHP was correct.
It should be:
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$rsa->loadKey(base64_decode($_SESSION['private'])) or die ("Cant load");
echo $rsa->decrypt(base64_decode($cipher));
I forgot to un-base64 my encrypted text sent from java and to set the encryption modes.
Thanks neubert.

Convert message and signature to BouncyCastle CMSSignedData object

I have an X509CertificateObject, a matching RSAPublicKey and managed to create a byte array containing a valid digital certificate for some message object also as a byte array.
Unfortunately the system I'm building upon only accepts CMSSignedData objects as input.
How do I convert my basic building blocks into such a valid CMSSignedData object?
Background: I'm experimenting with Java Bouncy Castle RSA blind signatures according to this example (digest is SHA512) and need to feed the result into the standard signature processing.
First, you'll probably want to sign your data with a private key. The idea being that the signature should be something only you can create. One you get that the rest should be as follows:
X509Certificate signingCertificate = getSigningCertificate();
//The chain of certificates that issued your signing certificate and so on
Collection&ltX509Certificate&gt certificateChain = getCertificateChain();
PrivateKey pk = getPrivateKey();
byte[] message = "SomeMessage".getBytes();
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
certificateChain.add(signingCertificate);
generator.addCertificates(new CollectionStore(certificateChain));
JcaDigestCalculatorProviderBuilder jcaDigestProvider = new JcaDigestCalculatorProviderBuilder();
jcaDigestProvider.setProvider(new BouncyCastleProvider());
JcaSignerInfoGeneratorBuilder singerInfoGenerator = new JcaSignerInfoGeneratorBuilder(jcaDigestProvider.build());
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
AsymmetricKeyParameter privateKeyParam = PrivateKeyFactory.createKey(pk.getEncoded());
ContentSigner cs = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKeyParam);
SignerInfoGenerator sig = singerInfoGenerator.build(cs, signingCertificate);
generator.addSignerInfoGenerator(sig);
CMSSignedData data = generator.generate(new CMSProcessableByteArray(message), true);

Categories

Resources