blob: a0809f0510018f5f53f83a40ab7b3674303ee693 [file] [log] [blame]
package org.bouncycastle.crypto.util;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;
import org.bouncycastle.util.Strings;
/**
* OpenSSHPublicKeyUtil utility classes for parsing OpenSSH public keys.
*/
public class OpenSSHPublicKeyUtil
{
private OpenSSHPublicKeyUtil()
{
}
private static final String RSA = "ssh-rsa";
private static final String ECDSA = "ecdsa";
private static final String ED_25519 = "ssh-ed25519";
private static final String DSS = "ssh-dss";
/**
* Parse a public key.
* <p>
* This method accepts the bytes that are Base64 encoded in an OpenSSH public key file.
*
* @param encoded The key.
* @return An AsymmetricKeyParameter instance.
*/
public static AsymmetricKeyParameter parsePublicKey(byte[] encoded)
{
SSHBuffer buffer = new SSHBuffer(encoded);
return parsePublicKey(buffer);
}
/**
* Encode a public key from an AsymmetricKeyParameter instance.
*
* @param cipherParameters The key to encode.
* @return the key OpenSSH encoded.
* @throws IOException
*/
public static byte[] encodePublicKey(AsymmetricKeyParameter cipherParameters)
throws IOException
{
BigInteger e;
BigInteger n;
if (cipherParameters == null)
{
throw new IllegalArgumentException("cipherParameters was null.");
}
if (cipherParameters instanceof RSAKeyParameters)
{
if (cipherParameters.isPrivate())
{
throw new IllegalArgumentException("RSAKeyParamaters was for encryption");
}
e = ((RSAKeyParameters)cipherParameters).getExponent();
n = ((RSAKeyParameters)cipherParameters).getModulus();
SSHBuilder builder = new SSHBuilder();
builder.writeString(RSA);
builder.rawArray(e.toByteArray());
builder.rawArray(n.toByteArray());
return builder.getBytes();
}
else if (cipherParameters instanceof ECPublicKeyParameters)
{
SSHBuilder builder = new SSHBuilder();
String name = null;
if (((ECPublicKeyParameters)cipherParameters).getParameters().getCurve() instanceof SecP256R1Curve)
{
name = "nistp256";
}
else
{
throw new IllegalArgumentException("unable to derive ssh curve name for " + ((ECPublicKeyParameters)cipherParameters).getParameters().getCurve().getClass().getName());
}
builder.writeString(ECDSA + "-sha2-" + name); // Magic
builder.writeString(name);
builder.rawArray(((ECPublicKeyParameters)cipherParameters).getQ().getEncoded(false)); //Uncompressed
return builder.getBytes();
}
else if (cipherParameters instanceof DSAPublicKeyParameters)
{
SSHBuilder builder = new SSHBuilder();
builder.writeString(DSS);
builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getParameters().getP().toByteArray());
builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getParameters().getQ().toByteArray());
builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getParameters().getG().toByteArray());
builder.rawArray(((DSAPublicKeyParameters)cipherParameters).getY().toByteArray());
return builder.getBytes();
}
else if (cipherParameters instanceof Ed25519PublicKeyParameters)
{
SSHBuilder builder = new SSHBuilder();
builder.writeString(ED_25519);
builder.rawArray(((Ed25519PublicKeyParameters)cipherParameters).getEncoded());
return builder.getBytes();
}
throw new IllegalArgumentException("unable to convert " + cipherParameters.getClass().getName() + " to private key");
}
/**
* Parse a public key from an SSHBuffer instance.
*
* @param buffer containing the SSH public key.
* @return A CipherParameters instance.
*/
public static AsymmetricKeyParameter parsePublicKey(SSHBuffer buffer)
{
AsymmetricKeyParameter result = null;
String magic = Strings.fromByteArray(buffer.readString());
if (RSA.equals(magic))
{
BigInteger e = buffer.positiveBigNum();
BigInteger n = buffer.positiveBigNum();
result = new RSAKeyParameters(false, n, e);
}
else if (DSS.equals(magic))
{
BigInteger p = buffer.positiveBigNum();
BigInteger q = buffer.positiveBigNum();
BigInteger g = buffer.positiveBigNum();
BigInteger pubKey = buffer.positiveBigNum();
result = new DSAPublicKeyParameters(pubKey, new DSAParameters(p, q, g));
}
else if (magic.startsWith(ECDSA))
{
String curveName = Strings.fromByteArray(buffer.readString());
String nameToFind = curveName;
if (curveName.startsWith("nist"))
{
//
// NIST names like P-256 are encoded in SSH as nistp256
//
nameToFind = curveName.substring(4);
nameToFind = nameToFind.substring(0, 1) + "-" + nameToFind.substring(1);
}
X9ECParameters x9ECParameters = ECNamedCurveTable.getByName(nameToFind);
if (x9ECParameters == null)
{
throw new IllegalStateException("unable to find curve for " + magic + " using curve name " + nameToFind);
}
//
// Extract name of digest from magic string value;
//
//String digest = magic.split("-")[1];
ECCurve curve = x9ECParameters.getCurve();
byte[] pointRaw = buffer.readString();
result = new ECPublicKeyParameters(curve.decodePoint(pointRaw), new ECDomainParameters(curve, x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH(), x9ECParameters.getSeed()));
}
else if (magic.startsWith(ED_25519))
{
result = new Ed25519PublicKeyParameters(buffer.readString(), 0);
}
if (result == null)
{
throw new IllegalArgumentException("unable to parse key");
}
if (buffer.hasRemaining())
{
throw new IllegalArgumentException("uncoded key has trailing data");
}
return result;
}
}