diff options
Diffstat (limited to 'pg/src/main/java/org/spongycastle/openpgp/examples')
12 files changed, 2411 insertions, 0 deletions
diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/ByteArrayHandler.java b/pg/src/main/java/org/spongycastle/openpgp/examples/ByteArrayHandler.java new file mode 100644 index 00000000..52b2327d --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/ByteArrayHandler.java @@ -0,0 +1,206 @@ +package org.spongycastle.openpgp.examples; + +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 org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.util.io.Streams; + +/** + * Simple routine to encrypt and decrypt using a passphrase. + * This service routine provides the basic PGP services between + * byte arrays. + * + * Note: this code plays no attention to -CONSOLE in the file name + * the specification of "_CONSOLE" in the filename. + * It also expects that a single pass phrase will have been used. + * + */ +public class ByteArrayHandler +{ + /** + * decrypt the passed in message stream + * + * @param encrypted The message to be decrypted. + * @param passPhrase Pass phrase (key) + * + * @return Clear text as a byte array. I18N considerations are + * not handled by this routine + * @exception IOException + * @exception PGPException + * @exception NoSuchProviderException + */ + public static byte[] decrypt( + byte[] encrypted, + char[] passPhrase) + throws IOException, PGPException, NoSuchProviderException + { + InputStream in = new ByteArrayInputStream(encrypted); + + in = PGPUtil.getDecoderStream(in); + + JcaPGPObjectFactory 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(); + } + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(passPhrase)); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear); + + PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + return Streams.readAll(ld.getInputStream()); + } + + /** + * 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, + char[] passPhrase, + String fileName, + int algorithm, + boolean armor) + throws IOException, PGPException, NoSuchProviderException + { + if (fileName == null) + { + fileName= PGPLiteralData.CONSOLE; + } + + byte[] compressedData = compress(clearData, fileName, CompressionAlgorithmTags.ZIP); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OutputStream out = bOut; + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(algorithm).setSecureRandom(new SecureRandom()).setProvider("SC")); + encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(passPhrase).setProvider("SC")); + + OutputStream encOut = encGen.open(out, compressedData.length); + + encOut.write(compressedData); + encOut.close(); + + if (armor) + { + out.close(); + } + + return bOut.toByteArray(); + } + + private static byte[] compress(byte[] clearData, String fileName, int algorithm) throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm); + 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, // "filename" to store + clearData.length, // length of clear data + new Date() // current time + ); + + pOut.write(clearData); + pOut.close(); + + comData.close(); + + return bOut.toByteArray(); + } + + public static void main(String[] args) throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + String passPhrase = "Dick Beck"; + char[] passArray = passPhrase.toCharArray(); + + byte[] original = "Hello world".getBytes(); + System.out.println("Starting PGP test"); + byte[] encrypted = encrypt(original, passArray, "iway", PGPEncryptedDataGenerator.CAST5, true); + + System.out.println("\nencrypted data = '"+new String(encrypted)+"'"); + byte[] decrypted= decrypt(encrypted,passArray); + + System.out.println("\ndecrypted data = '"+new String(decrypted)+"'"); + + encrypted = encrypt(original, passArray, "iway", PGPEncryptedDataGenerator.AES_256, false); + + System.out.println("\nencrypted data = '"+new String(org.spongycastle.util.encoders.Hex.encode(encrypted))+"'"); + decrypted= decrypt(encrypted, passArray); + + System.out.println("\ndecrypted data = '"+new String(decrypted)+"'"); + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java b/pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java new file mode 100644 index 00000000..62f53539 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java @@ -0,0 +1,391 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that creates clear signed files and verifies them. + * <p> + * To sign a file: ClearSignedFileProcessor -s fileName secretKey passPhrase.<br> + * <p> + * To decrypt: ClearSignedFileProcessor -v fileName signatureFile publicKeyFile. + */ +public class ClearSignedFileProcessor +{ + private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) + throws IOException + { + bOut.reset(); + + int lookAhead = -1; + int ch; + + while ((ch = fIn.read()) >= 0) + { + bOut.write(ch); + if (ch == '\r' || ch == '\n') + { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + + return lookAhead; + } + + private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) + throws IOException + { + bOut.reset(); + + int ch = lookAhead; + + do + { + bOut.write(ch); + if (ch == '\r' || ch == '\n') + { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + while ((ch = fIn.read()) >= 0); + + if (ch < 0) + { + lookAhead = -1; + } + + return lookAhead; + } + + private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) + throws IOException + { + int lookAhead = fIn.read(); + + if (lastCh == '\r' && lookAhead == '\n') + { + bOut.write(lookAhead); + lookAhead = fIn.read(); + } + + return lookAhead; + } + + /* + * verify a clear text signed file + */ + private static void verifyFile( + InputStream in, + InputStream keyIn, + String resultName) + throws Exception + { + ArmoredInputStream aIn = new ArmoredInputStream(in); + OutputStream out = new BufferedOutputStream(new FileOutputStream(resultName)); + + + + // + // write out signed section using the local line separator. + // note: trailing white space needs to be removed from the end of + // each line RFC 4880 Section 7.1 + // + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, aIn); + byte[] lineSep = getLineSeparator(); + + if (lookAhead != -1 && aIn.isClearText()) + { + byte[] line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line)); + out.write(lineSep); + + while (lookAhead != -1 && aIn.isClearText()) + { + lookAhead = readInputLine(lineOut, lookAhead, aIn); + + line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line)); + out.write(lineSep); + } + } + + out.close(); + + PGPPublicKeyRingCollection pgpRings = new PGPPublicKeyRingCollection(keyIn, new JcaKeyFingerprintCalculator()); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn); + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + PGPSignature sig = p3.get(0); + + PGPPublicKey publicKey = pgpRings.getPublicKey(sig.getKeyID()); + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), publicKey); + + // + // read the input, making sure we ignore the last newline. + // + + InputStream sigIn = new BufferedInputStream(new FileInputStream(resultName)); + + lookAhead = readInputLine(lineOut, sigIn); + + processLine(sig, lineOut.toByteArray()); + + if (lookAhead != -1) + { + do + { + lookAhead = readInputLine(lineOut, lookAhead, sigIn); + + sig.update((byte)'\r'); + sig.update((byte)'\n'); + + processLine(sig, lineOut.toByteArray()); + } + while (lookAhead != -1); + } + + sigIn.close(); + + if (sig.verify()) + { + System.out.println("signature verified."); + } + else + { + System.out.println("signature verification failed."); + } + } + + private static byte[] getLineSeparator() + { + String nl = System.getProperty("line.separator"); + byte[] nlBytes = new byte[nl.length()]; + + for (int i = 0; i != nlBytes.length; i++) + { + nlBytes[i] = (byte)nl.charAt(i); + } + + return nlBytes; + } + + /* + * create a clear text signed file. + */ + private static void signFile( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + String digestName) + throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException + { + int digest; + + if (digestName.equals("SHA256")) + { + digest = PGPUtil.SHA256; + } + else if (digestName.equals("SHA384")) + { + digest = PGPUtil.SHA384; + } + else if (digestName.equals("SHA512")) + { + digest = PGPUtil.SHA512; + } + else if (digestName.equals("MD5")) + { + digest = PGPUtil.MD5; + } + else if (digestName.equals("RIPEMD160")) + { + digest = PGPUtil.RIPEMD160; + } + else + { + digest = PGPUtil.SHA1; + } + + PGPSecretKey pgpSecKey = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSecKey.getPublicKey().getAlgorithm(), digest).setProvider("SC")); + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey); + + Iterator it = pgpSecKey.getPublicKey().getUserIDs(); + if (it.hasNext()) + { + spGen.setSignerUserID(false, (String)it.next()); + sGen.setHashedSubpackets(spGen.generate()); + } + + InputStream fIn = new BufferedInputStream(new FileInputStream(fileName)); + ArmoredOutputStream aOut = new ArmoredOutputStream(out); + + aOut.beginClearText(digest); + + // + // note the last \n/\r/\r\n in the file is ignored + // + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, fIn); + + processLine(aOut, sGen, lineOut.toByteArray()); + + if (lookAhead != -1) + { + do + { + lookAhead = readInputLine(lineOut, lookAhead, fIn); + + sGen.update((byte)'\r'); + sGen.update((byte)'\n'); + + processLine(aOut, sGen, lineOut.toByteArray()); + } + while (lookAhead != -1); + } + + fIn.close(); + + aOut.endClearText(); + + BCPGOutputStream bOut = new BCPGOutputStream(aOut); + + sGen.generate().encode(bOut); + + aOut.close(); + } + + private static void processLine(PGPSignature sig, byte[] line) + throws SignatureException, IOException + { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) + { + sig.update(line, 0, length); + } + } + + private static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line) + throws SignatureException, IOException + { + // note: trailing white space needs to be removed from the end of + // each line for signature calculation RFC 4880 Section 7.1 + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) + { + sGen.update(line, 0, length); + } + + aOut.write(line, 0, line.length); + } + + private static int getLengthWithoutSeparatorOrTrailingWhitespace(byte[] line) + { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) + { + end--; + } + + return end + 1; + } + + private static boolean isLineEnding(byte b) + { + return b == '\r' || b == '\n'; + } + + private static int getLengthWithoutWhiteSpace(byte[] line) + { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) + { + end--; + } + + return end + 1; + } + + private static boolean isWhiteSpace(byte b) + { + return isLineEnding(b) || b == '\t' || b == ' '; + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + InputStream keyIn = PGPUtil.getDecoderStream(new FileInputStream(args[2])); + FileOutputStream out = new FileOutputStream(args[1] + ".asc"); + + if (args.length == 4) + { + signFile(args[1], keyIn, out, args[3].toCharArray(), "SHA1"); + } + else + { + signFile(args[1], keyIn, out, args[3].toCharArray(), args[4]); + } + } + else if (args[0].equals("-v")) + { + if (args[1].indexOf(".asc") < 0) + { + System.err.println("file needs to end in \".asc\""); + System.exit(1); + } + FileInputStream in = new FileInputStream(args[1]); + InputStream keyIn = PGPUtil.getDecoderStream(new FileInputStream(args[2])); + + verifyFile(in, keyIn, args[1].substring(0, args[1].length() - 4)); + } + else + { + System.err.println("usage: ClearSignedFileProcessor [-s file keyfile passPhrase]|[-v sigFile keyFile]"); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/DSAElGamalKeyRingGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/examples/DSAElGamalKeyRingGenerator.java new file mode 100644 index 00000000..e67e6f22 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/DSAElGamalKeyRingGenerator.java @@ -0,0 +1,139 @@ +package org.spongycastle.openpgp.examples; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; + +/** + * A simple utility class that generates a public/secret keyring containing a DSA signing + * key and an El Gamal key for encryption. + * <p> + * usage: DSAElGamalKeyRingGenerator [-a] identity passPhrase + * <p> + * Where identity is the name to be associated with the public key. The keys are placed + * in the files pub.[asc|bpg] and secret.[asc|bpg]. + * <p> + * <b>Note</b>: this example encrypts the secret key using AES_256, many PGP products still + * do not support this, if you are having problems importing keys try changing the algorithm + * id to PGPEncryptedData.CAST5. CAST5 is more widely supported. + */ +public class DSAElGamalKeyRingGenerator +{ + private static void exportKeyPair( + OutputStream secretOut, + OutputStream publicOut, + KeyPair dsaKp, + KeyPair elgKp, + String identity, + char[] passPhrase, + boolean armor) + throws IOException, InvalidKeyException, NoSuchProviderException, SignatureException, PGPException + { + if (armor) + { + secretOut = new ArmoredOutputStream(secretOut); + } + + PGPKeyPair dsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new JcaPGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + identity, sha1Calc, null, null, new JcaPGPContentSignerBuilder(dsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + keyRingGen.addSubKey(elgKeyPair); + + keyRingGen.generateSecretKeyRing().encode(secretOut); + + secretOut.close(); + + if (armor) + { + publicOut = new ArmoredOutputStream(publicOut); + } + + keyRingGen.generatePublicKeyRing().encode(publicOut); + + publicOut.close(); + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length < 2) + { + System.out.println("DSAElGamalKeyRingGenerator [-a] identity passPhrase"); + System.exit(0); + } + + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(1024); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(elParams); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + + if (args[0].equals("-a")) + { + if (args.length < 3) + { + System.out.println("DSAElGamalKeyRingGenerator [-a] identity passPhrase"); + System.exit(0); + } + + FileOutputStream out1 = new FileOutputStream("secret.asc"); + FileOutputStream out2 = new FileOutputStream("pub.asc"); + + exportKeyPair(out1, out2, dsaKp, elgKp, args[1], args[2].toCharArray(), true); + } + else + { + FileOutputStream out1 = new FileOutputStream("secret.bpg"); + FileOutputStream out2 = new FileOutputStream("pub.bpg"); + + exportKeyPair(out1, out2, dsaKp, elgKp, args[0], args[1].toCharArray(), false); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java b/pg/src/main/java/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java new file mode 100644 index 00000000..fc9fa531 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java @@ -0,0 +1,199 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.Security; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that creates seperate signatures for files and verifies them. + * <p> + * To sign a file: DetachedSignatureProcessor -s [-a] fileName secretKey passPhrase.<br> + * If -a is specified the output file will be "ascii-armored". + * <p> + * To decrypt: DetachedSignatureProcessor -v fileName signatureFile publicKeyFile. + * <p> + * Note: this example will silently overwrite files. + * It also expects that a single pass phrase + * will have been used. + */ +public class DetachedSignatureProcessor +{ + private static void verifySignature( + String fileName, + String inputFileName, + String keyFileName) + throws GeneralSecurityException, IOException, PGPException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + + verifySignature(fileName, in, keyIn); + + keyIn.close(); + in.close(); + } + + /* + * verify the signature in in against the file fileName. + */ + private static void verifySignature( + String fileName, + InputStream in, + InputStream keyIn) + throws GeneralSecurityException, IOException, PGPException + { + in = PGPUtil.getDecoderStream(in); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(in); + PGPSignatureList p3; + + Object o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) + { + PGPCompressedData c1 = (PGPCompressedData)o; + + pgpFact = new JcaPGPObjectFactory(c1.getDataStream()); + + p3 = (PGPSignatureList)pgpFact.nextObject(); + } + else + { + p3 = (PGPSignatureList)o; + } + + PGPPublicKeyRingCollection pgpPubRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator()); + + + InputStream dIn = new BufferedInputStream(new FileInputStream(fileName)); + + PGPSignature sig = p3.get(0); + PGPPublicKey key = pgpPubRingCollection.getPublicKey(sig.getKeyID()); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), key); + + int ch; + while ((ch = dIn.read()) >= 0) + { + sig.update((byte)ch); + } + + dIn.close(); + + if (sig.verify()) + { + System.out.println("signature verified."); + } + else + { + System.out.println("signature verification failed."); + } + } + + private static void createSignature( + String inputFileName, + String keyFileName, + String outputFileName, + char[] pass, + boolean armor) + throws GeneralSecurityException, IOException, PGPException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + + createSignature(inputFileName, keyIn, out, pass, armor); + + out.close(); + keyIn.close(); + } + + private static void createSignature( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + boolean armor) + throws GeneralSecurityException, IOException, PGPException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPSecretKey pgpSec = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + BCPGOutputStream bOut = new BCPGOutputStream(out); + + InputStream fIn = new BufferedInputStream(new FileInputStream(fileName)); + + int ch; + while ((ch = fIn.read()) >= 0) + { + sGen.update((byte)ch); + } + + fIn.close(); + + sGen.generate().encode(bOut); + + if (armor) + { + out.close(); + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + if (args[1].equals("-a")) + { + createSignature(args[2], args[3], args[2] + ".asc", args[4].toCharArray(), true); + } + else + { + createSignature(args[1], args[2], args[1] + ".bpg", args[3].toCharArray(), false); + } + } + else if (args[0].equals("-v")) + { + verifySignature(args[1], args[2], args[3]); + } + else + { + System.err.println("usage: DetachedSignatureProcessor [-s [-a] file keyfile passPhrase]|[-v file sigFile keyFile]"); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/DirectKeySignature.java b/pg/src/main/java/org/spongycastle/openpgp/examples/DirectKeySignature.java new file mode 100644 index 00000000..00f14a23 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/DirectKeySignature.java @@ -0,0 +1,115 @@ +package org.spongycastle.openpgp.examples; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.security.Security; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.sig.NotationData; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that directly signs a public key and writes the signed key to "SignedKey.asc" in + * the current working directory. + * <p> + * To sign a key: DirectKeySignature secretKeyFile secretKeyPass publicKeyFile(key to be signed) NotationName NotationValue.<br/> + * </p><p> + * To display a NotationData packet from a publicKey previously signed: DirectKeySignature signedPublicKeyFile.<br/> + * </p><p> + * <b>Note</b>: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * </p> + */ +public class DirectKeySignature +{ + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length == 1) + { + PGPPublicKeyRing ring = new PGPPublicKeyRing(PGPUtil.getDecoderStream(new FileInputStream(args[0])), new JcaKeyFingerprintCalculator()); + PGPPublicKey key = ring.getPublicKey(); + + // iterate through all direct key signautures and look for NotationData subpackets + Iterator iter = key.getSignaturesOfType(PGPSignature.DIRECT_KEY); + while(iter.hasNext()) + { + PGPSignature sig = (PGPSignature)iter.next(); + + System.out.println("Signature date is: " + sig.getHashedSubPackets().getSignatureCreationTime()); + + NotationData[] data = sig.getHashedSubPackets().getNotationDataOccurences();//.getSubpacket(SignatureSubpacketTags.NOTATION_DATA); + + for (int i = 0; i < data.length; i++) + { + System.out.println("Found Notaion named '"+data[i].getNotationName()+"' with content '"+data[i].getNotationValue()+"'."); + } + } + } + else if (args.length == 5) + { + // gather command line arguments + PGPSecretKeyRing secRing = new PGPSecretKeyRing(PGPUtil.getDecoderStream(new FileInputStream(args[0])), new JcaKeyFingerprintCalculator()); + String secretKeyPass = args[1]; + PGPPublicKeyRing ring = new PGPPublicKeyRing(PGPUtil.getDecoderStream(new FileInputStream(args[2])), new JcaKeyFingerprintCalculator()); + String notationName = args[3]; + String notationValue = args[4]; + + // create the signed keyRing + PGPPublicKeyRing sRing = new PGPPublicKeyRing(new ByteArrayInputStream(signPublicKey(secRing.getSecretKey(), secretKeyPass, ring.getPublicKey(), notationName, notationValue)), new JcaKeyFingerprintCalculator()); + ring = sRing; + + // write the created keyRing to file + ArmoredOutputStream out = new ArmoredOutputStream(new FileOutputStream("SignedKey.asc")); + sRing.encode(out); + out.flush(); + out.close(); + } + else + { + System.err.println("usage: DirectKeySignature secretKeyFile secretKeyPass publicKeyFile(key to be signed) NotationName NotationValue"); + System.err.println("or: DirectKeySignature signedPublicKeyFile"); + + } + } + + private static byte[] signPublicKey(PGPSecretKey secretKey, String secretKeyPass, PGPPublicKey keyToBeSigned, String notationName, String notationValue) throws Exception + { + PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(secretKeyPass.toCharArray())); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.DIRECT_KEY, pgpPrivKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + boolean isHumanReadable = true; + + spGen.setNotationData(true, isHumanReadable, notationName, notationValue); + + PGPSignatureSubpacketVector packetVector = spGen.generate(); + + sGen.setHashedSubpackets(packetVector); + + return PGPPublicKey.addCertification(keyToBeSigned, sGen.generate()).getEncoded(); + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedFileProcessor.java b/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedFileProcessor.java new file mode 100644 index 00000000..cd893eaa --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedFileProcessor.java @@ -0,0 +1,280 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +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.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.Streams; + +/** + * A simple utility class that encrypts/decrypts public key based + * encryption files. + * <p> + * To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.<br> + * If -a is specified the output file will be "ascii-armored". + * If -i is specified the output file will be have integrity checking added. + * <p> + * To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase. + * <p> + * Note 1: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * <p> + * Note 2: if an empty file name has been specified in the literal data object contained in the + * encrypted packet a file with the name filename.out will be generated in the current working directory. + */ +public class KeyBasedFileProcessor +{ + private static void decryptFile( + String inputFileName, + String keyFileName, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + decryptFile(in, keyIn, passwd, defaultFileName); + keyIn.close(); + in.close(); + } + + /** + * decrypt the passed in message stream + */ + private static void decryptFile( + InputStream in, + InputStream keyIn, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + in = PGPUtil.getDecoderStream(in); + + try + { + JcaPGPObjectFactory 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 it = enc.getEncryptedDataObjects(); + PGPPrivateKey sKey = null; + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator()); + + while (sKey == null && it.hasNext()) + { + pbe = (PGPPublicKeyEncryptedData)it.next(); + + sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd); + } + + if (sKey == null) + { + throw new IllegalArgumentException("secret key for message not found."); + } + + InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(sKey)); + + JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); + + Object message = plainFact.nextObject(); + + if (message instanceof PGPCompressedData) + { + PGPCompressedData cData = (PGPCompressedData)message; + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); + + message = pgpFact.nextObject(); + } + + if (message instanceof PGPLiteralData) + { + PGPLiteralData ld = (PGPLiteralData)message; + + String outFileName = ld.getFileName(); + if (outFileName.length() == 0) + { + outFileName = defaultFileName; + } + + InputStream unc = ld.getInputStream(); + OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName)); + + Streams.pipeAll(unc, fOut); + + fOut.close(); + } + 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()) + { + System.err.println("message failed integrity check"); + } + else + { + System.err.println("message integrity check passed"); + } + } + else + { + System.err.println("no message integrity check"); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + private static void encryptFile( + String outputFileName, + String inputFileName, + String encKeyFileName, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException, PGPException + { + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName); + encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck); + out.close(); + } + + private static void encryptFile( + OutputStream out, + String fileName, + PGPPublicKey encKey, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + try + { + byte[] bytes = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator( + new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC")); + + encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("SC")); + + OutputStream cOut = encGen.open(out, bytes.length); + + cOut.write(bytes); + cOut.close(); + + if (armor) + { + out.close(); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length == 0) + { + System.err.println("usage: KeyBasedFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + return; + } + + if (args[0].equals("-e")) + { + if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) + { + encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0)); + } + else if (args[1].equals("-i")) + { + encryptFile(args[2] + ".bpg", args[2], args[3], false, true); + } + else + { + encryptFile(args[1] + ".bpg", args[1], args[2], false, false); + } + } + else if (args[0].equals("-d")) + { + decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out"); + } + else + { + System.err.println("usage: KeyBasedFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedLargeFileProcessor.java b/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedLargeFileProcessor.java new file mode 100644 index 00000000..cd39d05a --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedLargeFileProcessor.java @@ -0,0 +1,284 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +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.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.Streams; + +/** + * A simple utility class that encrypts/decrypts public key based + * encryption large files. + * <p> + * To encrypt a file: KeyBasedLargeFileProcessor -e [-a|-ai] fileName publicKeyFile.<br> + * If -a is specified the output file will be "ascii-armored". + * If -i is specified the output file will be have integrity checking added. + * <p> + * To decrypt: KeyBasedLargeFileProcessor -d fileName secretKeyFile passPhrase. + * <p> + * Note 1: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * <p> + * Note 2: this example generates partial packets to encode the file, the output it generates + * will not be readable by older PGP products or products that don't support partial packet + * encoding. + * <p> + * Note 3: if an empty file name has been specified in the literal data object contained in the + * encrypted packet a file with the name filename.out will be generated in the current working directory. + */ +public class KeyBasedLargeFileProcessor +{ + private static void decryptFile( + String inputFileName, + String keyFileName, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + decryptFile(in, keyIn, passwd, defaultFileName); + keyIn.close(); + in.close(); + } + + /** + * decrypt the passed in message stream + */ + private static void decryptFile( + InputStream in, + InputStream keyIn, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + in = PGPUtil.getDecoderStream(in); + + try + { + JcaPGPObjectFactory 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 it = enc.getEncryptedDataObjects(); + PGPPrivateKey sKey = null; + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator()); + + while (sKey == null && it.hasNext()) + { + pbe = (PGPPublicKeyEncryptedData)it.next(); + + sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd); + } + + if (sKey == null) + { + throw new IllegalArgumentException("secret key for message not found."); + } + + InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(sKey)); + + JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); + + PGPCompressedData cData = (PGPCompressedData)plainFact.nextObject(); + + InputStream compressedStream = new BufferedInputStream(cData.getDataStream()); + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(compressedStream); + + Object message = pgpFact.nextObject(); + + if (message instanceof PGPLiteralData) + { + PGPLiteralData ld = (PGPLiteralData)message; + + String outFileName = ld.getFileName(); + if (outFileName.length() == 0) + { + outFileName = defaultFileName; + } + + InputStream unc = ld.getInputStream(); + OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName)); + + Streams.pipeAll(unc, fOut); + + fOut.close(); + } + 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()) + { + System.err.println("message failed integrity check"); + } + else + { + System.err.println("message integrity check passed"); + } + } + else + { + System.err.println("no message integrity check"); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + private static void encryptFile( + String outputFileName, + String inputFileName, + String encKeyFileName, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException, PGPException + { + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName); + encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck); + out.close(); + } + + private static void encryptFile( + OutputStream out, + String fileName, + PGPPublicKey encKey, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + try + { + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC")); + + cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("SC")); + + OutputStream cOut = cPk.open(out, new byte[1 << 16]); + + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + PGPUtil.writeFileToLiteralData(comData.open(cOut), PGPLiteralData.BINARY, new File(fileName), new byte[1 << 16]); + + comData.close(); + + cOut.close(); + + if (armor) + { + out.close(); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length == 0) + { + System.err.println("usage: KeyBasedLargeFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + return; + } + + if (args[0].equals("-e")) + { + if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) + { + encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0)); + } + else if (args[1].equals("-i")) + { + encryptFile(args[2] + ".bpg", args[2], args[3], false, true); + } + else + { + encryptFile(args[1] + ".bpg", args[1], args[2], false, false); + } + } + else if (args[0].equals("-d")) + { + decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out"); + } + else + { + System.err.println("usage: KeyBasedLargeFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/PBEFileProcessor.java b/pg/src/main/java/org/spongycastle/openpgp/examples/PBEFileProcessor.java new file mode 100644 index 00000000..5e9fc83c --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/PBEFileProcessor.java @@ -0,0 +1,214 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +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 org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.util.io.Streams; + +/** + * A simple utility class that encrypts/decrypts password based + * encryption files. + * <p> + * To encrypt a file: PBEFileProcessor -e [-ai] fileName passPhrase.<br> + * If -a is specified the output file will be "ascii-armored".<br> + * If -i is specified the output file will be "integrity protected". + * <p> + * To decrypt: PBEFileProcessor -d fileName passPhrase. + * <p> + * Note: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + */ +public class PBEFileProcessor +{ + private static void decryptFile(String inputFileName, char[] passPhrase) + throws IOException, NoSuchProviderException, PGPException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + decryptFile(in, passPhrase); + in.close(); + } + + /* + * decrypt the passed in message stream + */ + private static void decryptFile( + InputStream in, + char[] passPhrase) + throws IOException, NoSuchProviderException, PGPException + { + in = PGPUtil.getDecoderStream(in); + + JcaPGPObjectFactory 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(); + } + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(passPhrase)); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear); + + // + // if we're trying to read a file generated by someone other than us + // the data might not be compressed, so we check the return type from + // the factory and behave accordingly. + // + o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) + { + PGPCompressedData cData = (PGPCompressedData)o; + + pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); + + o = pgpFact.nextObject(); + } + + PGPLiteralData ld = (PGPLiteralData)o; + InputStream unc = ld.getInputStream(); + + OutputStream fOut = new BufferedOutputStream(new FileOutputStream(ld.getFileName())); + + Streams.pipeAll(unc, fOut); + + fOut.close(); + + if (pbe.isIntegrityProtected()) + { + if (!pbe.verify()) + { + System.err.println("message failed integrity check"); + } + else + { + System.err.println("message integrity check passed"); + } + } + else + { + System.err.println("no message integrity check"); + } + } + + private static void encryptFile( + String outputFileName, + String inputFileName, + char[] passPhrase, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + encryptFile(out, inputFileName, passPhrase, armor, withIntegrityCheck); + out.close(); + } + + private static void encryptFile( + OutputStream out, + String fileName, + char[] passPhrase, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + try + { + byte[] compressedData = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5) + .setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC")); + + encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(passPhrase).setProvider("SC")); + + OutputStream encOut = encGen.open(out, compressedData.length); + + encOut.write(compressedData); + encOut.close(); + + if (armor) + { + out.close(); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-e")) + { + if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) + { + encryptFile(args[2] + ".asc", args[2], args[3].toCharArray(), true, (args[1].indexOf('i') > 0)); + } + else if (args[1].equals("-i")) + { + encryptFile(args[2] + ".bpg", args[2], args[3].toCharArray(), false, true); + } + else + { + encryptFile(args[1] + ".bpg", args[1], args[2].toCharArray(), false, false); + } + } + else if (args[0].equals("-d")) + { + decryptFile(args[1], args[2].toCharArray()); + } + else + { + System.err.println("usage: PBEFileProcessor -e [-ai]|-d file passPhrase"); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/PGPExampleUtil.java b/pg/src/main/java/org/spongycastle/openpgp/examples/PGPExampleUtil.java new file mode 100644 index 00000000..c83ee189 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/PGPExampleUtil.java @@ -0,0 +1,155 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.NoSuchProviderException; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +class PGPExampleUtil +{ + static byte[] compressFile(String fileName, int algorithm) throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm); + PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, + new File(fileName)); + comData.close(); + return bOut.toByteArray(); + } + + /** + * Search a secret key ring collection for a secret key corresponding to keyID if it + * exists. + * + * @param pgpSec a secret key ring collection. + * @param keyID keyID we want. + * @param pass passphrase to decrypt secret key with. + * @return the private key. + * @throws PGPException + * @throws NoSuchProviderException + */ + static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) + throws PGPException, NoSuchProviderException + { + PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); + + if (pgpSecKey == null) + { + return null; + } + + return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + } + + static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName)); + PGPPublicKey pubKey = readPublicKey(keyIn); + keyIn.close(); + return pubKey; + } + + /** + * A simple routine that opens a key ring file and loads the first available key + * suitable for encryption. + * + * @param input data stream containing the public key data + * @return the first public key found. + * @throws IOException + * @throws PGPException + */ + static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException + { + PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection( + PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); + + // + // we just loop through the collection till we find a key suitable for encryption, in the real + // world you would probably want to be a bit smarter about this. + // + + Iterator keyRingIter = pgpPub.getKeyRings(); + while (keyRingIter.hasNext()) + { + PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next(); + + Iterator keyIter = keyRing.getPublicKeys(); + while (keyIter.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)keyIter.next(); + + if (key.isEncryptionKey()) + { + return key; + } + } + } + + throw new IllegalArgumentException("Can't find encryption key in key ring."); + } + + static PGPSecretKey readSecretKey(String fileName) throws IOException, PGPException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName)); + PGPSecretKey secKey = readSecretKey(keyIn); + keyIn.close(); + return secKey; + } + + /** + * A simple routine that opens a key ring file and loads the first available key + * suitable for signature generation. + * + * @param input stream to read the secret key ring collection from. + * @return a secret key. + * @throws IOException on a problem with using the input stream. + * @throws PGPException if there is an issue parsing the input stream. + */ + static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException + { + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); + + // + // we just loop through the collection till we find a key suitable for encryption, in the real + // world you would probably want to be a bit smarter about this. + // + + Iterator keyRingIter = pgpSec.getKeyRings(); + while (keyRingIter.hasNext()) + { + PGPSecretKeyRing keyRing = (PGPSecretKeyRing)keyRingIter.next(); + + Iterator keyIter = keyRing.getSecretKeys(); + while (keyIter.hasNext()) + { + PGPSecretKey key = (PGPSecretKey)keyIter.next(); + + if (key.isSigningKey()) + { + return key; + } + } + } + + throw new IllegalArgumentException("Can't find signing key in key ring."); + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/PubringDump.java b/pg/src/main/java/org/spongycastle/openpgp/examples/PubringDump.java new file mode 100644 index 00000000..de565b96 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/PubringDump.java @@ -0,0 +1,100 @@ +package org.spongycastle.openpgp.examples; + +import java.io.FileInputStream; +import java.security.Security; +import java.util.Iterator; + +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.util.encoders.Hex; + +/** + * Basic class which just lists the contents of the public key file passed + * as an argument. If the file contains more than one "key ring" they are + * listed in the order found. + */ +public class PubringDump +{ + public static String getAlgorithm( + int algId) + { + switch (algId) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + return "RSA_GENERAL"; + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + return "RSA_ENCRYPT"; + case PublicKeyAlgorithmTags.RSA_SIGN: + return "RSA_SIGN"; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + return "ELGAMAL_ENCRYPT"; + case PublicKeyAlgorithmTags.DSA: + return "DSA"; + case PublicKeyAlgorithmTags.EC: + return "EC"; + case PublicKeyAlgorithmTags.ECDSA: + return "ECDSA"; + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + return "ELGAMAL_GENERAL"; + case PublicKeyAlgorithmTags.DIFFIE_HELLMAN: + return "DIFFIE_HELLMAN"; + } + + return "unknown"; + } + + public static void main(String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key rings + // + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection( + PGPUtil.getDecoderStream(new FileInputStream(args[0])), new JcaKeyFingerprintCalculator()); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + try + { + pgpPub.getPublicKey(); + } + catch (Exception e) + { + e.printStackTrace(); + continue; + } + + Iterator it = pgpPub.getPublicKeys(); + boolean first = true; + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (first) + { + System.out.println("Key ID: " + Long.toHexString(pgpKey.getKeyID())); + first = false; + } + else + { + System.out.println("Key ID: " + Long.toHexString(pgpKey.getKeyID()) + " (subkey)"); + } + System.out.println(" Algorithm: " + getAlgorithm(pgpKey.getAlgorithm())); + System.out.println(" Fingerprint: " + new String(Hex.encode(pgpKey.getFingerprint()))); + } + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/RSAKeyPairGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/examples/RSAKeyPairGenerator.java new file mode 100644 index 00000000..de653453 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/RSAKeyPairGenerator.java @@ -0,0 +1,112 @@ +package org.spongycastle.openpgp.examples; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; + +/** + * A simple utility class that generates a RSA PGPPublicKey/PGPSecretKey pair. + * <p> + * usage: RSAKeyPairGenerator [-a] identity passPhrase + * <p> + * Where identity is the name to be associated with the public key. The keys are placed + * in the files pub.[asc|bpg] and secret.[asc|bpg]. + */ +public class RSAKeyPairGenerator +{ + private static void exportKeyPair( + OutputStream secretOut, + OutputStream publicOut, + KeyPair pair, + String identity, + char[] passPhrase, + boolean armor) + throws IOException, InvalidKeyException, NoSuchProviderException, SignatureException, PGPException + { + if (armor) + { + secretOut = new ArmoredOutputStream(secretOut); + } + + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyPair keyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, pair, new Date()); + PGPSecretKey secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, identity, sha1Calc, null, null, new JcaPGPContentSignerBuilder(keyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.CAST5, sha1Calc).setProvider("SC").build(passPhrase)); + + secretKey.encode(secretOut); + + secretOut.close(); + + if (armor) + { + publicOut = new ArmoredOutputStream(publicOut); + } + + PGPPublicKey key = secretKey.getPublicKey(); + + key.encode(publicOut); + + publicOut.close(); + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC"); + + kpg.initialize(1024); + + KeyPair kp = kpg.generateKeyPair(); + + if (args.length < 2) + { + System.out.println("RSAKeyPairGenerator [-a] identity passPhrase"); + System.exit(0); + } + + if (args[0].equals("-a")) + { + if (args.length < 3) + { + System.out.println("RSAKeyPairGenerator [-a] identity passPhrase"); + System.exit(0); + } + + FileOutputStream out1 = new FileOutputStream("secret.asc"); + FileOutputStream out2 = new FileOutputStream("pub.asc"); + + exportKeyPair(out1, out2, kp, args[1], args[2].toCharArray(), true); + } + else + { + FileOutputStream out1 = new FileOutputStream("secret.bpg"); + FileOutputStream out2 = new FileOutputStream("pub.bpg"); + + exportKeyPair(out1, out2, kp, args[0], args[1].toCharArray(), false); + } + } +} diff --git a/pg/src/main/java/org/spongycastle/openpgp/examples/SignedFileProcessor.java b/pg/src/main/java/org/spongycastle/openpgp/examples/SignedFileProcessor.java new file mode 100644 index 00000000..4cd79133 --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/examples/SignedFileProcessor.java @@ -0,0 +1,216 @@ +package org.spongycastle.openpgp.examples; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that signs and verifies files. + * <p> + * To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.<br> + * If -a is specified the output file will be "ascii-armored". + * <p> + * To decrypt: SignedFileProcessor -v fileName publicKeyFile. + * <p> + * <b>Note</b>: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * <p> + * <b>Note</b>: the example also makes use of PGP compression. If you are having difficulty getting it + * to interoperate with other PGP programs try removing the use of compression first. + */ +public class SignedFileProcessor +{ + /* + * verify the passed in file as being correctly signed. + */ + private static void verifyFile( + InputStream in, + InputStream keyIn) + throws Exception + { + in = PGPUtil.getDecoderStream(in); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(in); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new JcaPGPObjectFactory(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), new JcaKeyFingerprintCalculator()); + + PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID()); + FileOutputStream out = new FileOutputStream(p2.getFileName()); + + ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), key); + + 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."); + } + } + + /** + * Generate an encapsulated signed file. + * + * @param fileName + * @param keyIn + * @param out + * @param pass + * @param armor + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws PGPException + * @throws SignatureException + */ + private static void signFile( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + boolean armor) + throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPSecretKey pgpSec = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + 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.ZLIB); + + BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(out)); + + sGen.generateOnePassVersion(false).encode(bOut); + + File file = new File(fileName); + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, file); + FileInputStream fIn = new FileInputStream(file); + int ch; + + while ((ch = fIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bOut); + + cGen.close(); + + if (armor) + { + out.close(); + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + if (args[1].equals("-a")) + { + FileInputStream keyIn = new FileInputStream(args[3]); + FileOutputStream out = new FileOutputStream(args[2] + ".asc"); + + signFile(args[2], keyIn, out, args[4].toCharArray(), true); + } + else + { + FileInputStream keyIn = new FileInputStream(args[2]); + FileOutputStream out = new FileOutputStream(args[1] + ".bpg"); + + signFile(args[1], keyIn, out, args[3].toCharArray(), false); + } + } + else if (args[0].equals("-v")) + { + FileInputStream in = new FileInputStream(args[1]); + FileInputStream keyIn = new FileInputStream(args[2]); + + verifyFile(in, keyIn); + } + else + { + System.err.println("usage: SignedFileProcessor -v|-s [-a] file keyfile [passPhrase]"); + } + } +}
\ No newline at end of file |