How to sign and verify the file in JAVA

不问归期 提交于 2021-01-28 07:08:26

问题


I have to place a file in remote SFTP server before that I have to sign the file with the private key and they will verify it with the public key. I am getting "PGP Signature verification failed" error from the response file.

So I tried to verify the sign from JAVA. Still, I am getting false value from the signature verify method.

Any help would be appreciated.

Here's a code that I've put together.

public class SignAndVerify {

static final KeyFingerPrintCalculator FP_CALC = new BcKeyFingerprintCalculator();
private static File publicKeyFile = new File("\\publicSign.asc");
private static File privateKeyFile = new File("\\privateSign.asc");
private static final BouncyCastleProvider provider = new BouncyCastleProvider();

static {
    Security.addProvider(provider);
}

public static void signFile(String fileName, PGPSecretKey secretKey, String secretPwd, boolean armor, OutputStream out)
        throws PGPException {
    BCPGOutputStream bOut = null;
    OutputStream lOut = null;
    InputStream fIn = null;
    try {
        OutputStream theOut = armor ? new ArmoredOutputStream(out) : out;

        PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(
                new JcePBESecretKeyDecryptorBuilder().setProvider(provider).build(secretPwd.toCharArray()));
        PGPSignatureGenerator sGen = new PGPSignatureGenerator(
                new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1)
                        .setProvider(provider));

        sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);

        Iterator<String> it = secretKey.getPublicKey().getUserIDs();
        if (it.hasNext()) {
            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();

            spGen.setSignerUserID(false, (String) it.next());
            sGen.setHashedSubpackets(spGen.generate());
        }

        bOut = new BCPGOutputStream(theOut);

        sGen.generateOnePassVersion(false).encode(bOut);

        PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();

        lOut = lGen.open(bOut, PGPLiteralData.BINARY, "filename", new Date(), new byte[2048]);

        fIn = new BufferedInputStream(new FileInputStream(fileName));
        byte[] buf = new byte[2048];
        int ch;

        while ((ch = fIn.read(buf)) >= 0) {
            lOut.write(ch);
            sGen.update(buf, 0, ch);
        }

        lGen.close();

        sGen.generate().encode(bOut);

        theOut.close();
    } catch (Exception e) {
        throw new PGPException("Error in sign", e);
    } finally {
        try {
            if (bOut != null) {
                bOut.close();
            }
            if(lOut != null) {
                lOut.close();
            }
            if(fIn != null) {
            fIn.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public static boolean verifyFile(InputStream lin, PGPPublicKey publicKey) throws PGPException {
    try {
        InputStream in = PGPUtil.getDecoderStream(lin);

        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;

        ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider(provider), publicKey);

        while ((ch = dIn.read()) >= 0) {
            ops.update((byte) ch);
        }

        PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();

        return ops.verify(p3.get(0));

    } catch (Exception e) {
        throw new PGPException("Error in verify", e);
    }
}

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<PGPSecretKeyRing> keyRingIter = pgpSec.getKeyRings();
    while (keyRingIter.hasNext()) {
        PGPSecretKeyRing keyRing = keyRingIter.next();

        Iterator<PGPSecretKey> keyIter = keyRing.getSecretKeys();
        while (keyIter.hasNext()) {
            PGPSecretKey key = keyIter.next();

            if (key.isSigningKey()) {
                return key;
            }
        }
    }

    throw new IllegalArgumentException("Can't find signing key in key ring..");
}

private static PGPPublicKey readPublicKeyFromCol(InputStream in) throws IOException, PGPException {
    PGPPublicKeyRing pkRing = null;
    PGPPublicKeyRingCollection pkCol = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(in), FP_CALC);
    println("key ring size=" + pkCol.size());
    Iterator<PGPPublicKeyRing> it = pkCol.getKeyRings();
    while (it.hasNext()) {
        pkRing =  it.next();
        Iterator<PGPPublicKey> pkIt = pkRing.getPublicKeys();
        while (pkIt.hasNext()) {
            PGPPublicKey key =  pkIt.next();
            println("Encryption key = " + key.isEncryptionKey() + ", Master key = " + key.isMasterKey());
            if (key.isEncryptionKey())
                return key;
        }
    }
    return null;
}

public static void main(String[] args) throws Exception {
    println("Inside Class..");

    String fileName = "\\fileToBeSigned.xml";
    String secretKey = "Passphrase";
    String outFileName = "\\signedFile.xml";

    OutputStream out = new BufferedOutputStream(new FileOutputStream(outFileName));

    InputStream lin = new BufferedInputStream(new FileInputStream(outFileName));

    PGPSecretKey pgpSec = readSecretKey(new BufferedInputStream(new FileInputStream(privateKeyFile)));
    signFile(fileName, pgpSec, secretKey, true, out);

    PGPPublicKey encKey = readPublicKeyFromCol(new FileInputStream(publicKeyFile));

    Boolean lverify = verifyFile(lin, encKey);

    println("result is ::" + lverify);
    out.close();
    lin.close();

}

private static void println(String msg) {
    System.out.println(msg);
}

}


回答1:


First, the signer is looking for any sign-capable key, but the verifier is looking for any encrypt-capable key. Depending on how and when you generated your key(s), these may not be the same: it has been considered good practice, and the default for at least two decades, to use a subkey for encryption (only) -- and sometimes also a different subkey for data signing (only, leaving the the masterkey for key-signing aka 'certifying' only). But it is technically possible to have an RSA masterkey with capability for both data-sign and encrypt (and optionally also certify, though it goes unused); in GPG this can be done by generating in 'expert' mode, or in recent versions by editting after generation. Since you don't show us details of your secret/private key(s) -- and shouldn't unless these are test-only key(s) you can afford to compromise -- it's impossible to tell your case. If you are actually using different keys to try to sign and verify then of course it couldn't ever work right.

In general the recipient should use the key specified by the keyid in the message: a sender encrypts using any encrypt-capable publickey, identifies that key in the message, and the receiver decrypts using the privatekey half of the key chosen by the sender; a sender signs using any sign-capable privatekey, identifies that key in the message, and the receiver verifies using the publickey half of the key chosen by the sender. This will require reorganizing your code to choose the verify key only after reading the signature packet. For now I just added a check in verifyFile that sig.getKeyID() == publicKey.getKeyID() .

That leaves the more serious error in your data handling in signFile

    byte[] buf = new byte[2048];
    int ch;
    while ((ch = fIn.read(buf)) >= 0) {
        lOut.write(ch);
        sGen.update(buf, 0, ch);
    }

You compute the signature over all of the data from the input file, but you put in the message only one byte for each bufferful of data; see the javadoc for OutputStream.write(int). Since the verifier uses the data in the message, that data is now totally different from the data that was signed, and the signature shouldn't and doesn't verify. As well as the message being useless to the receiver. Instead do

  lOut.write(buf,0,ch);

or switch to byte-at-a-time processing like you have in verifyFile

  int ch; // no byte[] buf
  while( (ch = fIn.read()) >= 0 ){
    lOut.write((byte)ch); // cast not needed but clarifies intent
    sig.update((byte)ch); 
  }


来源:https://stackoverflow.com/questions/57574714/how-to-sign-and-verify-the-file-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!