org.bouncycastle.gpg.SExprParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcpg-jdk15on Show documentation
Show all versions of bcpg-jdk15on Show documentation
The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.5 to JDK 1.7. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.
package org.bouncycastle.gpg;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Date;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.bcpg.DSAPublicBCPGKey;
import org.bouncycastle.bcpg.DSASecretBCPGKey;
import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
import org.bouncycastle.bcpg.ECPublicBCPGKey;
import org.bouncycastle.bcpg.ECSecretBCPGKey;
import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.RSAPublicBCPGKey;
import org.bouncycastle.bcpg.RSASecretBCPGKey;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SecretKeyPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
/**
* A parser for secret keys stored in SExpr
*/
public class SExprParser
{
private final PGPDigestCalculatorProvider digestProvider;
/**
* Base constructor.
*
* @param digestProvider a provider for digest calculations. Used to confirm key protection hashes.
*/
public SExprParser(PGPDigestCalculatorProvider digestProvider)
{
this.digestProvider = digestProvider;
}
/**
* Parse a secret key from one of the GPG S expression keys associating it with the passed in public key.
*
* @return a secret key object.
*/
public PGPSecretKey parseSecretKey(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, PGPPublicKey pubKey)
throws IOException, PGPException
{
SXprUtils.skipOpenParenthesis(inputStream);
String type;
type = SXprUtils.readString(inputStream, inputStream.read());
if (type.equals("protected-private-key")
|| type.equals("private-key"))
{
SXprUtils.skipOpenParenthesis(inputStream);
String keyType = SXprUtils.readString(inputStream, inputStream.read());
if (keyType.equals("ecc"))
{
SXprUtils.skipOpenParenthesis(inputStream);
String curveID = SXprUtils.readString(inputStream, inputStream.read());
String curveName = SXprUtils.readString(inputStream, inputStream.read());
SXprUtils.skipCloseParenthesis(inputStream);
byte[] qVal;
SXprUtils.skipOpenParenthesis(inputStream);
type = SXprUtils.readString(inputStream, inputStream.read());
if (type.equals("q"))
{
qVal = SXprUtils.readBytes(inputStream, inputStream.read());
}
else
{
throw new PGPException("no q value found");
}
SXprUtils.skipCloseParenthesis(inputStream);
BigInteger d = processECSecretKey(inputStream, curveID, curveName, qVal, keyProtectionRemoverFactory);
if (curveName.startsWith("NIST "))
{
curveName = curveName.substring("NIST ".length());
}
ECPublicBCPGKey basePubKey = new ECDSAPublicBCPGKey(ECNamedCurveTable.getOID(curveName), new BigInteger(1, qVal));
ECPublicBCPGKey assocPubKey = (ECPublicBCPGKey)pubKey.getPublicKeyPacket().getKey();
if (!basePubKey.getCurveOID().equals(assocPubKey.getCurveOID())
|| !basePubKey.getEncodedPoint().equals(assocPubKey.getEncodedPoint()))
{
throw new PGPException("passed in public key does not match secret key");
}
return new PGPSecretKey(new SecretKeyPacket(pubKey.getPublicKeyPacket(), SymmetricKeyAlgorithmTags.NULL, null, null, new ECSecretBCPGKey(d).getEncoded()), pubKey);
}
else if (keyType.equals("dsa"))
{
BigInteger p = readBigInteger("p", inputStream);
BigInteger q = readBigInteger("q", inputStream);
BigInteger g = readBigInteger("g", inputStream);
BigInteger y = readBigInteger("y", inputStream);
BigInteger x = processDSASecretKey(inputStream, p, q, g, y, keyProtectionRemoverFactory);
DSAPublicBCPGKey basePubKey = new DSAPublicBCPGKey(p, q, g, y);
DSAPublicBCPGKey assocPubKey = (DSAPublicBCPGKey)pubKey.getPublicKeyPacket().getKey();
if (!basePubKey.getP().equals(assocPubKey.getP())
|| !basePubKey.getQ().equals(assocPubKey.getQ())
|| !basePubKey.getG().equals(assocPubKey.getG())
|| !basePubKey.getY().equals(assocPubKey.getY()))
{
throw new PGPException("passed in public key does not match secret key");
}
return new PGPSecretKey(new SecretKeyPacket(pubKey.getPublicKeyPacket(), SymmetricKeyAlgorithmTags.NULL, null, null, new DSASecretBCPGKey(x).getEncoded()), pubKey);
}
else if (keyType.equals("elg"))
{
BigInteger p = readBigInteger("p", inputStream);
BigInteger g = readBigInteger("g", inputStream);
BigInteger y = readBigInteger("y", inputStream);
BigInteger x = processElGamalSecretKey(inputStream, p, g, y, keyProtectionRemoverFactory);
ElGamalPublicBCPGKey basePubKey = new ElGamalPublicBCPGKey(p, g, y);
ElGamalPublicBCPGKey assocPubKey = (ElGamalPublicBCPGKey)pubKey.getPublicKeyPacket().getKey();
if (!basePubKey.getP().equals(assocPubKey.getP())
|| !basePubKey.getG().equals(assocPubKey.getG())
|| !basePubKey.getY().equals(assocPubKey.getY()))
{
throw new PGPException("passed in public key does not match secret key");
}
return new PGPSecretKey(new SecretKeyPacket(pubKey.getPublicKeyPacket(), SymmetricKeyAlgorithmTags.NULL, null, null, new ElGamalSecretBCPGKey(x).getEncoded()), pubKey);
}
else if (keyType.equals("rsa"))
{
BigInteger n = readBigInteger("n", inputStream);
BigInteger e = readBigInteger("e", inputStream);
BigInteger[] values = processRSASecretKey(inputStream, n, e, keyProtectionRemoverFactory);
// TODO: type of RSA key?
RSAPublicBCPGKey basePubKey = new RSAPublicBCPGKey(n, e);
RSAPublicBCPGKey assocPubKey = (RSAPublicBCPGKey)pubKey.getPublicKeyPacket().getKey();
if (!basePubKey.getModulus().equals(assocPubKey.getModulus())
|| !basePubKey.getPublicExponent().equals(assocPubKey.getPublicExponent()))
{
throw new PGPException("passed in public key does not match secret key");
}
return new PGPSecretKey(new SecretKeyPacket(pubKey.getPublicKeyPacket(), SymmetricKeyAlgorithmTags.NULL, null, null, new RSASecretBCPGKey(values[0], values[1], values[2]).getEncoded()), pubKey);
}
else
{
throw new PGPException("unknown key type: " + keyType);
}
}
throw new PGPException("unknown key type found");
}
/**
* Parse a secret key from one of the GPG S expression keys.
*
* @return a secret key object.
*/
public PGPSecretKey parseSecretKey(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, KeyFingerPrintCalculator fingerPrintCalculator)
throws IOException, PGPException
{
SXprUtils.skipOpenParenthesis(inputStream);
String type;
type = SXprUtils.readString(inputStream, inputStream.read());
if (type.equals("protected-private-key")
|| type.equals("private-key"))
{
SXprUtils.skipOpenParenthesis(inputStream);
String keyType = SXprUtils.readString(inputStream, inputStream.read());
if (keyType.equals("ecc"))
{
SXprUtils.skipOpenParenthesis(inputStream);
String curveID = SXprUtils.readString(inputStream, inputStream.read());
String curveName = SXprUtils.readString(inputStream, inputStream.read());
if (curveName.startsWith("NIST "))
{
curveName = curveName.substring("NIST ".length());
}
SXprUtils.skipCloseParenthesis(inputStream);
byte[] qVal;
SXprUtils.skipOpenParenthesis(inputStream);
type = SXprUtils.readString(inputStream, inputStream.read());
if (type.equals("q"))
{
qVal = SXprUtils.readBytes(inputStream, inputStream.read());
}
else
{
throw new PGPException("no q value found");
}
PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTags.ECDSA, new Date(), new ECDSAPublicBCPGKey(ECNamedCurveTable.getOID(curveName), new BigInteger(1, qVal)));
SXprUtils.skipCloseParenthesis(inputStream);
BigInteger d = processECSecretKey(inputStream, curveID, curveName, qVal, keyProtectionRemoverFactory);
return new PGPSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTags.NULL, null, null, new ECSecretBCPGKey(d).getEncoded()), new PGPPublicKey(pubPacket, fingerPrintCalculator));
}
else if (keyType.equals("dsa"))
{
BigInteger p = readBigInteger("p", inputStream);
BigInteger q = readBigInteger("q", inputStream);
BigInteger g = readBigInteger("g", inputStream);
BigInteger y = readBigInteger("y", inputStream);
BigInteger x = processDSASecretKey(inputStream, p, q, g, y, keyProtectionRemoverFactory);
PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTags.DSA, new Date(), new DSAPublicBCPGKey(p, q, g, y));
return new PGPSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTags.NULL, null, null, new DSASecretBCPGKey(x).getEncoded()), new PGPPublicKey(pubPacket, fingerPrintCalculator));
}
else if (keyType.equals("elg"))
{
BigInteger p = readBigInteger("p", inputStream);
BigInteger g = readBigInteger("g", inputStream);
BigInteger y = readBigInteger("y", inputStream);
BigInteger x = processElGamalSecretKey(inputStream, p, g, y, keyProtectionRemoverFactory);
PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, new Date(), new ElGamalPublicBCPGKey(p, g, y));
return new PGPSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTags.NULL, null, null, new ElGamalSecretBCPGKey(x).getEncoded()), new PGPPublicKey(pubPacket, fingerPrintCalculator));
}
else if (keyType.equals("rsa"))
{
BigInteger n = readBigInteger("n", inputStream);
BigInteger e = readBigInteger("e", inputStream);
BigInteger[] values = processRSASecretKey(inputStream, n, e, keyProtectionRemoverFactory);
// TODO: type of RSA key?
PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTags.RSA_GENERAL, new Date(), new RSAPublicBCPGKey(n, e));
return new PGPSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTags.NULL, null, null, new RSASecretBCPGKey(values[0], values[1], values[2]).getEncoded()), new PGPPublicKey(pubPacket, fingerPrintCalculator));
}
else
{
throw new PGPException("unknown key type: " + keyType);
}
}
throw new PGPException("unknown key type found");
}
private BigInteger readBigInteger(String expectedType, InputStream inputStream)
throws IOException, PGPException
{
SXprUtils.skipOpenParenthesis(inputStream);
String type = SXprUtils.readString(inputStream, inputStream.read());
if (!type.equals(expectedType))
{
throw new PGPException(expectedType + " value expected");
}
byte[] nBytes = SXprUtils.readBytes(inputStream, inputStream.read());
BigInteger v = new BigInteger(1, nBytes);
SXprUtils.skipCloseParenthesis(inputStream);
return v;
}
private static byte[][] extractData(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory)
throws PGPException, IOException
{
byte[] data;
byte[] protectedAt = null;
SXprUtils.skipOpenParenthesis(inputStream);
String type = SXprUtils.readString(inputStream, inputStream.read());
if (type.equals("protected"))
{
String protection = SXprUtils.readString(inputStream, inputStream.read());
SXprUtils.skipOpenParenthesis(inputStream);
S2K s2k = SXprUtils.parseS2K(inputStream);
byte[] iv = SXprUtils.readBytes(inputStream, inputStream.read());
SXprUtils.skipCloseParenthesis(inputStream);
byte[] secKeyData = SXprUtils.readBytes(inputStream, inputStream.read());
SXprUtils.skipCloseParenthesis(inputStream);
PBESecretKeyDecryptor keyDecryptor = keyProtectionRemoverFactory.createDecryptor(protection);
// TODO: recognise other algorithms
byte[] key = keyDecryptor.makeKeyFromPassPhrase(SymmetricKeyAlgorithmTags.AES_128, s2k);
data = keyDecryptor.recoverKeyData(SymmetricKeyAlgorithmTags.AES_128, key, iv, secKeyData, 0, secKeyData.length);
// check if protected at is present
if (inputStream.read() == '(')
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
bOut.write('(');
int ch;
while ((ch = inputStream.read()) >= 0 && ch != ')')
{
bOut.write(ch);
}
if (ch != ')')
{
throw new IOException("unexpected end to SExpr");
}
bOut.write(')');
protectedAt = bOut.toByteArray();
}
SXprUtils.skipCloseParenthesis(inputStream);
SXprUtils.skipCloseParenthesis(inputStream);
}
else if (type.equals("d"))
{
return null;
}
else
{
throw new PGPException("protected block not found");
}
return new byte[][]{data, protectedAt};
}
private BigInteger processDSASecretKey(InputStream inputStream, BigInteger p, BigInteger q, BigInteger g, BigInteger y,
PBEProtectionRemoverFactory keyProtectionRemoverFactory)
throws IOException, PGPException
{
String type;
byte[][] basicData = extractData(inputStream, keyProtectionRemoverFactory);
byte[] keyData = basicData[0];
byte[] protectedAt = basicData[1];
//
// parse the secret key S-expr
//
InputStream keyIn = new ByteArrayInputStream(keyData);
SXprUtils.skipOpenParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
BigInteger x = readBigInteger("x", keyIn);
SXprUtils.skipCloseParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("hash"))
{
throw new PGPException("hash keyword expected");
}
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("sha1"))
{
throw new PGPException("hash keyword expected");
}
byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
SXprUtils.skipCloseParenthesis(keyIn);
if (digestProvider != null)
{
PGPDigestCalculator digestCalculator = digestProvider.get(HashAlgorithmTags.SHA1);
OutputStream dOut = digestCalculator.getOutputStream();
dOut.write(Strings.toByteArray("(3:dsa"));
writeCanonical(dOut, "p", p);
writeCanonical(dOut, "q", q);
writeCanonical(dOut, "g", g);
writeCanonical(dOut, "y", y);
writeCanonical(dOut, "x", x);
// check protected-at
if (protectedAt != null)
{
dOut.write(protectedAt);
}
dOut.write(Strings.toByteArray(")"));
byte[] check = digestCalculator.getDigest();
if (!Arrays.constantTimeAreEqual(check, hashBytes))
{
throw new PGPException("checksum on protected data failed in SExpr");
}
}
return x;
}
private BigInteger processElGamalSecretKey(InputStream inputStream, BigInteger p, BigInteger g, BigInteger y,
PBEProtectionRemoverFactory keyProtectionRemoverFactory)
throws IOException, PGPException
{
String type;
byte[][] basicData = extractData(inputStream, keyProtectionRemoverFactory);
byte[] keyData = basicData[0];
byte[] protectedAt = basicData[1];
//
// parse the secret key S-expr
//
InputStream keyIn = new ByteArrayInputStream(keyData);
SXprUtils.skipOpenParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
BigInteger x = readBigInteger("x", keyIn);
SXprUtils.skipCloseParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("hash"))
{
throw new PGPException("hash keyword expected");
}
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("sha1"))
{
throw new PGPException("hash keyword expected");
}
byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
SXprUtils.skipCloseParenthesis(keyIn);
if (digestProvider != null)
{
PGPDigestCalculator digestCalculator = digestProvider.get(HashAlgorithmTags.SHA1);
OutputStream dOut = digestCalculator.getOutputStream();
dOut.write(Strings.toByteArray("(3:elg"));
writeCanonical(dOut, "p", p);
writeCanonical(dOut, "g", g);
writeCanonical(dOut, "y", y);
writeCanonical(dOut, "x", x);
// check protected-at
if (protectedAt != null)
{
dOut.write(protectedAt);
}
dOut.write(Strings.toByteArray(")"));
byte[] check = digestCalculator.getDigest();
if (!Arrays.constantTimeAreEqual(check, hashBytes))
{
throw new PGPException("checksum on protected data failed in SExpr");
}
}
return x;
}
private BigInteger processECSecretKey(InputStream inputStream, String curveID, String curveName, byte[] qVal,
PBEProtectionRemoverFactory keyProtectionRemoverFactory)
throws IOException, PGPException
{
String type;
byte[][] basicData = extractData(inputStream, keyProtectionRemoverFactory);
byte[] keyData = basicData[0];
byte[] protectedAt = basicData[1];
//
// parse the secret key S-expr
//
InputStream keyIn = new ByteArrayInputStream(keyData);
SXprUtils.skipOpenParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
BigInteger d = readBigInteger("d", keyIn);
SXprUtils.skipCloseParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("hash"))
{
throw new PGPException("hash keyword expected");
}
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("sha1"))
{
throw new PGPException("hash keyword expected");
}
byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
SXprUtils.skipCloseParenthesis(keyIn);
if (digestProvider != null)
{
PGPDigestCalculator digestCalculator = digestProvider.get(HashAlgorithmTags.SHA1);
OutputStream dOut = digestCalculator.getOutputStream();
dOut.write(Strings.toByteArray("(3:ecc"));
dOut.write(Strings.toByteArray("(" + curveID.length() + ":" + curveID + curveName.length() + ":" + curveName + ")"));
writeCanonical(dOut, "q", qVal);
writeCanonical(dOut, "d", d);
// check protected-at
if (protectedAt != null)
{
dOut.write(protectedAt);
}
dOut.write(Strings.toByteArray(")"));
byte[] check = digestCalculator.getDigest();
if (!Arrays.constantTimeAreEqual(check, hashBytes))
{
throw new PGPException("checksum on protected data failed in SExpr");
}
}
return d;
}
private BigInteger[] processRSASecretKey(InputStream inputStream, BigInteger n, BigInteger e,
PBEProtectionRemoverFactory keyProtectionRemoverFactory)
throws IOException, PGPException
{
String type;
byte[][] basicData = extractData(inputStream, keyProtectionRemoverFactory);
byte[] keyData;
byte[] protectedAt = null;
InputStream keyIn;
BigInteger d;
if (basicData == null)
{
keyIn = inputStream;
byte[] nBytes = SXprUtils.readBytes(inputStream,
inputStream.read());
d = new BigInteger(1, nBytes);
SXprUtils.skipCloseParenthesis(inputStream);
}
else
{
keyData = basicData[0];
protectedAt = basicData[1];
keyIn = new ByteArrayInputStream(keyData);
SXprUtils.skipOpenParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
d = readBigInteger("d", keyIn);
}
//
// parse the secret key S-expr
//
BigInteger p = readBigInteger("p", keyIn);
BigInteger q = readBigInteger("q", keyIn);
BigInteger u = readBigInteger("u", keyIn);
if (basicData == null)
{
return new BigInteger[] { d, p, q, u };
}
SXprUtils.skipCloseParenthesis(keyIn);
SXprUtils.skipOpenParenthesis(keyIn);
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("hash"))
{
throw new PGPException("hash keyword expected");
}
type = SXprUtils.readString(keyIn, keyIn.read());
if (!type.equals("sha1"))
{
throw new PGPException("hash keyword expected");
}
byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
SXprUtils.skipCloseParenthesis(keyIn);
if (digestProvider != null)
{
PGPDigestCalculator digestCalculator = digestProvider.get(HashAlgorithmTags.SHA1);
OutputStream dOut = digestCalculator.getOutputStream();
dOut.write(Strings.toByteArray("(3:rsa"));
writeCanonical(dOut, "n", n);
writeCanonical(dOut, "e", e);
writeCanonical(dOut, "d", d);
writeCanonical(dOut, "p", p);
writeCanonical(dOut, "q", q);
writeCanonical(dOut, "u", u);
// check protected-at
if (protectedAt != null)
{
dOut.write(protectedAt);
}
dOut.write(Strings.toByteArray(")"));
byte[] check = digestCalculator.getDigest();
if (!Arrays.constantTimeAreEqual(check, hashBytes))
{
throw new PGPException("checksum on protected data failed in SExpr");
}
}
return new BigInteger[]{d, p, q, u};
}
private void writeCanonical(OutputStream dOut, String label, BigInteger i)
throws IOException
{
writeCanonical(dOut, label, i.toByteArray());
}
private void writeCanonical(OutputStream dOut, String label, byte[] data)
throws IOException
{
dOut.write(Strings.toByteArray("(" + label.length() + ":" + label + data.length + ":"));
dOut.write(data);
dOut.write(Strings.toByteArray(")"));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy