I used BouncyCastle to verify signature and extract infos from a ".p7m" file (original content, signer etc.).
Now I need to validate and extract infos from the TimeStamp within the same ".p7m" file.
How can I validate the TimeStampToken? I wrote this code that works well for the Signature but it doesn't validate the TimeStamp. I passed the "cert" variable to the build() method to validate the Signature and the TimeStampToken both. For the Signature it's ok, but for the TimeStamp it doesn't work :(
Where do I wrong? Thanks in advance.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Security;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataParser;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.Store;
public class Launcher3 {
public static void main(String[] args) throws Exception {
File myFile=new File("D:\\fdr\\bouncycastle\\New Text Document.txt.p7m");
byte[] bytesArray = readContentIntoByteArray(myFile);
FileOutputStream fos = new FileOutputStream("D:\\fdr\\bouncycastle\\New Text Document.txt");
byte[] bytesArrayOriginalFile=getData(bytesArray);
fos.write(bytesArrayOriginalFile);
fos.close();
verifySign(bytesArray);
}
static public void verifySign(byte[] signedData) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
CMSSignedDataParser sp = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), signedData);
sp.getSignedContent().drain();
Store certStore = sp.getCertificates();
SignerInformationStore signers = sp.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
System.out.println("info 1: "+cert.getIssuer());
System.out.println("info 2: "+cert.getSubject());
System.out.println("date from: "+cert.getNotBefore());
System.out.println("date to: "+cert.getNotAfter());
System.out.println("Serial n. "+cert.getSerialNumber());
System.out.println("verify returns: " + signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
// --------------------------------------------------------------------------------------------------------------------^
// LOOK AT HERE: it works!
AttributeTable attrs = signer.getUnsignedAttributes();
Attribute att = attrs.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
ASN1Encodable dob = att.getAttrValues().getObjectAt(0);
byte[] encodedTsp = dob.toASN1Primitive().getEncoded();
TimeStampToken result = null;
if(encodedTsp!=null) {
CMSSignedData cms = new CMSSignedData(encodedTsp);
result = new TimeStampToken(cms);
System.out.println("timestamp: "+result.getTimeStampInfo().getGenTime());
System.out.println("serial n. "+result.getTimeStampInfo().getSerialNumber());
System.out.println("tsa: "+result.getTimeStampInfo().getTsa());
System.out.println("policy: "+result.getTimeStampInfo().getPolicy());
result.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
// ------------------------------------------------------------------------------^
// LOOK AT HERE: it doesn't work!
}
}
}
static public byte[] getData(final byte[] p7bytes) throws CMSException, IOException {
CMSSignedData signedData = new CMSSignedData(p7bytes);
CMSProcessable signedContent = signedData.getSignedContent();
return (byte[])signedContent.getContent();
}
private static byte[] readContentIntoByteArray(File file)
{
FileInputStream fileInputStream = null;
byte[] bFile = new byte[(int) file.length()];
try
{
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return bFile;
}
}
I solved by myself (and I'm so happy). I need to find the certificate of the timestamptoken in this way:
Store storeTt = result.getCertificates();
Collection collTt = storeTt.getMatches(result.getSID());
Iterator certIt2 = collTt.iterator();
X509CertificateHolder cert2 = (X509CertificateHolder)certIt2.next();
System.out.println("timestamp's verify: "+result.isSignatureValid(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert2)));
result.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert2));
Related
import android.util.Log;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import static android.support.constraint.Constraints.TAG;
public class RSAEncryption {
static dataToBeEncrypt = "Hello";
static String publickey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mI0EXEr27gEEALybxOEubZ39PX1t+oU9iXhTTmqrc/+ha+J6TYd+yvYcNEBIOzPI\n" +
"1fG2Tz30zJYwT9uZOyW7LAGKRcsIH6p4SiKGaEYBLEWuCQrGfXPAAXaH+WNsKOmz\n" +
"SAZilPwugEcWknIUDZzfU0AD8PPCLqhJM6Ij7AJ/SNa2xUIsyUW+PwBdABEBAAG0\n" +
"BWplZXRziM4EEwEKADgWIQRp0+dcK5ZTXht9TIcw0CiFbn2j6gUCXEr27gIbAwUL\n" +
"CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAw0CiFbn2j6um7A/9Ttzl8xHa4MvuF\n" +
"htOEAb6sXOk17B9BQyd3bkUCYtBjmFA3Q7XzWln5lm2QQTxW+AnlVJYwQS5zvVC0\n" +
"8WgPh6XP9sCi6/2ar748wpS8dL1wEAwaMOSYGEDCUpmW2uEGsrsAKfiMlKGhLiPC\n" +
"uUvPJ0x2jjHn9HHO1e9BES9tlkRPJLiNBFxK9u4BBADQtF6EajJMi2IPsVDR6PTX\n" +
"swjGgVbYKqwZRUwfLecJ95VeiQzznWUMpWFze4ESlAjCYeJf37voDazs6Ol5Uqz7\n" +
"7d5GurEk4kNjQp4Pm+chFQWLWU1seMghyJqsXCjWApy0lNJ2YPNbYKUqdJsZpJgT\n" +
"7qofPvEEPlGRRqzZxdN8oQARAQABiLYEGAEKACAWIQRp0+dcK5ZTXht9TIcw0CiF\n" +
"bn2j6gUCXEr27gIbDAAKCRAw0CiFbn2j6ky9BACgADAD/VIFIUjuQtNa4GEcAS0T\n" +
"vtJvsL26qW/Gohl5nb1ix7MLBwiH/l1Co6K52GKAVUrZBCnjkJW5zyGTKUkPGbGY\n" +
"Loh481phsvpYgXHqol7UQivBoF14EO1dNmn61QFRn7D3zhNirziOKQl3kLSuEdc5\n" +
"VpRF7ubkHL3jRrdQFA==\n" +
"=/lmV\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
public static void main (String[] args){
String encrypted = null;
try {
encrypted = encryptToFile(dataToBeEncrypt, publicKey);
} catch (Exception e) {
Log.i(TAG, "Exception: " + e);
}
return encrypted;
}
public static String encryptToFile(String inputdata, String keyFile) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] original = inputdata.getBytes();
InputStream secKey = new ByteArrayInputStream(keyFile.getBytes("UTF-8"));
byte[] encrypted = encrypt(original, readPublicKey(secKey), true, true);
return new String(encrypted);
}
private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
PGPPublicKey k = null;
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
break;
}
}
}
return k;
}
/**
* Simple PGP encryptor between byte[].
*
* #param clearData
* The test to be encrypted
* #param //passPhrase
* The pass phrase (key). This method assumes that the key is a
* simple pass phrase, and does not yet support RSA or more
* sophisiticated keying.
* #param //fileName
* File name. This is used in the Literal Data Packet (tag 11)
* which is really inly important if the data is to be related to
* a file to be recovered later. Because this routine does not
* know the source of the information, the caller can set
* something here for file name use that will be carried. If this
* routine is being used to encrypt SOAP MIME bodies, for
* example, use the file name from the MIME type, if applicable.
* Or anything else appropriate.
*
* #param armor
* #return encrypted data.
* #exception IOException
* #exception PGPException
* #exception NoSuchProviderException
*/
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, "", // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(),
"BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
}
I was trying to achieve encryption using RSA algorithm with given public key and text to be encrypted. Below is my code which I was trying where I am passing my public key and string and getting the encrypted data. The problem is below code works fine when I run it as Java Application but when I use it in my android code I always get org.bouncycastle.openpgp.PGPException: Exception creating cipher as exception for line OutputStream cOut = cPk.open(out, bytes.length);. Please suggest me how can I make it work with my Android application.
Also,
OutputStream pOut = lData.open(cos, // the compressed output stream PGPLiteralData.BINARY, "", // "filename" to store clearData.length, // length of clear data new Date() // current time );
I am passing file name as null. Is this creating issue?
I expect Encrypted Message Without Version: BCPG v1.46, but I am getting org.bouncycastle.openpgp.PGPException: Exception creating cipher at line OutputStream cOut = cPk.open(out, bytes.length);
Editted
Exception:
org.bouncycastle.openpgp.PGPException: Exception creating cipher
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at com.example.Resources.RSAEncryption.encrypt(RSAEncryption.java:157)
at com.example.Resources.RSAEncryption.encryptToFile(RSAEncryption.java:74)
at com.example.Resources.RSAEncryption.encryption(RSAEncryption.java:62)
at com.example.EQR.QRUserCredentials.onActivityResult(QRUserCredentials.java:140)
at android.app.Activity.dispatchActivityResult(Activity.java:6931)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4090)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4137)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1529)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.security.NoSuchAlgorithmException: Provider BC does not provide CAST5/CFB/NoPadding
at javax.crypto.Cipher.createCipher(Cipher.java:921)
at javax.crypto.Cipher.getInstance(Cipher.java:901)
... 17 more
Finally, I found the solution.
Instead of CAST5 I am using AES_128 PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.AES_128, withIntegrityCheck, new SecureRandom(),"BC");
Below is the working code
import android.util.Log;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import static android.support.constraint.Constraints.TAG;
public class RSAEncryption {
static dataToBeEncrypt = "Hello";
static String publickey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mI0EXEr27gEEALybxOEubZ39PX1t+oU9iXhTTmqrc/+ha+J6TYd+yvYcNEBIOzPI\n" +
"1fG2Tz30zJYwT9uZOyW7LAGKRcsIH6p4SiKGaEYBLEWuCQrGfXPAAXaH+WNsKOmz\n" +
"SAZilPwugEcWknIUDZzfU0AD8PPCLqhJM6Ij7AJ/SNa2xUIsyUW+PwBdABEBAAG0\n" +
"BWplZXRziM4EEwEKADgWIQRp0+dcK5ZTXht9TIcw0CiFbn2j6gUCXEr27gIbAwUL\n" +
"CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAw0CiFbn2j6um7A/9Ttzl8xHa4MvuF\n" +
"htOEAb6sXOk17B9BQyd3bkUCYtBjmFA3Q7XzWln5lm2QQTxW+AnlVJYwQS5zvVC0\n" +
"8WgPh6XP9sCi6/2ar748wpS8dL1wEAwaMOSYGEDCUpmW2uEGsrsAKfiMlKGhLiPC\n" +
"uUvPJ0x2jjHn9HHO1e9BES9tlkRPJLiNBFxK9u4BBADQtF6EajJMi2IPsVDR6PTX\n" +
"swjGgVbYKqwZRUwfLecJ95VeiQzznWUMpWFze4ESlAjCYeJf37voDazs6Ol5Uqz7\n" +
"7d5GurEk4kNjQp4Pm+chFQWLWU1seMghyJqsXCjWApy0lNJ2YPNbYKUqdJsZpJgT\n" +
"7qofPvEEPlGRRqzZxdN8oQARAQABiLYEGAEKACAWIQRp0+dcK5ZTXht9TIcw0CiF\n" +
"bn2j6gUCXEr27gIbDAAKCRAw0CiFbn2j6ky9BACgADAD/VIFIUjuQtNa4GEcAS0T\n" +
"vtJvsL26qW/Gohl5nb1ix7MLBwiH/l1Co6K52GKAVUrZBCnjkJW5zyGTKUkPGbGY\n" +
"Loh481phsvpYgXHqol7UQivBoF14EO1dNmn61QFRn7D3zhNirziOKQl3kLSuEdc5\n" +
"VpRF7ubkHL3jRrdQFA==\n" +
"=/lmV\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
public static void main (String[] args){
String encrypted = null;
try {
encrypted = encryptToFile(dataToBeEncrypt, publicKey);
} catch (Exception e) {
Log.i(TAG, "Exception: " + e);
}
return encrypted;
}
public static String encryptToFile(String inputdata, String keyFile) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] original = inputdata.getBytes();
InputStream secKey = new ByteArrayInputStream(keyFile.getBytes("UTF-8"));
byte[] encrypted = encrypt(original, readPublicKey(secKey), true, true);
return new String(encrypted);
}
private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
PGPPublicKey k = null;
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
break;
}
}
}
return k;
}
/**
* Simple PGP encryptor between byte[].
*
* #param clearData
* The test to be encrypted
* #param //passPhrase
* The pass phrase (key). This method assumes that the key is a
* simple pass phrase, and does not yet support RSA or more
* sophisiticated keying.
* #param //fileName
* File name. This is used in the Literal Data Packet (tag 11)
* which is really inly important if the data is to be related to
* a file to be recovered later. Because this routine does not
* know the source of the information, the caller can set
* something here for file name use that will be carried. If this
* routine is being used to encrypt SOAP MIME bodies, for
* example, use the file name from the MIME type, if applicable.
* Or anything else appropriate.
*
* #param armor
* #return encrypted data.
* #exception IOException
* #exception PGPException
* #exception NoSuchProviderException
*/
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, "", // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.AES_128, withIntegrityCheck, new SecureRandom(),
"BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
}
I have tried answers of these questions:
Bouncy Castle : PEMReader => PEMParser
Read an encrypted private key with bouncycastle/spongycastle
However as my encrypted key is encoded in DER when I call
Object object = pemParser.readObject();
object is null.
I can convert it to PEM with this openssl's command (it decrypts the key too)
openssl pkcs8 -inform der -in pkey.key -out pkey.pem
but I need to read the key in its original file
Both those Qs are about parsing, and decrypting, files using OpenSSL's 'legacy PEM' encryption. You are using PKCS8 encryption which is different though similar, so Reading PKCS8 in PEM format: Cannot find provider is closer. You can use most of the approach there, but skipping the PEM parse:
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo; // NOT the
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; // javax ones!
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
// args[0] = filename args[1] = password
FileInputStream fis = new FileInputStream(args[0]);
byte[] buff = new byte[9999]; int len = fis.read(buff); fis.close();
// could use File.readAllBytes in j8 but my dev machine is old
// create what PEMParser would have
ASN1Sequence derseq = ASN1Sequence.getInstance (Arrays.copyOf(buff,len));
PKCS8EncryptedPrivateKeyInfo encobj = new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(derseq));
// decrypt and convert key
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
InputDecryptorProvider decryptionProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(args[1].toCharArray());
PrivateKeyInfo keyInfo = encobj.decryptPrivateKeyInfo(decryptionProv);
PrivateKey key = converter.getPrivateKey(keyInfo);
// now actually use key, this is just a dummy
System.out.println (key.getAlgorithm());
By using SpongyCastle we can get good results. I made some code that will get the desired result.
SpongyCastle Jars can be downloaded from below:-
core, prov, pkix
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
// need core.jar, prov.jar, bcpkix-jdk15on.jar
import org.spongycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
import org.spongycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.spongycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.spongycastle.operator.InputDecryptorProvider;
import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
class Decrypt {
private static void usage() {
System.err.println(" Usage: encrypted.pk8 password out_decrypted.pk8");
System.exit(2);
}
public static byte[] copyOf(byte[] original, int newLength) {
byte[] copy = new byte[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
public static void loadProvider(String providerClassName)
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class providerClass = Class.forName(providerClassName);
Provider provider = (Provider)providerClass.newInstance();
// Security.insertProviderAt(provider, 1);
Security.addProvider(provider);
}
public static void main(String[] args) throws Exception {
if (args.length != 3) usage();
loadProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
FileInputStream fis = new FileInputStream(args[0]);
byte[] buff = new byte[fis.available()];
int len = fis.read(buff);
fis.close();
// create what PEMParser would have
ASN1Sequence derseq = ASN1Sequence.getInstance(copyOf(buff, len));
EncryptedPrivateKeyInfo epkInfo = EncryptedPrivateKeyInfo.getInstance(derseq);
PKCS8EncryptedPrivateKeyInfo encobj = new PKCS8EncryptedPrivateKeyInfo(epkInfo);
// decrypt and convert key
InputDecryptorProvider decryptionProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(args[1].toCharArray());
PrivateKeyInfo keyInfo = encobj.decryptPrivateKeyInfo(decryptionProv);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("SC");
PrivateKey key = converter.getPrivateKey(keyInfo);
// now actually use key, this is just a dummy
System.out.println(key.getAlgorithm());
FileOutputStream fos = new FileOutputStream(args[2]);
fos.write(key.getEncoded());
fos.close();
}
}
Java NetBeans - New to Java and I'm trying to make an old app work so I can mirror it in C# (my lang of choice) ... Trying to import a cert... not sure what I'm missing... Where can I find "ResourceReader"? as I cannot seem to figure out if that is something I need ? also... I get an errror reading the file (likely related to ResourceReader?) I searched here, but not sure which one is the one I'm after (if any). I'm recreating this with NetBeans on "Windows 7".
package doccentertestingencryption;
/**
*
*/
import java.security.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.security.cert.*;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import doccentertestingencryption.CryptoConstants.*;
//import doccentertestingencryption.sLinksBC;
import doccentertestingencryption.SignData;
import doccentertestingencryption.FileLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
//import ResourceReader;
public class EncryptData
{
private String certFile = null;
private String encString = null;
private String encryptMode = null;
private X509Certificate cert = null;
private SecretKey symmKey = null;
CryptoConstants crypto = null;
public EncryptData() throws Exception
{
try
{
crypto = new CryptoConstants();
certFile = crypto.getCertFileName();
if (certFile != null)
try
{
initSecurity();
}
catch ( ExceptionInInitializerError e )
{
throw e;
}
}
catch(ExceptionInInitializerError ex )
{
throw ex;
}
} // end contructor EncryptData
public EncryptData(String vendorAcro) throws Exception
{
crypto = new CryptoConstants();
certFile = crypto.getCertFileName(vendorAcro);
if (certFile != null)
try
{
initSecurity();
}
catch ( Exception e )
{
throw e;
}
else
throw new Exception(vendorAcro+"certFile not found in CryptoConstants class") ;
}// end contructor EncryptData
private void initSecurity() throws CertificateException, Exception
{
try
{
Security.addProvider(new BouncyCastleProvider());
CertificateFactory cf = CertificateFactory.getInstance(CryptoConstants.certType);
//InputStream inStream = this.getClass().getResourceAsStream(certFile);
InputStream inStream = this.getClass().getResourceAsStream(certFile);
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
//cert = (X509Certificate) cf.generateCertificate(inStream);
cert = (X509Certificate) cf.generateCertificate(inStream);
if (cert == null)
throw new Exception("in EncryptData.init: cert = null");
KeyGenerator keyGen = KeyGenerator.getInstance(CryptoConstants.symmEncryptMode);
symmKey = keyGen.generateKey();
}
catch(ExceptionInInitializerError ex)
{
throw ex;
}
}
#SuppressWarnings("null")
public String encrypt(String strToEncrypt) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException
{
try
{
//String encString= null;
byte dataToEncrypt[] = strToEncrypt.getBytes();
Cipher symmCipher = null;
try {
symmCipher = Cipher.getInstance(CryptoConstants.symmEncryptMode);
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
Logger.getLogger(EncryptData.class.getName()).log(Level.SEVERE, null, ex);
}
symmCipher.init(Cipher.ENCRYPT_MODE, symmKey);
byte[] encrypted = symmCipher.doFinal(dataToEncrypt);
encString= new String(Base64.encode(encrypted));
encString = URLEncoder.encode(encString, "UTF-8");
return(encString);
}
catch (ExceptionInInitializerError ei )
{
throw ei;
}
} //end method create Signature
public String encryptSymmKey() throws Exception
{
Cipher asymmCipher = Cipher.getInstance(CryptoConstants.asymmEncryptCipher, "BC");
asymmCipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
byte[] encSymmKeyBytes = asymmCipher.doFinal(symmKey.getEncoded());
String encSymmKeyString= new String(Base64.encode(encSymmKeyBytes));
encSymmKeyString = URLEncoder.encode(encSymmKeyString, "UTF-8");
return(encSymmKeyString);
} //end method create Signature
public String encryptQSValues(String unencryptedQS) throws Exception
{
String encryptedQS = null;
String nameNequals = null;
String value = null;
String nameValuePair = null;
int i = 0;
try
{
encryptedQS = "ENCQS="+ encrypt(unencryptedQS);
encryptedQS = encryptedQS +"&KS="+ encryptSymmKey();
}
catch (Exception e)
{
throw e;
}
return(encryptedQS);
} //end method encryptQSValues
/**
* Returns the signature
* #return String
*/
public String getEncString()
{
return encString;
}
public Provider[] getProviders()
{
Provider provs[] = Security.getProviders();
return provs;
}
public static void dumpProviders()
{
Provider provs[] = Security.getProviders();
for (int i=0; i< provs.length;i++)
System.out.println("providers["+i+"]= "+provs[i].getName());
}
} // end EncryptData
My error is:
Exception in thread "main" java.security.cert.CertificateException: Missing input stream
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:93)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
at doccentertestingencryption.EncryptData.initSecurity(EncryptData.java:103)
at doccentertestingencryption.EncryptData.<init>(EncryptData.java:82)
at doccentertestingencryption.DocCenterTestingEncryption.main(DocCenterTestingEncryption.java:31)
C:\Users\dbarry\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 1 second)
This issue is generated in continuation of past question How to RSA verify a signature in java that was generated in php . That code work for simple text. But Now I have requirement for signing and verifying the text which also have a public key ( other than verification key ) in format.
text1:text2:exported-public-key
Example :
53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:MIIBCgKCAQEAxWg6ErfkN3xu8rk9WsdzjL5GpjAucMmOAQNeZcgMBxN+VmU43EnvsDLSxUZD1e/cvfP2t2/dzhtV6N2IvT7hveuo/zm3+bUK6AnAfo6pM1Ho0z4WetoYOrHdOVNMMPaytXiVkNlXyeWRF6rl9JOe94mMYWRJzygntiD44+MXsB6agsvQmB1l8thg/8+QHNOBBU1yC4pLQwwO2cb1+oIl0svESkGpzHk8xJUl5jL6dDnhqp8+01KE7AGHwvufrsw9TfVSAPH73lwo3mBMVXE4sfXBzC0/YwZ/8pz13ToYiN88DoqzcfD3+dtrjmpoMpymAA5FBc5c6xhPRcrn24KaiwIDAQAB
PHP Code :
$rsa = new Crypt_RSA();
$keysize=2048;
$pubformat = "CRYPT_RSA_PUBLIC_FORMAT_PKCS1";
$privformat = "CRYPT_RSA_PRIVATE_FORMAT_PKCS8";
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kp = $d['publickey'];
$Ks = $d['privatekey'];
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kver = $d['publickey'];
$KSign = $d['privatekey'];
$plainText = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:".$Kp;
// Signing
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($KSign);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($plainText);
$signedHS = base64_encode($signature);
// Verification
$signature = base64_decode($signedHS);
$rsa->loadKey($Kver);
$status = $rsa->verify($plainText, $signature);
var_dump($status);
JAVA Code
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
//import java.util.Base64;
//import java.util.Base64.Decoder;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerifySig {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = "MIIBCgKCAQEA+8fKYCT4QiFUdsJ7VdF4xCkVmq/Kwc/10Jl3ie6mvn8hEsC3NAtMJu+Od12gyWYsS0zBDiQ8h2pGZ7p4uWqenc01dRRrq+g968zmoCKPUllPUuR6v9o+wYTX/os4hgaQSBg7DQn4g3BEekcvyk6e6zAMvuhHjeqnrinhCMFgJUhFL8zFNoyaH559C0TNbR6BTKzOoikah8cKhu4UOga0tWDC0I2Ifus/sHOwVaOBkDFIzD6jBxDH/QF8FsrLLTocuIb7Y6lVxFPPtgiUJku6b7wKExV0bPJvm6/Xhv1GX1FpMrA0Ylzj5IFviuviwgo534EcZQ/Hx3aIf4oPG8jVTQIDAQAB";
byte[] dpkey = Base64.decodeBase64(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:MIIBCgKCAQEArszIunGg3ievJOpgesYQsp3nPGgrW+3VwkivkkktOXUBRzb3G3mZzidEjG6LxNe/rrNe0UczmnSHQoSBxJCHyUnCWNfScBD66CFG4hLo5Z1gxrP8D2M2lCa6ap2PWcsKiWqlu38EinMeBjBvB4aYpF7+FkFy64ObxR4pfVZxnxradkD0HvvMPLMbyeHxeGqYf8orERf9jfuKTdY8V44rxht2D2fg2WhB1+XL0JulsPvgOaSK3RPnwi+RQAJbihCIh5Zznn0KQCs5pIWoT3XKe1DMpQuEmphSOY9ZUg3AwlOrpRV+565x6GCSc615/6nowmqKzE4T7qT5nbH+ctiEHQIDAQAB";
String data = "iD96rNeR51BF2TUZSaw+QhW8SnsMXE5AdJiDVmJk6LL55jC26PBCnqXrFo2lsQt8aWRsZc0bHFGCcuIbhHA+Duo1/PwrxTqC5BZFL/frqsRSVa+vpvGEnj3xe4iImTEasMicQzzaAG9IWIgkRZ272lUZ8PqdtTuqAsRIwir6fEsfVs5uIErEWM18R4JxlFBc3LDIjFOFemEPSVIEBHwWht1c/CrdTtxPRIiugEb1jdofEBUNcWPZgfvApVx5+0aS9WTl31AY+RMlvp+13P/FQgAMnH9rvBdopRIVsZUNlMf8AOE2afhLPfOgx+41rzCB2wGCrRGELbml466WJ3wYNQ==";
byte[] ciphertext = Base64.decodeBase64(data);
System.out.println(new String(plainData.getBytes(), UTF_8));
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void verifyBC(PublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
//Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
Signature sig = Signature.getInstance( "SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
}
It not gave any error but just return false when using public key in plainText. If try after removing with public key, It works and return true.
PHP is working fine and signature is verified in all cases.
I suspecting if java is unable to verify data having base 64 text/public key as text ?
UPDATE : I compare binary bytes of both two times and result show minor difference.
First Case
PHP -> ��#C:���sQ
JAVA -> ��/#C:���sQ
Second Case
PHP -> ��]Q0l�O+
JAVA -> ��]Q0l�
If php base64 is not compatible with apache base 64 ?
When I run the PHP code, I'm noticing that the $Kp variable contains a key in the wrong format:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAqCJ/2E+YZvXJyabQmi0zZlaXXGbfXHt8KYS27i+PAJKBODmevTrS
w59S5AOy2l7lB4z5mYHuwdT6bm6YYXgE0gnoX/b2L65xdD9XtlenS4Zm15TVTdR5
zde4nBa0QPKfhFvthOmdPr9xDhDb8Rojy/phX+Ftva33ceTXoB+CtLyidMWbQmUh
ZufnI7MwIOPAIzXNJJ85eyUjBdoNMwlAPZo9vYQWeiwYGyP1fjQwEWZgjCH/LJjl
sNR1X9vp5oi8/4omdnFRvKLpkd5R7WMmMfAyAXe7tcfMSXuVAgMWEj9ZG0ELpXbG
S3CK6nvOp2gFF+AjHo9bCrh397jYotE3HQIDAQAB
-----END RSA PUBLIC KEY-----
When I strip out all the extra formatting and export the key as a single line of base64, it works.
PHP code:
function extract_key($pkcs1) {
# strip out -----BEGIN/END RSA PUBLIC KEY-----, line endings, etc
$temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $pkcs1, 1);
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
return str_replace(array("\r", "\n", ' '), '', $temp);
}
$rsa = new Crypt_RSA();
$keysize=2048;
$pubformat = "CRYPT_RSA_PUBLIC_FORMAT_PKCS1";
$privformat = "CRYPT_RSA_PRIVATE_FORMAT_PKCS8";
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kp = $d['publickey'];
$Ks = $d['privatekey'];
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
$Kver = $d['publickey'];
$KSign = $d['privatekey'];
file_put_contents("pub_verify_key.txt",extract_key($Kver));
$plainText = "53965C38-E950-231A-8417-074BD95744A4:22-434-565-54544:".extract_key($Kp);
file_put_contents("plain.txt",$plainText);
// Signing
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($KSign);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($plainText);
$signedHS = base64_encode($signature);
file_put_contents("signedkey.txt", $signedHS);
// Verification
$signature = base64_decode($signedHS);
$rsa->loadKey($Kver);
$status = $rsa->verify($plainText, $signature);
var_dump($status);
Java code:
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.File;
import java.io.FileReader;
import java.math.BigInteger;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerifySig {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = fromFile("pub_verify_key.txt");
byte[] dpkey = Base64.decodeBase64(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = fromFile("plain.txt");
String data = fromFile("signedkey.txt");
byte[] ciphertext = Base64.decodeBase64(data);
System.out.println(new String(plainData.getBytes(), UTF_8));
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void verifyBC(PublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
//Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
Signature sig = Signature.getInstance( "SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
private static String fromFile(String filename) {
StringBuilder builder = new StringBuilder(8000);
try {
FileReader reader = new FileReader(new File(filename));
int c;
while((c = reader.read()) != -1) {
builder.append((char)c);
}
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
return builder.toString();
}
}
I have a bouncy castle code that encrypts and sign a message(listed below) ; now I need to find the code for opposite end ; Verify the signature & decrypt the message. My search to find a solution for the past 2 days has been futile, as I could only find example for either verifying the signature (or) for decryption ; but not both. Please find below the encryption & signing code ; would greatly appreciate any hint or direction to Verify the signature & decrypt the message
Note : The method encryptAndSign() is called to encrypt and sign the Content/data
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.naming.NamingException;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public final class EncryptorNsigner {
public byte[] encryptAndSign(final String content, final String selfSignedCert,
final String encryptionCert) throws CertificateException, IOException,
KeyStoreException, Exception {
byte[] signedBytes;
//Get key store
KeyStore keyStore = this.getKeyStore();
//Setup provider
CMSSignedDataGenerator signatureGenerator = this.setUpProvider(
keyStore, selfSignedCert);
//Encrypt data
byte[] encryptBytes = this.encryptData(content.getBytes("UTF-8"),
getEncyptionCertFromKeystore(keyStore, encryptionCert), null);
//Sign the encrypted data
signedBytes = this.signPkcs7(encryptBytes, signatureGenerator);
return Base64.encode(signedBytes);
}
private KeyStore getKeyStore() throws NamingException, KeyStoreException,
FileNotFoundException, CertificateException, IOException,
NoSuchAlgorithmException{
...
return keystore;
}
private X509Certificate getEncyptionCertFromKeystore(final KeyStore keystore, final String encryptionCertName)
throws CertificateException, IOException, KeyStoreException{
Certificate c = keystore.getCertificate(encryptionCertName);
X509CertificateHolder certHolder = new X509CertificateHolder(c.getEncoded());
return new JcaX509CertificateConverter().setProvider( "BC" )
.getCertificate(certHolder );
}
private CMSSignedDataGenerator setUpProvider(final KeyStore keystore, String signCertName) throws Exception {
Security.addProvider(new BouncyCastleProvider());
//Get certificate chain
Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(signCertName);
final List<Certificate> certlist = new ArrayList<Certificate>();
//Add the certificates in Certificate chain to the Certificate list
for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
certlist.add(certchain[i]);
}
//Class for storing certificates for later lookup
Store certstore = new JcaCertStore(certlist);
//Get certificate
Certificate cert = keystore.getCertificate(signCertName);
String algorithm = "SHA1withRSA";
String keyStorePwd = "....";
ContentSigner signer = new JcaContentSignerBuilder(algorithm).setProvider("BC").
build((PrivateKey) (keystore.getKey(signCertName, keyStorePwd.toCharArray())));
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
build()).build(signer, (X509Certificate) cert));
generator.addCertificates(certstore);
return generator;
}
private byte[] signPkcs7(final byte[] content, final CMSSignedDataGenerator generator)
throws CMSException, IOException {
CMSTypedData cmsdata = new CMSProcessableByteArray(content);
CMSSignedData signeddata = generator.generate(cmsdata, true);
return signeddata.getEncoded();
}
private byte[] encryptData(byte[] dataToEncrypt, X509Certificate recipientCert, String algorithmName) throws IOException{
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
CMSEnvelopedData ed;
AlgorithmIdentifier digestAlgorithm;
if (algorithmName!=null && !algorithmName.isEmpty()){
digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(new DefaultSignatureAlgorithmIdentifierFinder().find(algorithmName));
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(digestAlgorithm.getAlgorithm())
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
else{
try {
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
ed = edGen.generate(new CMSProcessableByteArray(dataToEncrypt),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC) //Default algorithmName = DES_EDE3_CBC
.setProvider("BC").build());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
return ed.getEncoded();
}
}
2 years waiting for an answer. I hope you find the solution.
To help others, here is a sample for reading SMIME emails and decrypt attachments. It's based on the fact that the sender used your public key to encrypt and you got your key and password to decrypt the message.
Security.addProvider(new BouncyCastleProvider());
// IMAP encrypt
final KeyStore ks = KeyStore.getInstance("PKCS12");
final String password = CERTIFICAT_SMIME_PWD;
ks.load(this.class.getClassLoader()
.getResourceAsStream(CERTIFICAT_SMIME_PFX), password.toCharArray());
final String alias = ks.aliases().nextElement();
final PrivateKey pKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
final SMIMEEnveloped m = new SMIMEEnveloped((MimeMessage) message);
final RecipientInformationStore recipients = m.getRecipientInfos();
final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
final RecipientId recId = new JceKeyTransRecipientId(cert);
final RecipientInformation recipient = recipients.get(recId);
final byte[] content = recipient.getContent(new JceKeyTransEnvelopedRecipient(pKey)
.setProvider(BouncyCastleProvider.PROVIDER_NAME));
final String providerName = BouncyCastleProvider.PROVIDER_NAME;
final MimeBodyPart res = SMIMEUtil.toMimeBodyPart(content);
final MimeMultipart parts = (MimeMultipart) res.getContent();
for (int i = 0; i < parts.getCount(); i++) {
final BodyPart part = parts.getBodyPart(i);
if (part.getContent() instanceof Multipart) {
multipart = (Multipart) part.getContent();
}
}