totpgenerator

Generate TOTP verification codes based on encrypted GPG files.
git clone git://git.wimdupont.com/totpgenerator.git
Log | Files | Refs | README | LICENSE

GpgService.java (5315B)


      1 package com.wimdupont.service;
      2 
      3 import org.bouncycastle.crypto.DataLengthException;
      4 import org.bouncycastle.jce.provider.BouncyCastleProvider;
      5 import org.bouncycastle.openpgp.PGPCompressedData;
      6 import org.bouncycastle.openpgp.PGPEncryptedData;
      7 import org.bouncycastle.openpgp.PGPEncryptedDataList;
      8 import org.bouncycastle.openpgp.PGPException;
      9 import org.bouncycastle.openpgp.PGPLiteralData;
     10 import org.bouncycastle.openpgp.PGPObjectFactory;
     11 import org.bouncycastle.openpgp.PGPPrivateKey;
     12 import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
     13 import org.bouncycastle.openpgp.PGPSecretKey;
     14 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
     15 import org.bouncycastle.openpgp.PGPUtil;
     16 import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
     17 import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
     18 import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
     19 import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
     20 import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
     21 
     22 import java.io.ByteArrayOutputStream;
     23 import java.io.FileInputStream;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.security.Security;
     27 import java.util.Iterator;
     28 
     29 public class GpgService {
     30 
     31     private final ApplicationProperties applicationProperties;
     32 
     33     public GpgService(ApplicationProperties applicationProperties){
     34         this.applicationProperties = applicationProperties;
     35     }
     36 
     37     public byte[] decrypt(String fileName) throws IOException, PGPException {
     38         Security.addProvider(new BouncyCastleProvider());
     39 
     40         var encryptedDataObjects = getPgpEncryptedDataIterator(fileName);
     41 
     42         if (encryptedDataObjects.hasNext()) {
     43             var pgpPublicKeyEncryptedData = (PGPPublicKeyEncryptedData) encryptedDataObjects.next();
     44             var pgpPrivateKey = retrievePgpPrivateKey(pgpPublicKeyEncryptedData);
     45 
     46             return getByteArrayOutputStream(pgpPublicKeyEncryptedData, pgpPrivateKey);
     47         } else {
     48             throw new DataLengthException(String.format("No encrypted dataObjects found in file: %s", fileName));
     49         }
     50     }
     51 
     52     private Iterator<PGPEncryptedData> getPgpEncryptedDataIterator(String fileName) throws IOException {
     53         var secretDecoderStream = PGPUtil.getDecoderStream(KeyRetriever
     54                 .getSecretKeyInputStream(fileName, applicationProperties.getDirPath()));
     55         var pgpObjectFactory = new PGPObjectFactory(secretDecoderStream, new BcKeyFingerprintCalculator());
     56         var nextObject = pgpObjectFactory.nextObject();
     57 
     58         return (nextObject instanceof PGPEncryptedDataList)
     59                 ? ((PGPEncryptedDataList) nextObject).getEncryptedDataObjects()
     60                 : ((PGPEncryptedDataList) pgpObjectFactory.nextObject()).getEncryptedDataObjects();
     61     }
     62 
     63     private PGPPrivateKey retrievePgpPrivateKey(PGPPublicKeyEncryptedData pgpPublicKeyEncryptedData) throws IOException, PGPException {
     64         var pgpPrivateKey = readSecretKey(new FileInputStream(applicationProperties.getSecretFile()), pgpPublicKeyEncryptedData.getKeyID())
     65                 .extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider())
     66                         .build(PasswordReader.getPassword()));
     67 
     68         if (pgpPrivateKey == null) {
     69             throw new IllegalArgumentException("Secret key for message not found.");
     70         }
     71 
     72         return pgpPrivateKey;
     73     }
     74 
     75     private PGPSecretKey readSecretKey(InputStream inputStream,
     76                                        long keyId) throws IOException, PGPException {
     77         inputStream = PGPUtil.getDecoderStream(inputStream);
     78         var pgpSecretKeyRings = new PGPSecretKeyRingCollection(inputStream, new BcKeyFingerprintCalculator());
     79         var secretKey = pgpSecretKeyRings.getSecretKey(keyId);
     80 
     81         if (secretKey == null) {
     82             throw new IllegalArgumentException("Can't find encryption key in key ring.");
     83         }
     84 
     85         return secretKey;
     86     }
     87 
     88     private byte[] getByteArrayOutputStream(PGPPublicKeyEncryptedData pgpPublicKeyEncryptedData,
     89                                             PGPPrivateKey pgpPrivateKey) throws IOException, PGPException {
     90         ByteArrayOutputStream byteArrayOutputStream;
     91 
     92         try (var dataStream = toDataStream(pgpPublicKeyEncryptedData, pgpPrivateKey)) {
     93             byteArrayOutputStream = new ByteArrayOutputStream();
     94             int dataRead;
     95 
     96             while ((dataRead = dataStream.read()) >= 0) {
     97                 byteArrayOutputStream.write(dataRead);
     98             }
     99         }
    100 
    101         return byteArrayOutputStream.toByteArray();
    102     }
    103 
    104     private InputStream toDataStream(PGPPublicKeyEncryptedData pgpPublicKeyEncryptedData,
    105                                      PGPPrivateKey pgpPrivateKey) throws PGPException, IOException {
    106         var pgpDataStream = pgpPublicKeyEncryptedData.getDataStream(
    107                 new BcPublicKeyDataDecryptorFactory(pgpPrivateKey));
    108         var jcaPGPObjectFactory = new JcaPGPObjectFactory(pgpDataStream);
    109         var pgpCompressedDataStream = ((PGPCompressedData) jcaPGPObjectFactory.nextObject()).getDataStream();
    110         jcaPGPObjectFactory = new JcaPGPObjectFactory(pgpCompressedDataStream);
    111 
    112         return ((PGPLiteralData) jcaPGPObjectFactory.nextObject()).getDataStream();
    113     }
    114 
    115 }