I'm looking for an implementation of Bouncy Castle PGP 'sign and encrypt'. Ideally in one operation, if that makes any difference.
I've taken the encrypt example and the signing example and tried to turn it into a 'one pass' encrypt and sign operation.
I see this relatively dated implementation Boncode. It seems to show that the two operations are just linked together.
I'm not getting the consumer to decrypt the code. The signature seems to be able to be verified. This is true whether I use the merged operations or separate encrypt then sign.
Is there a better Bouncy Castle PGP implementation?
Here is my current implementation of a one pass, Bouncy Castle PGP encrypt+sign. The signature seems to verify, but the payload is not decrypted.
public class SinglePassSignedEncryptedFileProcessor {
private static final Logger logger = LoggerFactory.getLogger(SinglePassSignedEncryptedFileProcessor.class);
/*
* This is the primary function that will create encrypt a file and sign it
* with a one pass signature. This leans on an C# example by John Opincar
* #author Bilal Soylu
* #param targetFileName
* -- file name on drive systems that will contain encrypted content
* #param embeddedFileName
* -- the original file name before encryption
* #param secretKeyRingInputStream
* -- Private Key Ring File
* #param targetFileStream
* -- The stream for the encrypted target file
* #param secretKeyPassphrase
* -- The private key password for the key retrieved from
* collection used for signing
* #param signPublicKeyInputStream
* -- the public key of the target recipient to be used to
* encrypt the file
* #throws Exception
*/
public void encryptOnePassSign(
String fileName,
InputStream keyIn,
OutputStream out,
char[] pass,
PGPPublicKey encryptionKey,
boolean armor,
boolean withIntegrityCheck,
String providerName)
throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException {
if (armor) {
out = new ArmoredOutputStream(out);
}
// Compress
byte[] bytes = PGPEncryptUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP);
// Encryption process.
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider("BC"));
ByteArrayOutputStream encryptedOutputStream = new ByteArrayOutputStream();
OutputStream encryptedOut = encGen.open(encryptedOutputStream, bytes);
encryptedOut.write(bytes);
encryptedOut.close();
byte[] bytesEncrypted = encryptedOutputStream.toByteArray();
encryptedOutputStream.close();
// Signing process.
PGPSecretKey pgpSec = PGPEncryptUtil.readSecretKey(keyIn);
PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("BC"));
sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
Iterator it = pgpSec.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, (String) it.next());
sGen.setHashedSubpackets(spGen.generate());
}
PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(
PGPCompressedData.UNCOMPRESSED);
// Write to the output stream.
BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(out));
sGen.generateOnePassVersion(false).encode(bOut);
File file = new File(fileName);
PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
// file is encoding name.
Date lastModified = new Date(file.lastModified());
OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, fileName, lastModified, bytesEncrypted);
//FileInputStream fIn = new FileInputStream(file);
//int ch;
//while ((ch = fIn.read()) >= 0) {
lOut.write(bytesEncrypted);
sGen.update(bytesEncrypted);
// }
// ?
lGen.close();
sGen.generate().encode(bOut);
cGen.close();
if (armor) {
out.close();
}
// close everything down we are done
/*
literalOut.close();
literalDataGenerator.close();
signatureGenerator.generate().encode(compressedOut);
compressedOut.close();
compressedDataGenerator.close();
encryptedOut.close();
encryptedDataGenerator.close();
*/
// if (armor) targetFileStream.close();
}
}
Latest answer is to use BouncyGPG
Works as per the test cases. Kotlin
val encryptionStream = BouncyGPG
.encryptToStream()
.withConfig(keyringConfig)
.withStrongAlgorithms()
.toRecipient("recipient#example.com")
.andSignWith("sender#example.com")
.armorAsciiOutput()
.andWriteTo(cipherText)
encryptionStream.write(expectedPlaintext)
encryptionStream.close()
cipherText.close()
Related
I am trying to decrypt an AES encrypted string from Java, in C#. When I decrypt, it returns gibberish and does not match the original plain text, which was encrypted via Java code. Pls guide me on what is going wrong here.
Attached the Java code for encryption and the C# code for decryption. Pls let me know if you need more details.
I tried AesCryptoServiceProvider as well and it did not work either. You can see the code tried in the commented code in C#.
Pls note that I can make changes on my C# code only to match the Java code and can not make any edits to Java side.
Java code for Encryption:
/** encrypt cipher */
private static final Cipher ENCRYPT_CIPHER = generateCipher(Cipher.ENCRYPT_MODE);
private static String ENCRYPT_KEY = "key";
/**
* #param val
* #return encrypted value
* #throws Exception
*/
public String encrypt(final String val) throws Exception {
return new String(Base64.encodeBase64(ENCRYPT_CIPHER.doFinal(val.getBytes()), true)).toString();
}
/**
* #param encrypt
* #return cipher
*/
protected static Cipher generateCipher(final int encrypt) {
try {
final Cipher cipher = Cipher.getInstance("AES");
cipher.init(encrypt, SecretKeyFactory.getInstance("AES").generateSecret(new IBMAESKeySpec(Base64.decodeBase64(ENCRYPT_KEY.getBytes()))));
return cipher;
} catch (final Exception e) {
return null;
}
}
C# code for decryption:
private static String ENCRYPT_KEY = "key";
public String decodeString (String encodedStr)
{
/*using (var aesCryptoProvider = new AesCryptoServiceProvider())
{
aesCryptoProvider.BlockSize = 128;
aesCryptoProvider.KeySize = 256;
aesCryptoProvider.Key = Convert.FromBase64String(ENCRYPT_KEY.ToString());
aesCryptoProvider.Padding = PaddingMode.Zeros;
aesCryptoProvider.Mode = CipherMode.ECB;
using (var decryptor = aesCryptoProvider.CreateDecryptor())
using (var memoryStream = new MemoryStream(Convert.FromBase64String(encodedStr)))
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var streamReader = new StreamReader(cryptoStream, Encoding.UTF8))
{
decodedStr = streamReader.ReadToEnd();
}
}
*/
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Convert.FromBase64String(ENCRYPT_KEY.ToString()); ;
aesAlg.BlockSize = 128;
aesAlg.KeySize = 256;
aesAlg.Mode = CipherMode.ECB;
aesAlg.Padding = PaddingMode.Zeros;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor();
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(encodedStr)))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
decodedStr = srDecrypt.ReadToEnd();
}
}
}
}
}
This is just a quick answer. Haven't done a ton of research into it, but have you checked to see if the endian-ness matches? It looks like C# (.NET) is little-endian, but the JVM is big-endian. I'm not sure if it swaps it for network transmission, however (then it would just match the hardware). Just an idea. If I find anything additional, I'll update my answer.
How can I get BouncyCastle to decrypt a GPG-encrypted message?
I have created a GPG key pair at the CentOS 7 command line using gpg --gen-key. I chose RSA RSA as the encryption types, and I exported the keys using gpg --export-secret-key -a "User Name" > /home/username/username_private.key and gpg --armor --export 66677FC6 > /home/username/username_pubkey.asc
I am able to import username_pubkey.asc into a remote Thunderbird client of another email account and successfully send an encrypted email to username#mydomain.com. But when my Java/BouncyCastle code running at mydomain.com tries to decrypt the GPG-encoded data, it gives the following error:
org.bouncycastle.openpgp.PGPException:
Encrypted message contains a signed message - not literal data.
If you look at the code below, you will see this corresponds with the line in PGPUtils.decryptFile() which states else if (message instanceof PGPOnePassSignatureList) {throw new PGPException("Encrypted message contains a signed message - not literal data.");
The original code for this came from the blog entry at this link, though I made minor changes to get it to compile in Eclipse Luna with Java 7. A user of the linked blog reported the same error, and the blog author replied by saying that it does not work with GPG. So how do I fix this to make it work with GPG?
The Java decryption code starts when the GPG-encoded-file and the GPG-secret-key are passed into Tester.testDecrypt() as follows:
Tester.java contains:
public InputStream testDecrypt(String input, String output, String passphrase, String skeyfile) throws Exception {
PGPFileProcessor p = new PGPFileProcessor();
p.setInputFileName(input);//this is GPG-encoded data sent from another email address using Thunderbird
p.setOutputFileName(output);
p.setPassphrase(passphrase);
p.setSecretKeyFileName(skeyfile);//this is the GPG-generated key
return p.decrypt();//this line throws the error
}
PGPFileProcessor.java includes:
public InputStream decrypt() throws Exception {
FileInputStream in = new FileInputStream(inputFileName);
FileInputStream keyIn = new FileInputStream(secretKeyFileName);
FileOutputStream out = new FileOutputStream(outputFileName);
PGPUtils.decryptFile(in, out, keyIn, passphrase.toCharArray());//error thrown here
in.close();
out.close();
keyIn.close();
InputStream result = new FileInputStream(outputFileName);//I changed return type from boolean on 1/27/15
Files.deleteIfExists(Paths.get(outputFileName));//I also added this to accommodate change of return type on 1/27/15
return result;
}
PGPUtils.java includes:
/**
* decrypt the passed in message stream
*/
#SuppressWarnings("unchecked")
public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList) {enc = (PGPEncryptedDataList) o;}
else {enc = (PGPEncryptedDataList) pgpF.nextObject();}
//
// find the secret key
//
Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && it.hasNext()) {
pbe = it.next();
sKey = findPrivateKey(keyIn, pbe.getKeyID(), passwd);
}
if (sKey == null) {throw new IllegalArgumentException("Secret key for message not found.");}
InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
InputStream unc = ld.getInputStream();
int ch;
while ((ch = unc.read()) >= 0) {out.write(ch);}
} else if (message instanceof PGPOnePassSignatureList) {
throw new PGPException("Encrypted message contains a signed message - not literal data.");
} else {
throw new PGPException("Message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected()) {
if (!pbe.verify()) {throw new PGPException("Message failed integrity check");}
}
}
/**
* Load a secret key ring collection from keyIn and find the private key corresponding to
* keyID if it exists.
*
* #param keyIn input stream representing a key ring collection.
* #param keyID keyID we want.
* #param pass passphrase to decrypt secret key with.
* #return
* #throws IOException
* #throws PGPException
* #throws NoSuchProviderException
*/
public static PGPPrivateKey findPrivateKey(InputStream keyIn, long keyID, char[] pass)
throws IOException, PGPException, NoSuchProviderException
{
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPSecretKeyRingCollection pgpSec = new JcaPGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
return findPrivateKey(pgpSec.getSecretKey(keyID), pass);
}
/**
* Load a secret key and find the private key in it
* #param pgpSecKey The secret key
* #param pass passphrase to decrypt secret key with
* #return
* #throws PGPException
*/
public static PGPPrivateKey findPrivateKey(PGPSecretKey pgpSecKey, char[] pass)
throws PGPException
{
if (pgpSecKey == null) return null;
PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass);
return pgpSecKey.extractPrivateKey(decryptor);
}
The complete code of all three Java files can be found on a file sharing site by clicking on this link.
The complete stack trace for the error can be found by clicking on this link.
For reference, the GUI instructions for encryption by the remote Thunderbird sender are summarized in the following screen shot:
I have read many postings and links about this. In particular, this other SO posting looks similar, but is different. My Keys use RSA RSA, but the other posting does not.
EDIT#1
As per #DavidHook's suggestion, I have read SignedFileProcessor, and I am starting to read the much longer RFC 4880. However, I need actual working code to study in order to understand this. Most people who find this via google searches will also need working code to illustrate the examples.
For reference, the SignedFileProcessor.verifyFile() method recommended by #DavidHook is as follows. How should this be customized to fix the problems in the code above?
private static void verifyFile(InputStream in, InputStream keyIn) throws Exception {
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpFact = new PGPObjectFactory(in);
PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject();
pgpFact = new PGPObjectFactory(c1.getDataStream());
PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject();
PGPOnePassSignature ops = p1.get(0);
PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject();
InputStream dIn = p2.getInputStream();
int ch;
PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());
FileOutputStream out = new FileOutputStream(p2.getFileName());
ops.initVerify(key, "BC");
while ((ch = dIn.read()) >= 0){
ops.update((byte)ch);
out.write(ch);
}
out.close();
PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject();
if (ops.verify(p3.get(0))){System.out.println("signature verified.");}
else{System.out.println("signature verification failed.");}
}
EDIT#2
The SignedFileProcessor.verifyFile() method recommended by #DavidHook is almost identical to the PGPUtils.verifyFile() method in my code above, except that PGPUtils.verifyFile() makes a copy of extractContentFile and calls PGPOnePassSignature.init() instead of PGPOnePassSignature.initVerify(). This may be due to a version difference. Also, PGPUtils.verifyFile() returns a boolean, while SignedFileProcessor.verifyFile() gives SYSO for the two boolean values and returns void after the SYSO.
If I interpret #JRichardSnape's comments correctly, this means that the verifyFile() method might best be called upstream to confirm the signature of the incoming file using the sender's public key, and then, if the signature on the file is verified, using another method to decrypt the file using the recipient's private key. Is this correct? If so, how do I restructure the code to accomplish this?
if anyone is interested to know how to encrypt and decrypt gpg files using bouncy castle openPGP library, check the below java code:
The below are the 4 methods you going to need:
The below method will read and import your secret key from .asc file:
public static PGPSecretKey readSecretKeyFromCol(InputStream in, long keyId) throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(in, new BcKeyFingerprintCalculator());
PGPSecretKey key = pgpSec.getSecretKey(keyId);
if (key == null) {
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
return key;
}
The below method will read and import your public key from .asc file:
#SuppressWarnings("rawtypes")
public static PGPPublicKey readPublicKeyFromCol(InputStream in) throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in, new BcKeyFingerprintCalculator());
PGPPublicKey key = null;
Iterator rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
key = k;
}
}
}
if (key == null) {
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
return key;
}
The below 2 methods to decrypt and encrypt gpg files:
public void decryptFile(InputStream in, InputStream secKeyIn, InputStream pubKeyIn, char[] pass) throws IOException, PGPException, InvalidCipherTextException {
Security.addProvider(new BouncyCastleProvider());
PGPPublicKey pubKey = readPublicKeyFromCol(pubKeyIn);
PGPSecretKey secKey = readSecretKeyFromCol(secKeyIn, pubKey.getKeyID());
in = PGPUtil.getDecoderStream(in);
JcaPGPObjectFactory pgpFact;
PGPObjectFactory pgpF = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
Object o = pgpF.nextObject();
PGPEncryptedDataList encList;
if (o instanceof PGPEncryptedDataList) {
encList = (PGPEncryptedDataList) o;
} else {
encList = (PGPEncryptedDataList) pgpF.nextObject();
}
Iterator<PGPPublicKeyEncryptedData> itt = encList.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData encP = null;
while (sKey == null && itt.hasNext()) {
encP = itt.next();
secKey = readSecretKeyFromCol(new FileInputStream("PrivateKey.asc"), encP.getKeyID());
sKey = secKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass));
}
if (sKey == null) {
throw new IllegalArgumentException("Secret key for message not found.");
}
InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
pgpFact = new JcaPGPObjectFactory(clear);
PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new JcaPGPObjectFactory(c1.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
InputStream inLd = ld.getDataStream();
int ch;
while ((ch = inLd.read()) >= 0) {
bOut.write(ch);
}
//System.out.println(bOut.toString());
bOut.writeTo(new FileOutputStream(ld.getFileName()));
//return bOut;
}
public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey) throws IOException, NoSuchProviderException, PGPException {
Security.addProvider(new BouncyCastleProvider());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom()));
cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
}
Now here is how to invoke/run the above:
try {
decryptFile(new FileInputStream("encryptedFile.gpg"), new FileInputStream("PrivateKey.asc"), new FileInputStream("PublicKey.asc"), "yourKeyPassword".toCharArray());
PGPPublicKey pubKey = readPublicKeyFromCol(new FileInputStream("PublicKey.asc"));
encryptFile(new FileOutputStream("encryptedFileOutput.gpg"), "fileToEncrypt.txt", pubKey);
} catch (PGPException e) {
fail("exception: " + e.getMessage(), e.getUnderlyingException());
}
It just means that content has been signed and then encrypted, the routine provided does not know how to deal with it, but at least tells you that. PGP protocol presents as a series of packets some of which can be wrapped in other ones (for example compressed data can also wrap signed data or simply literal data, these can be used to generate encrypted data as well, actual content always appears in literal data).
If you look at the verifyFile method in the SignedFileProcessor in the Bouncy Castle OpenPGP examples package you will see how to handle the signature data and get to the literal data containing the actual content.
I would also recommend looking at RFC 4880 so you have some idea of how the protocol works. The protocol is very loose and both GPG, BC, and the variety of products out there reflect this - that said the looseness does mean that if you try and cut and paste your way to a solution you'll end up with a disaster. It's not complicated, but understanding is required here as well.
I am a newbie to site. I have folowed this post How to Encrypt or Decrypt a File in Java?
and have everything functional (also following: http://www-users.york.ac.uk/~mal503/lore/pkencryption.htm and the previous stackoverflow post).
This may be a stupid question but where is the encrypted file after compiling? I cannot find it. How can I output it to either my desktop or src folder in java eclipse? I am trying to eventually turn this into an application where you can input a file at command line for encryption and then whenever you like decryption (strictly educational, for a class). Thank you for your time and help.
My Code:
/**
* Utility class for encrypting/decrypting files.
*
*/
public class CryptoFunction {
public static final int AES_Key_Size = 256;
Cipher pkCipher, aesCipher;
byte[] aesKey;
SecretKeySpec aeskeySpec;
/**
* Constructor: creates ciphers
*/
public CryptoFunction() throws GeneralSecurityException {
// create RSA public key cipher
pkCipher = Cipher.getInstance("RSA");
// create AES shared key cipher
aesCipher = Cipher.getInstance("AES");
}
/**
* Creates a new AES key
* A random AES key is generated to encrypt files.
* A key size (AES_Key_Size) of 256 bits is standard for AES
*/
public void makeKey() throws NoSuchAlgorithmException {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(AES_Key_Size);
SecretKey key = kgen.generateKey();
aesKey = key.getEncoded();
aeskeySpec = new SecretKeySpec(aesKey, "AES");
}
/**
* Decrypts an AES key from a file using an RSA private key
*/
#SuppressWarnings("resource")
public void loadKey(File in, File privateKeyFile) throws GeneralSecurityException, IOException {
// read private key to be used to decrypt the AES key
byte[] encodedKey = new byte[(int)privateKeyFile.length()];
new FileInputStream(privateKeyFile).read(encodedKey);
// create private key
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pk = kf.generatePrivate(privateKeySpec);
// read AES key
pkCipher.init(Cipher.DECRYPT_MODE, pk);
aesKey = new byte[AES_Key_Size/8];
CipherInputStream is = new CipherInputStream(new FileInputStream(in), pkCipher);
is.read(aesKey);
aeskeySpec = new SecretKeySpec(aesKey, "AES");
}
/**
* Encrypts the AES key to a file using an RSA public key
*/
#SuppressWarnings("resource")
public void saveKey(File out, File publicKeyFile) throws IOException, GeneralSecurityException {
// read public key to be used to encrypt the AES key
byte[] encodedKey = new byte[(int)publicKeyFile.length()];
new FileInputStream(publicKeyFile).read(encodedKey);
// create public key
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
// write AES key
pkCipher.init(Cipher.ENCRYPT_MODE, pk);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), pkCipher);
os.write(aesKey);
os.close();
}
/**
* Encrypts and then copies the contents of a given file.
*/
public void encrypt(File in, File out) throws IOException, InvalidKeyException {
aesCipher.init(Cipher.ENCRYPT_MODE, aeskeySpec);
FileInputStream is = new FileInputStream(in);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher);
copy(is, os);
os.close();
}
/**
* Decrypts and then copies the contents of a given file.
*/
public void decrypt(File in, File out) throws IOException, InvalidKeyException {
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec);
CipherInputStream is = new CipherInputStream(new FileInputStream(in), aesCipher);
FileOutputStream os = new FileOutputStream(out);
copy(is, os);
is.close();
os.close();
}
/**
* Copies a stream.
*/
private void copy(InputStream is, OutputStream os) throws IOException {
int i;
byte[] b = new byte[1024];
while((i=is.read(b))!=-1) {
os.write(b, 0, i);
System.out.println("I is copying!");
// read what we wrote
for (int j = 0; j < b.length; j++) {
System.out.print("" + (char) is.read() + '\n');
}
}
}
public void encryption(){
}
public void decryption(){
}
public static void main(String[] args) throws GeneralSecurityException, IOException {
System.out.println("Begin Encyption!"); // Display the string.
CryptoFunction secure = new CryptoFunction();
// Encrypt code
File encryptFile = new File("encrypt.data");
File publicKeyData = new File("src/publickey.der");
File originalFile = new File("src/stufftoencrypt.txt");
File secureFile = new File("secure.data");
// create AES key
secure.makeKey();
// save AES key using public key
secure.saveKey(encryptFile, publicKeyData);
// save original file securely
secure.encrypt(originalFile, secureFile);
// Decrypt code
File encryptFile = new File("encrypt.data");
File privateKeyFile = new File("privatekey");
File secureFile = new File("secure.data");
File unencryptedFile = new File("unencryptedFile");
// load AES key
secure.loadKey(encryptFile, privateKeyFile);
// decrypt file
secure.decrypt(secureFile, unencryptedFile);
System.out.println("End Encryption!"); // Display the string.
}
}
Assuming the code works (I didn't test it), you may need to just refresh your eclipse project. If the secure.data file is created in there, eclipse won't automatically refresh to show it. If you want to create the files in a specific location, put an absolute path into new File(...) e.g. to create in C:\temp, use:
File secureFile = new File("c:\\temp\\secure.data");
File unencryptedFile = new File("c:\\temp\\unencryptedFile");
You can check the absolute path of these files using:
System.out.println(unencryptedFile.getAbsolutePath());
System.out.println(secureFile.getAbsolutePath());
I have a .gpg file and a RSA private key. How can I programatically decrypt it without using operating system? e.g. without using something like Runtime.getRuntime().exec("gpg -decrypt.....");
Libraries I've found all run operating system. Like GnuPG or gnugpg-for-java.
As Skyr mentioned: Bouncy Castle is the way to go.
What do you want to do with this key? If your goal is to en- or decrypt files you might want to take a look at bouncy-gpg (shameless plug: I wrote it).
Using secret keys is actually three steps
Parse the key and put it into a PGPSecretKeyRing
Extract the secret key from the keyring
Decrypt it with the password
1. Parsing the exported key
In any case look here for the part that parses keys:
class ...
private PGPSecretKeyRingCollection secretKeyRings = new PGPSecretKeyRingCollection(EMPTY_LIST);
...
/**
* Add a new secret keyring to the public keyrings.
* .
* Can read the result of "gpg --export" and "gpg --export -a keyid"
* .
* E.g. "gpg --export-secret-key -a keyid":
* addSecretKey("-----BEGIN PGP PRIVATE KEY BLOCK----- ....".getBytes("US-ASCII")
* <p>
* The password is queried via the callback (decryptionSecretKeyPassphraseForSecretKeyId).
*
* #param encodedPrivateKey the key ascii armored or binary
* #throws IOException IO is dangerous
* #throws PGPException E.g. this is nor a valid key
*/
public void addSecretKey(byte[] encodedPrivateKey) throws IOException, PGPException {
if (encodedPrivateKey == null) {
throw new NullPointerException("encodedPrivateKey must not be null");
}
try (
final InputStream raw = new ByteArrayInputStream(encodedPrivateKey);
final InputStream decoded = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(raw)
) {
PGPSecretKeyRing pgpPrivate = new PGPSecretKeyRing(decoded, getKeyFingerPrintCalculator());
this.secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(this.secretKeyRings, pgpPrivate);
}
}
2. Getting the key from the keyring
final PGPSecretKeyRingCollection pgpSec = ...
final PGPSecretKey encryptedKey = pgpSec.getSecretKey(keyID);
3. Decrypting the key
Later you have to decrypt the key using a password like so:
/**
* Decrypt an encrypted PGP secret key.
*
* #param encryptedKey An encrypted key
* #param passphrase The passphrase for the key
* #return the decrypted secret key
* #throws PGPException E.g. wrong passphrase
*/
public static PGPPrivateKey extractPrivateKey(PGPSecretKey encryptedKey, final char[] passphrase) throws PGPException {
LOGGER.debug("Extracting secret key with key ID '0x{}'", Long.toHexString(encryptedKey.getKeyID()));
PGPDigestCalculatorProvider calcProvider = new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder(
calcProvider).setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(passphrase);
return encryptedKey.extractPrivateKey(decryptor);
}
there are too many examples that I've tried on Bouncy Castle with PGP. The common issue is keyID can't be found in KeyRing.
So, I found #Jens' bouncy-gpg (not sure if he still maintains it.)
Here is his documentation from github.io. It's simple to follow and works!
https://neuhalje.github.io/bouncy-gpg/
The Bouncy Castle library provides (among other features) an OpenPGP implementation. The package org.bouncycastle.openpgp.examples contains several usage examples, one of them showing how to encrypt/decrypt a file using a public/secret key pair (you can have a look at the examples on GrepCode or on the project's Github mirror).
I'm trying to load a private RSA key generated with ssl into java, my code is:
Generate the key:
openssl genrsa -out mykey.pem 1024
Result:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCUibP4fY2PA/sGMKMbU6usuIGcOAqgQjD6c2ylVo05Oz7pgjnE
+O0l2MFRUYUGT5KKk/W+0cAXkxaQHE3n8A8X1mHT8eMDmWnzz0PeYjDE8LQmAw8R
Y2FnVKFAB36BIjdb5FsZmCk5QYKU5+nWLMqH/j/IR5AyX5wR2SMoslUg2QIDAQAB
AoGAeJ1s7638IhLIZtldyRXjRKi6ToFPV50IKodJxOSIXt3WE0V05ZaA84eUSxUY
IOzCgRbuqVmnUz1USAdD18AecC8qc7tXTRALLy7q8fxklPwmGPUOvTFmI7gRMUnv
cWrq1gySk3SKpj0YmWnuY9Xmd2+xoWLzUeFD1CROY5OTjIECQQDDlp1+Al7+duR0
XyMlkWLIk0nIbkQ5zlTAEipzmaeTSOJi6YG3EMMz3AGuZb7tw6HFxWqeg1hyKJ+T
cTM3WTdJAkEAwmrCDKE29n3wFOBKsZZFQbDgVOUXCBs2ubEI+ALe1DJU5BlfnrhJ
OINRCNgnwSFNbwxDTkDpR6J6Av2ElAvNEQJAV0dVvk5Wj50Ecz2lFHWdLD41taAn
B9igDxnMIcvWcK4cf+ENhmCPiwvJIEa8/aLIBNYErvmTtVWVaBkirrc8KQJABr+z
+sJB6S6X/fGHRkDkKJKeRvQo54QiUzHdENbwq0cQAVcMJbNZ/1c3oen2/1JLoNY5
I+dG8dCnEaGBT65VMQJBAIDqH1Kqs5tb51cpt6h9ot31SUVud5pSML/babwp3pRs
1s6poreym4PkAyRug0Dgcj1zVLt25TlOHvrL9r3Swq8=
-----END RSA PRIVATE KEY-----
Load:
String privKeyPEM=readFile("mykey.pem");
privKeyPEM= privKeyPEM.replace("-----BEGIN RSA PRIVATE KEY-----", "").replace("\n", "");
// Remove the first and last lines
privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");
System.out.println(privKeyPEM);
// Base64 decode the data
byte [] encoded = Base64.decode(privKeyPEM);
// PKCS8 decode the encoded RSA private key
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
// Display the results
System.out.println(privKey);
and it throws an IOException : algid parse error, not a sequence. Where is the error?
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(Unknown Source)
at base54.encrypt.RSAToy.main(RSAToy.java:36)
Caused by: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(Unknown Source)
at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(Unknown Source)
at sun.security.rsa.RSAKeyFactory.generatePrivate(Unknown Source)
Further to Julien Kronegg's answer, if you are getting this error because your file has a PKCS#1 format, you can use the following steps to convert it to a PKCS#8 file.
First, save your PKCS#1 key file to a file called priv1.pem:
-----BEGIN RSA PRIVATE KEY-----
[...]
-----END RSA PRIVATE KEY-----
Then, execute the following command:
openssl pkcs8 -topk8 -inform PEM -outform PEM -in priv1.pem -out priv8.pem -nocrypt
This produces a file called priv8.pem, which is your key file in PKCS#8 format:
-----BEGIN PRIVATE KEY-----
[...]
-----END PRIVATE KEY-----
I use this from Java as follows:
String PRIVATE_RSA_KEY_PKCS8 =
"-----BEGIN PRIVATE KEY-----\n" +
"MDSTofml23d....\n" +
[...] +
"-----END PRIVATE KEY-----\n";
String key = PRIVATE_RSA_KEY_PKCS8
.replace("-----BEGIN PRIVATE KEY-----\n", "")
.replace("\n-----END PRIVATE KEY-----\n", "");
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(DatatypeConverter.parseBase64Binary(key));
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes = parseBase64Binary(encryptedNodeIdentifier);
byte[] decryptedData = cipher.doFinal(bytes);
return new String(decryptedData);
} catch (GeneralSecurityException e) {
return "";
}
Here's a modified org.apache.jmeter.protocol.oauth.sampler.PrivateKeyReader code using Java runtime only. It works for me, it can load PKCS#1 or PKCS#8 private key file and returns a PrivateKey class. (Please let me know if i messed up anything while copy/pasting).
Use it like this:
PrivateKey pk = (new PrivateKeyReader("/path/to/myfile.der")).getPrivateKey();
/**
*
*/
package default;
/****************************************************************************
* Copyright (c) 1998-2010 AOL Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Modified 23-4-2015 misterti
*
****************************************************************************/
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
/**
* Class for reading RSA private key from PEM file. It uses
* the JMeter FileServer to find the file. So the file should
* be located in the same directory as the test plan if the
* path is relative.
*
* <p/>There is a cache so each file is only read once. If file
* is changed, it will not take effect until the program
* restarts.
*
* <p/>It can read PEM files with PKCS#8 or PKCS#1 encodings.
* It doesn't support encrypted PEM files.
*
*/
public class PrivateKeyReader {
// Private key file using PKCS #1 encoding
public static final String P1_BEGIN_MARKER
= "-----BEGIN RSA PRIVATE KEY"; //$NON-NLS-1$
public static final String P1_END_MARKER
= "-----END RSA PRIVATE KEY"; //$NON-NLS-1$
// Private key file using PKCS #8 encoding
public static final String P8_BEGIN_MARKER
= "-----BEGIN PRIVATE KEY"; //$NON-NLS-1$
public static final String P8_END_MARKER
= "-----END PRIVATE KEY"; //$NON-NLS-1$
private static Map<String, PrivateKey> keyCache =
Collections.synchronizedMap(new HashMap<String, PrivateKey>());
protected final String fileName;
/**
* Create a PEM private key file reader.
*
* #param fileName The name of the PEM file
*/
public PrivateKeyReader(String fileName) {
this.fileName = fileName;
}
/**
* Get a Private Key for the file.
*
* #return Private key
* #throws IOException
*/
public PrivateKey getPrivateKey() throws IOException, GeneralSecurityException
{
PrivateKey key = null;
FileInputStream fis = null;
boolean isRSAKey = false;
try {
File f = new File(fileName);
fis = new FileInputStream(f);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
StringBuilder builder = new StringBuilder();
boolean inKey = false;
for (String line = br.readLine(); line != null; line = br.readLine()) {
if (!inKey) {
if (line.startsWith("-----BEGIN ") &&
line.endsWith(" PRIVATE KEY-----")) {
inKey = true;
isRSAKey = line.contains("RSA");
}
continue;
}
else {
if (line.startsWith("-----END ") &&
line.endsWith(" PRIVATE KEY-----")) {
inKey = false;
isRSAKey = line.contains("RSA");
break;
}
builder.append(line);
}
}
KeySpec keySpec = null;
byte[] encoded = DatatypeConverter.parseBase64Binary(builder.toString());
if (isRSAKey)
{
keySpec = getRSAKeySpec(encoded);
}
else
{
keySpec = new PKCS8EncodedKeySpec(encoded);
}
KeyFactory kf = KeyFactory.getInstance("RSA");
key = kf.generatePrivate(keySpec);
} finally {
if (fis != null)
try { fis.close(); } catch (Exception ign) {} }
return key;
}
/**
* Convert PKCS#1 encoded private key into RSAPrivateCrtKeySpec.
*
* <p/>The ASN.1 syntax for the private key with CRT is
*
* <pre>
* --
* -- Representation of RSA private key with information for the CRT algorithm.
* --
* RSAPrivateKey ::= SEQUENCE {
* version Version,
* modulus INTEGER, -- n
* publicExponent INTEGER, -- e
* privateExponent INTEGER, -- d
* prime1 INTEGER, -- p
* prime2 INTEGER, -- q
* exponent1 INTEGER, -- d mod (p-1)
* exponent2 INTEGER, -- d mod (q-1)
* coefficient INTEGER, -- (inverse of q) mod p
* otherPrimeInfos OtherPrimeInfos OPTIONAL
* }
* </pre>
*
* #param keyBytes PKCS#1 encoded key
* #return KeySpec
* #throws IOException
*/
private RSAPrivateCrtKeySpec getRSAKeySpec(byte[] keyBytes) throws IOException {
DerParser parser = new DerParser(keyBytes);
Asn1Object sequence = parser.read();
if (sequence.getType() != DerParser.SEQUENCE)
throw new IOException("Invalid DER: not a sequence"); //$NON-NLS-1$
// Parse inside the sequence
parser = sequence.getParser();
parser.read(); // Skip version
BigInteger modulus = parser.read().getInteger();
BigInteger publicExp = parser.read().getInteger();
BigInteger privateExp = parser.read().getInteger();
BigInteger prime1 = parser.read().getInteger();
BigInteger prime2 = parser.read().getInteger();
BigInteger exp1 = parser.read().getInteger();
BigInteger exp2 = parser.read().getInteger();
BigInteger crtCoef = parser.read().getInteger();
RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(
modulus, publicExp, privateExp, prime1, prime2,
exp1, exp2, crtCoef);
return keySpec;
}
}
/**
* A bare-minimum ASN.1 DER decoder, just having enough functions to
* decode PKCS#1 private keys. Especially, it doesn't handle explicitly
* tagged types with an outer tag.
*
* <p/>This parser can only handle one layer. To parse nested constructs,
* get a new parser for each layer using <code>Asn1Object.getParser()</code>.
*
* <p/>There are many DER decoders in JRE but using them will tie this
* program to a specific JCE/JVM.
*
* #author zhang
*
*/
class DerParser {
// Classes
public final static int UNIVERSAL = 0x00;
public final static int APPLICATION = 0x40;
public final static int CONTEXT = 0x80;
public final static int PRIVATE = 0xC0;
// Constructed Flag
public final static int CONSTRUCTED = 0x20;
// Tag and data types
public final static int ANY = 0x00;
public final static int BOOLEAN = 0x01;
public final static int INTEGER = 0x02;
public final static int BIT_STRING = 0x03;
public final static int OCTET_STRING = 0x04;
public final static int NULL = 0x05;
public final static int OBJECT_IDENTIFIER = 0x06;
public final static int REAL = 0x09;
public final static int ENUMERATED = 0x0a;
public final static int RELATIVE_OID = 0x0d;
public final static int SEQUENCE = 0x10;
public final static int SET = 0x11;
public final static int NUMERIC_STRING = 0x12;
public final static int PRINTABLE_STRING = 0x13;
public final static int T61_STRING = 0x14;
public final static int VIDEOTEX_STRING = 0x15;
public final static int IA5_STRING = 0x16;
public final static int GRAPHIC_STRING = 0x19;
public final static int ISO646_STRING = 0x1A;
public final static int GENERAL_STRING = 0x1B;
public final static int UTF8_STRING = 0x0C;
public final static int UNIVERSAL_STRING = 0x1C;
public final static int BMP_STRING = 0x1E;
public final static int UTC_TIME = 0x17;
public final static int GENERALIZED_TIME = 0x18;
protected InputStream in;
/**
* Create a new DER decoder from an input stream.
*
* #param in
* The DER encoded stream
*/
public DerParser(InputStream in) throws IOException {
this.in = in;
}
/**
* Create a new DER decoder from a byte array.
*
* #param The
* encoded bytes
* #throws IOException
*/
public DerParser(byte[] bytes) throws IOException {
this(new ByteArrayInputStream(bytes));
}
/**
* Read next object. If it's constructed, the value holds
* encoded content and it should be parsed by a new
* parser from <code>Asn1Object.getParser</code>.
*
* #return A object
* #throws IOException
*/
public Asn1Object read() throws IOException {
int tag = in.read();
if (tag == -1)
throw new IOException("Invalid DER: stream too short, missing tag"); //$NON-NLS-1$
int length = getLength();
byte[] value = new byte[length];
int n = in.read(value);
if (n < length)
throw new IOException("Invalid DER: stream too short, missing value"); //$NON-NLS-1$
Asn1Object o = new Asn1Object(tag, length, value);
return o;
}
/**
* Decode the length of the field. Can only support length
* encoding up to 4 octets.
*
* <p/>In BER/DER encoding, length can be encoded in 2 forms,
* <ul>
* <li>Short form. One octet. Bit 8 has value "0" and bits 7-1
* give the length.
* <li>Long form. Two to 127 octets (only 4 is supported here).
* Bit 8 of first octet has value "1" and bits 7-1 give the
* number of additional length octets. Second and following
* octets give the length, base 256, most significant digit first.
* </ul>
* #return The length as integer
* #throws IOException
*/
private int getLength() throws IOException {
int i = in.read();
if (i == -1)
throw new IOException("Invalid DER: length missing"); //$NON-NLS-1$
// A single byte short length
if ((i & ~0x7F) == 0)
return i;
int num = i & 0x7F;
// We can't handle length longer than 4 bytes
if ( i >= 0xFF || num > 4)
throw new IOException("Invalid DER: length field too big (" //$NON-NLS-1$
+ i + ")"); //$NON-NLS-1$
byte[] bytes = new byte[num];
int n = in.read(bytes);
if (n < num)
throw new IOException("Invalid DER: length too short"); //$NON-NLS-1$
return new BigInteger(1, bytes).intValue();
}
}
/**
* An ASN.1 TLV. The object is not parsed. It can
* only handle integers and strings.
*
* #author zhang
*
*/
class Asn1Object {
protected final int type;
protected final int length;
protected final byte[] value;
protected final int tag;
/**
* Construct a ASN.1 TLV. The TLV could be either a
* constructed or primitive entity.
*
* <p/>The first byte in DER encoding is made of following fields,
* <pre>
*-------------------------------------------------
*|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
*-------------------------------------------------
*| Class | CF | + Type |
*-------------------------------------------------
* </pre>
* <ul>
* <li>Class: Universal, Application, Context or Private
* <li>CF: Constructed flag. If 1, the field is constructed.
* <li>Type: This is actually called tag in ASN.1. It
* indicates data type (Integer, String) or a construct
* (sequence, choice, set).
* </ul>
*
* #param tag Tag or Identifier
* #param length Length of the field
* #param value Encoded octet string for the field.
*/
public Asn1Object(int tag, int length, byte[] value) {
this.tag = tag;
this.type = tag & 0x1F;
this.length = length;
this.value = value;
}
public int getType() {
return type;
}
public int getLength() {
return length;
}
public byte[] getValue() {
return value;
}
public boolean isConstructed() {
return (tag & DerParser.CONSTRUCTED) == DerParser.CONSTRUCTED;
}
/**
* For constructed field, return a parser for its content.
*
* #return A parser for the construct.
* #throws IOException
*/
public DerParser getParser() throws IOException {
if (!isConstructed())
throw new IOException("Invalid DER: can't parse primitive entity"); //$NON-NLS-1$
return new DerParser(value);
}
/**
* Get the value as integer
*
* #return BigInteger
* #throws IOException
*/
public BigInteger getInteger() throws IOException {
if (type != DerParser.INTEGER)
throw new IOException("Invalid DER: object is not integer"); //$NON-NLS-1$
return new BigInteger(value);
}
/**
* Get value as string. Most strings are treated
* as Latin-1.
*
* #return Java string
* #throws IOException
*/
public String getString() throws IOException {
String encoding;
switch (type) {
// Not all are Latin-1 but it's the closest thing
case DerParser.NUMERIC_STRING:
case DerParser.PRINTABLE_STRING:
case DerParser.VIDEOTEX_STRING:
case DerParser.IA5_STRING:
case DerParser.GRAPHIC_STRING:
case DerParser.ISO646_STRING:
case DerParser.GENERAL_STRING:
encoding = "ISO-8859-1"; //$NON-NLS-1$
break;
case DerParser.BMP_STRING:
encoding = "UTF-16BE"; //$NON-NLS-1$
break;
case DerParser.UTF8_STRING:
encoding = "UTF-8"; //$NON-NLS-1$
break;
case DerParser.UNIVERSAL_STRING:
throw new IOException("Invalid DER: can't handle UCS-4 string"); //$NON-NLS-1$
default:
throw new IOException("Invalid DER: object is not a string"); //$NON-NLS-1$
}
return new String(value, encoding);
}
}
try this :
java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
You get this error because you are reading a PKCS#8 key file, but your file has a PKCS#1 format (PKCS#1 has the BEGIN RSA PRIVATE KEY header while PKCS#8 has the BEGIN PRIVATE KEY header).
In order to read both PKCS#1 and PKCS#8 PEM files, I use the source code of Apache JMeter's org.apache.jmeter.protocol.oauth.sampler.PrivateKeyReader:
PrivateKey pk = (new PrivateKeyReader("/path/to/myfile.der")).getPrivateKey();
Alternately you can conver your mykey.pem to DER format and use your existing code. Following is the openssl command to do that
openssl pkcs8 -topk8 -inform PEM -outform DER -in mykey.pem -out mykey.der -nocrypt
The new formatted file will look some thing like
3082 0276 0201 0030 0d06 092a 8648 86f7
0d01 0101 0500 0482 0260 3082 025c 0201
0002 8181 00a9 0689 7676 2a63 5779 e4e8
4bb5 8e53 bc44 a975 8254 cb64 5cf2 1499
ad1c 6c35 6027 e357 ac09 0421 2b4b ddb4
1be3 a688 a848 5618 bce8 25d0 2cf2 972b
3801 a023 d6ee d824 4cfb 078f dad8 4317
fc9d 9468 27cc 2f08 f755 3293 76cb 3c60
4302 1c59 a8e8 5ac9 0576 8bb0 6bbb 8f1a
092d 7884 a405 c775 a8ca 7a2c 8037 435c
557a c9f0 a702 0301 0001 0281 8100 8462
6c53 ee25 30fd 98a9 230f f939 6a78 30c7
1114 6d59 8858 0bfa fa8a 4d92 ab13 8eea
4f06 9d61 30a1 7aa0 40aa ff58 b5fc 27fb
d710 4e3b 1f9b b4bd 95ca 1deb d165 063a
da5c 383d e428 cbf6 1f62 4549 5b96 ee2d
b811 4cff c849 9637 67e4 a5d5 1c75 f13b
8ddd b212 ba2f 7118 885e bc98 cab6 5434
d1e8 2a33 c408 1186 be05 6074 7431 0241
00d8 a1e2 d382 d52a 372e 0a62 807f 8283
e615 c7a7 180b e21c b6ce cd55 940f 542b
903f 86a0 e488 74b2 d69d d5d1 27d9 4079
72a2 80f8 c105 3e19 309b dd78 4dbe 440d
0d02 4100 c7bd e5fd ccee 60b2 cbc0 7520
53c1 6442 9f11 e0d0 969f 6315 41cd 7b3c
c279 cebe 4025 a4f4 97c9 6d1b c48f 6f01
cd3b e849 33e6 5f13 7fb6 2780 22d1 f2da
You could still load the keys if necessary,
public static PublicKey bigIntegerToPublicKey(BigInteger e, BigInteger m) {
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);
return pubKey;
}
public static PrivateKey bigIntegerToPrivateKey(BigInteger e, BigInteger m) {
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(keySpec);
return privKey;
}
all you need is the modulus and the exponent.