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-jdk14 Show documentation
Show all versions of bcpg-jdk14 Show documentation
The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.4. 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