{"id":178,"date":"2011-08-11T11:02:29","date_gmt":"2011-08-11T18:02:29","guid":{"rendered":"http:\/\/sloanseaman.com\/wordpress\/?p=178"},"modified":"2012-05-13T15:50:08","modified_gmt":"2012-05-13T22:50:08","slug":"pgp-encryptiondecryption-in-java","status":"publish","type":"post","link":"http:\/\/sloanseaman.com\/wordpress\/2011\/08\/11\/pgp-encryptiondecryption-in-java\/","title":{"rendered":"PGP Encryption\/Decryption in Java"},"content":{"rendered":"<div style=\"border: 1px solid black; background:#3399FF; font-size:12px; padding: 5px;\">\nA new version of the code (and a new post) has been posted at :<br \/>\n<a href=\"http:\/\/sloanseaman.com\/wordpress\/2012\/05\/13\/revisited-pgp-encryptiondecryption-in-java\/\">http:\/\/sloanseaman.com\/wordpress\/2012\/05\/13\/revisited-pgp-encryptiondecryption-in-java\/<\/a><br \/>\nPlease note that if you are using Bouncy Castles libraries < 1.47 you should use the code in this post\n<\/div>\n<\/p>\n<p>I have a requirement where I need to decrypt a file that is PGP encrypted for a project I am working on.  At the same time I need a unit test for my code so I need to encrypt a file as well.<\/p>\n<p\/>\nI started digging around and found that there is really no documentation I could find that covered the whole process.  Some postings only covered encryption, some decryption, but none covered both as well as how to make the public\/private keys.<\/p>\n<p\/>\nSo, here you go \ud83d\ude42<\/p>\n<p\/>\n<h3>The Downloads\/Installs<\/h3>\n<p>Before you even start coding we need to discuss Java Security.  The JRE by default ships with security for cryptography called Java Cryptography Extension (JCE).  The JCE will support PGP but because import\/export of cryptography can be sketchy, it only supports weak keys by default (think keys that wouldn&#8217;t be too hard to crack).  PGP won&#8217;t work under this environment so you need to first address this.<\/p>\n<p\/>\n<h4>JCE Update<\/h4>\n<p>Credit where due: This part is taken mainly from this article <a href=\"http:\/\/www.ngs.ac.uk\/tools\/jcepolicyfiles\">on jce policy files<\/a><br \/>\n<br \/>\nGo to the <a href=\"http:\/\/www.oracle.com\/technetwork\/java\/javase\/downloads\/index.html\">Java download page<\/a> and go all the way to the bottom and download the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.  The number at the end (currently 6 or 7) is just the JRE version.<\/p>\n<p\/>\nExtract the files and copy the two jars to your JRE&#8217;s \/lib\/security directory.  This will override two existing jars and will allow you to use strong keys in security.<\/p>\n<p\/>\n<h4>PGP Tools<\/h4>\n<p>Next you need to create you public and private key files.  Since Symantec bought PGP in 2010 most of the freeware stuff has disappeared and most links lead to Symantec&#8217;s website.  I found an old freeware version of PGP (Windows only) at <a href=\"http:\/\/www.pgpi.org\/products\/pgp\/versions\/freeware\/win32\/6.5.8\/\">http:\/\/www.pgpi.org\/products\/pgp\/versions\/freeware\/win32\/6.5.8\/<\/a>.  Please note that newer version don&#8217;t work on Windows 7 or are owned by Symantac.<\/p>\n<p\/>\nInstall the program and then launch it by running PGPKeys.exe. To use it:<\/p>\n<ol>\n<li>Select <b>Keys -> New Key<\/b><\/li>\n<li><b>Click Next<\/b><\/li>\n<li>Enter your information<\/li>\n<li><b>Click Next<\/b><\/li>\n<li>Use the Diffie-Hellman\/DSS key Pair Type<\/li>\n<li><b>Click Next<\/b><\/li>\n<li>Select your Key Pair Size (I just used 1024 since I&#8217;m not that worried about it for now)<\/li>\n<li><b>Click Next<\/b><\/li>\n<li>Leave your Key Expiration as never expires<\/li>\n<li><b>Click Next<\/b><\/li>\n<li>Enter the passphrase (password) you want to use<\/li>\n<li><b>Click Next<\/b><\/li>\n<li>If you entered less than 8 characters you will be warned.  <b>Click Next<\/b><\/li>\n<li>Key will be generated<\/li>\n<li><b>Click Next<\/b><\/li>\n<li><b>Click Finish<\/b><\/li>\n<\/ol>\n<p>The files that were generated and that you will need to use in your code can be found by going to <b>Edit -> Options -> Files<\/b>.<br \/>\nFind these files and copy them to someplace where your code can reach them.<\/p>\n<p\/>\nSide note: If you just need a basic public\/private key you can generate ones at <a href=\"https:\/\/www.igolder.com\/pgp\/generate-key\/\">https:\/\/www.igolder.com\/pgp\/generate-key\/<\/a><\/p>\n<h4>BouncyCastle&#8217;s PGP Libraries<\/h4>\n<p>I&#8217;m using the PGP library from <a href=\"http:\/\/www.bouncycastle.org\/\">Bouncy Castle<\/a>.  Honestly it&#8217;s poorly documented and rather complex but it&#8217;s the best and most common one that I could find.<br \/>\n<br \/>\nEither download the latest from <a href=\"http:\/\/www.bouncycastle.org\/latest_releases.html\">http:\/\/www.bouncycastle.org\/latest_releases.html<\/a> or use maven like so:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n    &lt;dependency&gt;\r\n\t&lt;groupId&gt;org.bouncycastle&lt;\/groupId&gt;\r\n\t&lt;artifactId&gt;bcpg-jdk16&lt;\/artifactId&gt;\r\n        &lt;version&gt;1.45&lt;\/version&gt;\r\n    &lt;\/dependency&gt;\r\n<\/pre>\n<p>You are now (finally) ready to actually code something!<\/p>\n<p\/>\n<h3>The Code<\/h3>\n<p>First lets write a general util that will handle any InputStreams (shouldn&#8217;t we all be using Readers by now?) and then lets write one that wraps the first one and will handle files for us.<\/p>\n<p\/>\nHere is the first util which will handle any type of InputStream:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\nimport java.io.ByteArrayOutputStream;\r\nimport java.io.File;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.io.OutputStream;\r\nimport java.security.NoSuchProviderException;\r\nimport java.security.SecureRandom;\r\nimport java.security.Security;\r\nimport java.util.Iterator;\r\n\r\nimport org.bouncycastle.bcpg.ArmoredOutputStream;\r\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\r\nimport org.bouncycastle.openpgp.PGPCompressedData;\r\nimport org.bouncycastle.openpgp.PGPCompressedDataGenerator;\r\nimport org.bouncycastle.openpgp.PGPEncryptedData;\r\nimport org.bouncycastle.openpgp.PGPEncryptedDataGenerator;\r\nimport org.bouncycastle.openpgp.PGPEncryptedDataList;\r\nimport org.bouncycastle.openpgp.PGPException;\r\nimport org.bouncycastle.openpgp.PGPLiteralData;\r\nimport org.bouncycastle.openpgp.PGPObjectFactory;\r\nimport org.bouncycastle.openpgp.PGPOnePassSignatureList;\r\nimport org.bouncycastle.openpgp.PGPPrivateKey;\r\nimport org.bouncycastle.openpgp.PGPPublicKey;\r\nimport org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;\r\nimport org.bouncycastle.openpgp.PGPPublicKeyRing;\r\nimport org.bouncycastle.openpgp.PGPPublicKeyRingCollection;\r\nimport org.bouncycastle.openpgp.PGPSecretKey;\r\nimport org.bouncycastle.openpgp.PGPSecretKeyRingCollection;\r\n\r\n\/**\r\n * Taken from org.bouncycastle.openpgp.examples\r\n *\r\n * @author seamans\r\n *\r\n *\/\r\npublic class PGPUtil {\r\n\r\n\t@SuppressWarnings(&quot;unchecked&quot;)\r\n\tpublic static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {\r\n        in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);\r\n\r\n        PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);\r\n\r\n        \/\/\r\n        \/\/ we just loop through the collection till we find a key suitable for encryption, in the real\r\n        \/\/ world you would probably want to be a bit smarter about this.\r\n        \/\/\r\n        PGPPublicKey key = null;\r\n\r\n        \/\/\r\n        \/\/ iterate through the key rings.\r\n        \/\/\r\n        Iterator&lt;PGPPublicKeyRing&gt; rIt = pgpPub.getKeyRings();\r\n\r\n        while (key == null &amp;&amp; rIt.hasNext()) {\r\n            PGPPublicKeyRing kRing = rIt.next();\r\n            Iterator&lt;PGPPublicKey&gt; kIt = kRing.getPublicKeys();\r\n            while (key == null &amp;&amp; kIt.hasNext()) {\r\n                PGPPublicKey k = kIt.next();\r\n\r\n                if (k.isEncryptionKey()) {\r\n                    key = k;\r\n                }\r\n            }\r\n        }\r\n\r\n        if (key == null) {\r\n            throw new IllegalArgumentException(&quot;Can't find encryption key in key ring.&quot;);\r\n        }\r\n\r\n        return key;\r\n    }\r\n\r\n    \/**\r\n     * Load a secret key ring collection from keyIn and find the secret key corresponding to\r\n     * keyID if it exists.\r\n     *\r\n     * @param keyIn input stream representing a key ring collection.\r\n     * @param keyID keyID we want.\r\n     * @param pass passphrase to decrypt secret key with.\r\n     * @return\r\n     * @throws IOException\r\n     * @throws PGPException\r\n     * @throws NoSuchProviderException\r\n     *\/\r\n    private static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID, char[] pass)\r\n    \tthrows IOException, PGPException, NoSuchProviderException\r\n    {\r\n        PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(\r\n        \torg.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn));\r\n\r\n        PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);\r\n\r\n        if (pgpSecKey == null) {\r\n            return null;\r\n        }\r\n\r\n        return pgpSecKey.extractPrivateKey(pass, &quot;BC&quot;);\r\n    }\r\n\r\n    \/**\r\n     * decrypt the passed in message stream\r\n     *\/\r\n    @SuppressWarnings(&quot;unchecked&quot;)\r\n\tpublic static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd)\r\n    \tthrows Exception\r\n    {\r\n    \tSecurity.addProvider(new BouncyCastleProvider());\r\n\r\n        in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);\r\n\r\n        PGPObjectFactory pgpF = new PGPObjectFactory(in);\r\n        PGPEncryptedDataList enc;\r\n\r\n        Object o = pgpF.nextObject();\r\n        \/\/\r\n        \/\/ the first object might be a PGP marker packet.\r\n        \/\/\r\n        if (o instanceof  PGPEncryptedDataList) {\r\n            enc = (PGPEncryptedDataList) o;\r\n        } else {\r\n            enc = (PGPEncryptedDataList) pgpF.nextObject();\r\n        }\r\n\r\n        \/\/\r\n        \/\/ find the secret key\r\n        \/\/\r\n        Iterator&lt;PGPPublicKeyEncryptedData&gt; it = enc.getEncryptedDataObjects();\r\n        PGPPrivateKey sKey = null;\r\n        PGPPublicKeyEncryptedData pbe = null;\r\n\r\n        while (sKey == null &amp;&amp; it.hasNext()) {\r\n            pbe = it.next();\r\n\r\n            sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);\r\n        }\r\n\r\n        if (sKey == null) {\r\n            throw new IllegalArgumentException(&quot;Secret key for message not found.&quot;);\r\n        }\r\n\r\n        InputStream clear = pbe.getDataStream(sKey, &quot;BC&quot;);\r\n\r\n        PGPObjectFactory plainFact = new PGPObjectFactory(clear);\r\n\r\n        Object message = plainFact.nextObject();\r\n\r\n        if (message instanceof  PGPCompressedData) {\r\n            PGPCompressedData cData = (PGPCompressedData) message;\r\n            PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());\r\n\r\n            message = pgpFact.nextObject();\r\n        }\r\n\r\n        if (message instanceof  PGPLiteralData) {\r\n            PGPLiteralData ld = (PGPLiteralData) message;\r\n\r\n            InputStream unc = ld.getInputStream();\r\n            int ch;\r\n\r\n            while ((ch = unc.read()) &gt;= 0) {\r\n                out.write(ch);\r\n            }\r\n        } else if (message instanceof  PGPOnePassSignatureList) {\r\n            throw new PGPException(&quot;Encrypted message contains a signed message - not literal data.&quot;);\r\n        } else {\r\n            throw new PGPException(&quot;Message is not a simple encrypted file - type unknown.&quot;);\r\n        }\r\n\r\n        if (pbe.isIntegrityProtected()) {\r\n            if (!pbe.verify()) {\r\n            \tthrow new PGPException(&quot;Message failed integrity check&quot;);\r\n            }\r\n        }\r\n    }\r\n\r\n    public static void encryptFile(OutputStream out, String fileName,\r\n        PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck)\r\n        throws IOException, NoSuchProviderException, PGPException\r\n    {\r\n    \tSecurity.addProvider(new BouncyCastleProvider());\r\n\r\n        if (armor) {\r\n            out = new ArmoredOutputStream(out);\r\n        }\r\n\r\n        ByteArrayOutputStream bOut = new ByteArrayOutputStream();\r\n\r\n        PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(\r\n            PGPCompressedData.ZIP);\r\n\r\n        org.bouncycastle.openpgp.PGPUtil.writeFileToLiteralData(comData.open(bOut),\r\n            PGPLiteralData.BINARY, new File(fileName));\r\n\r\n        comData.close();\r\n\r\n        PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(\r\n            PGPEncryptedData.CAST5, withIntegrityCheck,\r\n            new SecureRandom(), &quot;BC&quot;);\r\n\r\n        cPk.addMethod(encKey);\r\n\r\n        byte[] bytes = bOut.toByteArray();\r\n\r\n        OutputStream cOut = cPk.open(out, bytes.length);\r\n\r\n        cOut.write(bytes);\r\n\r\n        cOut.close();\r\n\r\n        out.close();\r\n    }\r\n\r\n}\r\n<\/pre>\n<p>I&#8217;m not going to go over it line-by-line because, honestly, I haven&#8217;t looked at it that hard so I&#8217;m not sure what it&#8217;s all doing \ud83d\ude09  Sorry.<\/p>\n<p\/>\n<p>Next let&#8217;s write a util that allows us to easily work with files (and Spring, if you are into that sort of thing :))<br \/>\n<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\nimport java.io.FileInputStream;\r\nimport java.io.FileOutputStream;\r\n\r\npublic class PGPFileProcessor {\r\n\r\n\tprivate String passphrase;\r\n\r\n\tprivate String keyFile;\r\n\r\n\tprivate String inputFile;\r\n\r\n\tprivate String outputFile;\r\n\r\n\tprivate boolean asciiArmored = false;\r\n\r\n\tprivate boolean integrityCheck = true;\r\n\r\n\tpublic boolean encrypt() throws Exception {\r\n\t\tFileInputStream keyIn = new FileInputStream(keyFile);\r\n        FileOutputStream out = new FileOutputStream(outputFile);\r\n        PGPUtil.encryptFile(out, inputFile, PGPUtil.readPublicKey(keyIn),\r\n        \tasciiArmored, integrityCheck);\r\n        out.close();\r\n        keyIn.close();\r\n        return true;\r\n\t}\r\n\r\n\tpublic boolean decrypt() throws Exception {\r\n\t\t FileInputStream in = new FileInputStream(inputFile);\r\n         FileInputStream keyIn = new FileInputStream(keyFile);\r\n         FileOutputStream out = new FileOutputStream(outputFile);\r\n         PGPUtil.decryptFile(in, out, keyIn, passphrase.toCharArray());\r\n         in.close();\r\n         out.close();\r\n         keyIn.close();\r\n         return true;\r\n\t}\r\n\r\n\tpublic boolean isAsciiArmored() {\r\n\t\treturn asciiArmored;\r\n\t}\r\n\r\n\tpublic void setAsciiArmored(boolean asciiArmored) {\r\n\t\tthis.asciiArmored = asciiArmored;\r\n\t}\r\n\r\n\tpublic boolean isIntegrityCheck() {\r\n\t\treturn integrityCheck;\r\n\t}\r\n\r\n\tpublic void setIntegrityCheck(boolean integrityCheck) {\r\n\t\tthis.integrityCheck = integrityCheck;\r\n\t}\r\n\r\n\tpublic String getPassphrase() {\r\n\t\treturn passphrase;\r\n\t}\r\n\r\n\tpublic void setPassphrase(String passphrase) {\r\n\t\tthis.passphrase = passphrase;\r\n\t}\r\n\r\n\tpublic String getKeyFile() {\r\n\t\treturn keyFile;\r\n\t}\r\n\r\n\tpublic void setKeyFile(String keyFile) {\r\n\t\tthis.keyFile = keyFile;\r\n\t}\r\n\r\n\tpublic String getInputFile() {\r\n\t\treturn inputFile;\r\n\t}\r\n\r\n\tpublic void setInputFile(String inputFile) {\r\n\t\tthis.inputFile = inputFile;\r\n\t}\r\n\r\n\tpublic String getOutputFile() {\r\n\t\treturn outputFile;\r\n\t}\r\n\r\n\tpublic void setOutputFile(String outputFile) {\r\n\t\tthis.outputFile = outputFile;\r\n\t}\r\n\r\n}\r\n<\/pre>\n<p\/>\nRemember that if you use the PGPFileProcessor in Spring and intend on changing things programmatically via the get\/sets you need to make the scope of the bean &#8220;prototype&#8221; otherwise you will have issues in a multi-threaded environment.<\/p>\n<p\/>\nThat should do it!  You should be able to encrypt and decrypt files (streams really) using PGP!<\/p>\n<h3>Support for signed files<\/h3>\n<p>If you wish to support signed files, Mateusz Klos posted an excellent comment <a href=\"#comment-626\">below<\/a> that modifies the above code to support signed files.  <\/p>\n<p\/>\nI didn&#8217;t incorporate it into my code because I think he should get full credit for the modifications \ud83d\ude42<\/p>\n<h3>Common Errors<\/h3>\n<p><B>Illegal key size or default parameters<\/B><br \/>\n<br \/>\nIt&#8217;s yelling at you because the default JRE security can&#8217;t handle PGP.<br \/>\nYou didn&#8217;t follow the instructions (above) on updating the JCE jars in your JRE&#8217;s lib\/security directory.<\/p>\n<p\/>\n<p><B>Secret key for message not found<\/B><br \/>\n<br \/>\nRegenerate you pubring and secring using the PGP tool.  Odds are something is wrong with them<\/p>\n<h3>The Comments<\/h3>\n<p><small>(Note: Added 4\/2\/2010)<\/small><br \/>\nThere had been a lot of great activity in the comments as well as some very kind readers who have posted code snippets to do things that my initial library did not do.  I highly suggest you dig through the comments if what you are looking for is not in my original post.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A new version of the code (and a new post) has been posted at : http:\/\/sloanseaman.com\/wordpress\/2012\/05\/13\/revisited-pgp-encryptiondecryption-in-java\/ Please note that if you are using Bouncy Castles libraries < 1.47 you should use the code in this post I have a requirement &hellip; <a href=\"http:\/\/sloanseaman.com\/wordpress\/2011\/08\/11\/pgp-encryptiondecryption-in-java\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7,10],"tags":[29,18,19,15],"_links":{"self":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/178"}],"collection":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/comments?post=178"}],"version-history":[{"count":26,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/178\/revisions"}],"predecessor-version":[{"id":278,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/178\/revisions\/278"}],"wp:attachment":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/media?parent=178"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/categories?post=178"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/tags?post=178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}