From 18c3bb7cb6f17b14402501623239433f3f06ae56 Mon Sep 17 00:00:00 2001 From: David Hook Date: Wed, 18 Mar 2020 18:51:13 +1100 Subject: [PATCH] github #589/#620 multi-release classes for Java11+ --- .../asymmetric/edec/BC11XDHPrivateKey.java | 192 ++++++++++ .../asymmetric/edec/BC11XDHPublicKey.java | 184 ++++++++++ .../asymmetric/edec/KeyFactorySpi.java | 330 ++++++++++++++++++ .../asymmetric/edec/KeyPairGeneratorSpi.java | 311 +++++++++++++++++ 4 files changed, 1017 insertions(+) create mode 100644 prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPrivateKey.java create mode 100644 prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPublicKey.java create mode 100644 prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java create mode 100644 prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java diff --git a/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPrivateKey.java b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPrivateKey.java new file mode 100644 index 0000000000..2194938176 --- /dev/null +++ b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPrivateKey.java @@ -0,0 +1,192 @@ +package org.bouncycastle.jcajce.provider.asymmetric.edec; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.interfaces.XECPrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.NamedParameterSpec; +import java.util.Optional; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.X448PrivateKeyParameters; +import org.bouncycastle.crypto.util.PrivateKeyInfoFactory; +import org.bouncycastle.jcajce.interfaces.XDHPrivateKey; +import org.bouncycastle.jcajce.interfaces.XDHPublicKey; +import org.bouncycastle.util.Arrays; + +class BC11XDHPrivateKey + implements XDHPrivateKey, XECPrivateKey +{ + static final long serialVersionUID = 1L; + + private transient AsymmetricKeyParameter xdhPrivateKey; + + private final boolean hasPublicKey; + private final byte[] attributes; + + BC11XDHPrivateKey(AsymmetricKeyParameter privKey) + { + this.hasPublicKey = true; + this.attributes = null; + this.xdhPrivateKey = privKey; + } + + BC11XDHPrivateKey(PrivateKeyInfo keyInfo) + throws IOException + { + this.hasPublicKey = keyInfo.hasPublicKey(); + this.attributes = (keyInfo.getAttributes() != null) ? keyInfo.getAttributes().getEncoded() : null; + + populateFromPrivateKeyInfo(keyInfo); + } + + private void populateFromPrivateKeyInfo(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1Encodable keyOcts = keyInfo.parsePrivateKey(); + if (EdECObjectIdentifiers.id_X448.equals(keyInfo.getPrivateKeyAlgorithm().getAlgorithm())) + { + xdhPrivateKey = new X448PrivateKeyParameters(ASN1OctetString.getInstance(keyOcts).getOctets(), 0); + } + else + { + xdhPrivateKey = new X25519PrivateKeyParameters(ASN1OctetString.getInstance(keyOcts).getOctets(), 0); + } + } + + public AlgorithmParameterSpec getParams() + { + if (xdhPrivateKey instanceof X448PrivateKeyParameters) + { + return NamedParameterSpec.X448; + } + else + { + return NamedParameterSpec.X25519; + } + } + + public Optional getScalar() + { + if (xdhPrivateKey instanceof X448PrivateKeyParameters) + { + return Optional.of(((X448PrivateKeyParameters)xdhPrivateKey).getEncoded()); + } + else + { + return Optional.of(((X25519PrivateKeyParameters)xdhPrivateKey).getEncoded()); + } + } + + public String getAlgorithm() + { + return (xdhPrivateKey instanceof X448PrivateKeyParameters) ? "X448" : "X25519"; + } + + public String getFormat() + { + return "PKCS#8"; + } + + public byte[] getEncoded() + { + try + { + ASN1Set attrSet = ASN1Set.getInstance(attributes); + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(xdhPrivateKey, attrSet); + + if (hasPublicKey) + { + return privInfo.getEncoded(); + } + else + { + return new PrivateKeyInfo(privInfo.getPrivateKeyAlgorithm(), privInfo.parsePrivateKey(), attrSet).getEncoded(); + } + } + catch (IOException e) + { + return null; + } + } + + public XDHPublicKey getPublicKey() + { + if (xdhPrivateKey instanceof X448PrivateKeyParameters) + { + return new BCXDHPublicKey(((X448PrivateKeyParameters)xdhPrivateKey).generatePublicKey()); + } + else + { + return new BCXDHPublicKey(((X25519PrivateKeyParameters)xdhPrivateKey).generatePublicKey()); + } + } + + AsymmetricKeyParameter engineGetKeyParameters() + { + return xdhPrivateKey; + } + + public String toString() + { + AsymmetricKeyParameter pubKey; + if (xdhPrivateKey instanceof X448PrivateKeyParameters) + { + pubKey = ((X448PrivateKeyParameters)xdhPrivateKey).generatePublicKey(); + } + else + { + pubKey = ((X25519PrivateKeyParameters)xdhPrivateKey).generatePublicKey(); + } + return Utils.keyToString("Private Key", getAlgorithm(), pubKey); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof BC11XDHPrivateKey)) + { + return false; + } + + BC11XDHPrivateKey other = (BC11XDHPrivateKey)o; + + return Arrays.areEqual(other.getEncoded(), this.getEncoded()); + } + + public int hashCode() + { + return Arrays.hashCode(this.getEncoded()); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPrivateKeyInfo(PrivateKeyInfo.getInstance(enc)); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPublicKey.java b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPublicKey.java new file mode 100644 index 0000000000..afc13e94e0 --- /dev/null +++ b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/BC11XDHPublicKey.java @@ -0,0 +1,184 @@ +package org.bouncycastle.jcajce.provider.asymmetric.edec; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.security.interfaces.XECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.NamedParameterSpec; + +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.X25519PublicKeyParameters; +import org.bouncycastle.crypto.params.X448PublicKeyParameters; +import org.bouncycastle.jcajce.interfaces.XDHPublicKey; +import org.bouncycastle.util.Arrays; + +class BC11XDHPublicKey + implements XDHPublicKey, XECPublicKey +{ + static final long serialVersionUID = 1L; + + private transient AsymmetricKeyParameter xdhPublicKey; + + BC11XDHPublicKey(AsymmetricKeyParameter pubKey) + { + this.xdhPublicKey = pubKey; + } + + BC11XDHPublicKey(SubjectPublicKeyInfo keyInfo) + { + populateFromPubKeyInfo(keyInfo); + } + + BC11XDHPublicKey(byte[] prefix, byte[] rawData) + throws InvalidKeySpecException + { + int prefixLength = prefix.length; + + if (Utils.isValidPrefix(prefix, rawData)) + { + if ((rawData.length - prefixLength) == X448PublicKeyParameters.KEY_SIZE) + { + xdhPublicKey = new X448PublicKeyParameters(rawData, prefixLength); + } + else if ((rawData.length - prefixLength) == X25519PublicKeyParameters.KEY_SIZE) + { + xdhPublicKey = new X25519PublicKeyParameters(rawData, prefixLength); + } + else + { + throw new InvalidKeySpecException("raw key data not recognised"); + } + } + else + { + throw new InvalidKeySpecException("raw key data not recognised"); + } + } + + private void populateFromPubKeyInfo(SubjectPublicKeyInfo keyInfo) + { + if (EdECObjectIdentifiers.id_X448.equals(keyInfo.getAlgorithm().getAlgorithm())) + { + xdhPublicKey = new X448PublicKeyParameters(keyInfo.getPublicKeyData().getOctets(), 0); + } + else + { + xdhPublicKey = new X25519PublicKeyParameters(keyInfo.getPublicKeyData().getOctets(), 0); + } + } + + public AlgorithmParameterSpec getParams() + { + if (xdhPublicKey instanceof X448PublicKeyParameters) + { + return NamedParameterSpec.X448; + } + else + { + return NamedParameterSpec.X25519; + } + } + + public BigInteger getU() + { + if (xdhPublicKey instanceof X448PublicKeyParameters) + { + return new BigInteger(1, ((X448PublicKeyParameters)xdhPublicKey).getEncoded()); + } + else + { + return new BigInteger(1, ((X25519PublicKeyParameters)xdhPublicKey).getEncoded()); + } + } + + public String getAlgorithm() + { + return (xdhPublicKey instanceof X448PublicKeyParameters) ? "X448" : "X25519"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + if (xdhPublicKey instanceof X448PublicKeyParameters) + { + byte[] encoding = new byte[KeyFactorySpi.x448Prefix.length + X448PublicKeyParameters.KEY_SIZE]; + + System.arraycopy(KeyFactorySpi.x448Prefix, 0, encoding, 0, KeyFactorySpi.x448Prefix.length); + + ((X448PublicKeyParameters)xdhPublicKey).encode(encoding, KeyFactorySpi.x448Prefix.length); + + return encoding; + } + else + { + byte[] encoding = new byte[KeyFactorySpi.x25519Prefix.length + X25519PublicKeyParameters.KEY_SIZE]; + + System.arraycopy(KeyFactorySpi.x25519Prefix, 0, encoding, 0, KeyFactorySpi.x25519Prefix.length); + + ((X25519PublicKeyParameters)xdhPublicKey).encode(encoding, KeyFactorySpi.x25519Prefix.length); + + return encoding; + } + } + + AsymmetricKeyParameter engineGetKeyParameters() + { + return xdhPublicKey; + } + + public String toString() + { + return Utils.keyToString("Public Key", getAlgorithm(), xdhPublicKey); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof BC11XDHPublicKey)) + { + return false; + } + + BC11XDHPublicKey other = (BC11XDHPublicKey)o; + + return Arrays.areEqual(other.getEncoded(), this.getEncoded()); + } + + public int hashCode() + { + return Arrays.hashCode(this.getEncoded()); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(enc)); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java new file mode 100644 index 0000000000..3ef98cd8e6 --- /dev/null +++ b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyFactorySpi.java @@ -0,0 +1,330 @@ +package org.bouncycastle.jcajce.provider.asymmetric.edec; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil; +import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil; +import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec; +import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec; +import org.bouncycastle.util.encoders.Hex; + +public class KeyFactorySpi + extends BaseKeyFactorySpi + implements AsymmetricKeyInfoConverter +{ + static final byte[] x448Prefix = Hex.decode("3042300506032b656f033900"); + static final byte[] x25519Prefix = Hex.decode("302a300506032b656e032100"); + static final byte[] Ed448Prefix = Hex.decode("3043300506032b6571033a00"); + static final byte[] Ed25519Prefix = Hex.decode("302a300506032b6570032100"); + + private static final byte x448_type = 0x6f; + private static final byte x25519_type = 0x6e; + private static final byte Ed448_type = 0x71; + private static final byte Ed25519_type = 0x70; + + String algorithm; + private final boolean isXdh; + private final int specificBase; + + public KeyFactorySpi( + String algorithm, + boolean isXdh, + int specificBase) + { + this.algorithm = algorithm; + this.isXdh = isXdh; + this.specificBase = specificBase; + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + throw new InvalidKeyException("key type unknown"); + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(OpenSSHPrivateKeySpec.class) && key instanceof BCEdDSAPrivateKey) + { + try + { + // + // The DEROctetString at element 2 is an encoded DEROctetString with the private key value + // within it. + // + + ASN1Sequence seq = ASN1Sequence.getInstance(key.getEncoded()); + DEROctetString val = (DEROctetString)seq.getObjectAt(2); + ASN1InputStream in = new ASN1InputStream(val.getOctets()); + + return new OpenSSHPrivateKeySpec(OpenSSHPrivateKeyUtil.encodePrivateKey(new Ed25519PrivateKeyParameters(ASN1OctetString.getInstance(in.readObject()).getOctets(), 0))); + } + catch (IOException ex) + { + throw new InvalidKeySpecException(ex.getMessage(), ex.getCause()); + } + + } + else if (spec.isAssignableFrom(OpenSSHPublicKeySpec.class) && key instanceof BCEdDSAPublicKey) + { + try + { + return new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(new Ed25519PublicKeyParameters(key.getEncoded(), Ed25519Prefix.length))); + } + catch (IOException ex) + { + throw new InvalidKeySpecException(ex.getMessage(), ex.getCause()); + } + } + if (spec.isAssignableFrom(org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec.class) && key instanceof BCEdDSAPrivateKey) + { + try + { + // + // The DEROctetString at element 2 is an encoded DEROctetString with the private key value + // within it. + // + + ASN1Sequence seq = ASN1Sequence.getInstance(key.getEncoded()); + DEROctetString val = (DEROctetString)seq.getObjectAt(2); + ASN1InputStream in = new ASN1InputStream(val.getOctets()); + + return new org.bouncycastle.jce.spec.OpenSSHPrivateKeySpec(OpenSSHPrivateKeyUtil.encodePrivateKey(new Ed25519PrivateKeyParameters(ASN1OctetString.getInstance(in.readObject()).getOctets(), 0))); + } + catch (IOException ex) + { + throw new InvalidKeySpecException(ex.getMessage(), ex.getCause()); + } + + } + else if (spec.isAssignableFrom(org.bouncycastle.jce.spec.OpenSSHPublicKeySpec.class) && key instanceof BCEdDSAPublicKey) + { + try + { + return new org.bouncycastle.jce.spec.OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(new Ed25519PublicKeyParameters(key.getEncoded(), Ed25519Prefix.length))); + } + catch (IOException ex) + { + throw new InvalidKeySpecException(ex.getMessage(), ex.getCause()); + } + } + + return super.engineGetKeySpec(key, spec); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof OpenSSHPrivateKeySpec) + { + CipherParameters parameters = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(((OpenSSHPrivateKeySpec)keySpec).getEncoded()); + if (parameters instanceof Ed25519PrivateKeyParameters) + { + return new BCEdDSAPrivateKey((Ed25519PrivateKeyParameters)parameters); + } + throw new IllegalStateException("openssh private key not Ed25519 private key"); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof X509EncodedKeySpec) + { + byte[] enc = ((X509EncodedKeySpec)keySpec).getEncoded(); + // optimise if we can + if ((specificBase == 0 || specificBase == enc[8])) + { + // watch out for badly placed DER NULL - the default X509Cert will add these! + if (enc[9] == 0x05 && enc[10] == 0x00) + { + SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(enc); + + keyInfo = new SubjectPublicKeyInfo( + new AlgorithmIdentifier(keyInfo.getAlgorithm().getAlgorithm()), keyInfo.getPublicKeyData().getBytes()); + + try + { + enc = keyInfo.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new InvalidKeySpecException("attempt to reconstruct key failed: " + e.getMessage()); + } + } + + switch (enc[8]) + { + case x448_type: + return new BC11XDHPublicKey(x448Prefix, enc); + case x25519_type: + return new BC11XDHPublicKey(x25519Prefix, enc); + case Ed448_type: + return new BCEdDSAPublicKey(Ed448Prefix, enc); + case Ed25519_type: + return new BCEdDSAPublicKey(Ed25519Prefix, enc); + default: + return super.engineGeneratePublic(keySpec); + } + } + } + else if (keySpec instanceof OpenSSHPublicKeySpec) + { + CipherParameters parameters = OpenSSHPublicKeyUtil.parsePublicKey(((OpenSSHPublicKeySpec)keySpec).getEncoded()); + if (parameters instanceof Ed25519PublicKeyParameters) + { + return new BCEdDSAPublicKey(new byte[0], ((Ed25519PublicKeyParameters)parameters).getEncoded()); + } + + throw new IllegalStateException("openssh public key not Ed25519 public key"); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (isXdh) + { + if ((specificBase == 0 || specificBase == x448_type) && algOid.equals(EdECObjectIdentifiers.id_X448)) + { + return new BC11XDHPrivateKey(keyInfo); + } + if ((specificBase == 0 || specificBase == x25519_type) && algOid.equals(EdECObjectIdentifiers.id_X25519)) + { + return new BC11XDHPrivateKey(keyInfo); + } + } + else if (algOid.equals(EdECObjectIdentifiers.id_Ed448) || algOid.equals(EdECObjectIdentifiers.id_Ed25519)) + { + if ((specificBase == 0 || specificBase == Ed448_type) && algOid.equals(EdECObjectIdentifiers.id_Ed448)) + { + return new BCEdDSAPrivateKey(keyInfo); + } + if ((specificBase == 0 || specificBase == Ed25519_type) && algOid.equals(EdECObjectIdentifiers.id_Ed25519)) + { + return new BCEdDSAPrivateKey(keyInfo); + } + } + + throw new IOException("algorithm identifier " + algOid + " in key not recognized"); + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (isXdh) + { + if ((specificBase == 0 || specificBase == x448_type) && algOid.equals(EdECObjectIdentifiers.id_X448)) + { + return new BC11XDHPublicKey(keyInfo); + } + if ((specificBase == 0 || specificBase == x25519_type) && algOid.equals(EdECObjectIdentifiers.id_X25519)) + { + return new BC11XDHPublicKey(keyInfo); + } + } + else if (algOid.equals(EdECObjectIdentifiers.id_Ed448) || algOid.equals(EdECObjectIdentifiers.id_Ed25519)) + { + if ((specificBase == 0 || specificBase == Ed448_type) && algOid.equals(EdECObjectIdentifiers.id_Ed448)) + { + return new BCEdDSAPublicKey(keyInfo); + } + if ((specificBase == 0 || specificBase == Ed25519_type) && algOid.equals(EdECObjectIdentifiers.id_Ed25519)) + { + return new BCEdDSAPublicKey(keyInfo); + } + } + + throw new IOException("algorithm identifier " + algOid + " in key not recognized"); + } + + public static class XDH + extends KeyFactorySpi + { + public XDH() + { + super("XDH", true, 0); + } + } + + public static class X448 + extends KeyFactorySpi + { + public X448() + { + super("X448", true, x448_type); + } + } + + public static class X25519 + extends KeyFactorySpi + { + public X25519() + { + super("X25519", true, x25519_type); + } + } + + public static class EdDSA + extends KeyFactorySpi + { + public EdDSA() + { + super("EdDSA", false, 0); + } + } + + public static class Ed448 + extends KeyFactorySpi + { + public Ed448() + { + super("Ed448", false, Ed448_type); + } + } + + public static class Ed25519 + extends KeyFactorySpi + { + public Ed25519() + { + super("Ed25519", false, Ed25519_type); + } + } +} \ No newline at end of file diff --git a/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java new file mode 100644 index 0000000000..15022308f2 --- /dev/null +++ b/prov/src/main/jdk1.11/org/bouncycastle/jcajce/provider/asymmetric/edec/KeyPairGeneratorSpi.java @@ -0,0 +1,311 @@ +package org.bouncycastle.jcajce.provider.asymmetric.edec; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.NamedParameterSpec; + +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; +import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator; +import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; +import org.bouncycastle.crypto.generators.X448KeyPairGenerator; +import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters; +import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.X448KeyGenerationParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.bouncycastle.jcajce.spec.EdDSAParameterSpec; +import org.bouncycastle.jcajce.spec.XDHParameterSpec; +import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGeneratorSpi +{ + private static final int EdDSA = -1; + private static final int XDH = -2; + + private static final int Ed448 = 0; + private static final int Ed25519 = 1; + private static final int X448 = 2; + private static final int X25519 = 3; + + private int algorithm; + private AsymmetricCipherKeyPairGenerator generator; + + private boolean initialised; + private SecureRandom secureRandom; + + KeyPairGeneratorSpi(int algorithm, AsymmetricCipherKeyPairGenerator generator) + { + this.algorithm = algorithm; + this.generator = generator; + } + + public void initialize(int strength, SecureRandom secureRandom) + { + this.secureRandom = secureRandom; + try + { + switch (strength) + { + case 255: + case 256: + switch (algorithm) + { + case EdDSA: + case Ed25519: + algorithmCheck(Ed25519); + this.generator = new Ed25519KeyPairGenerator(); + setupGenerator(Ed25519); + break; + case XDH: + case X25519: + algorithmCheck(X25519); + this.generator = new X25519KeyPairGenerator(); + setupGenerator(X25519); + break; + default: + throw new InvalidParameterException("key size not configurable"); + } + break; + case 448: + switch (algorithm) + { + case EdDSA: + case Ed448: + algorithmCheck(Ed448); + this.generator = new Ed448KeyPairGenerator(); + setupGenerator(Ed448); + break; + case XDH: + case X448: + algorithmCheck(X448); + this.generator = new X448KeyPairGenerator(); + setupGenerator(X448); + break; + default: + throw new InvalidParameterException("key size not configurable"); + } + break; + default: + throw new InvalidParameterException("unknown key size"); + } + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException(e.getMessage()); + } + } + + public void initialize(AlgorithmParameterSpec paramSpec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException + { + this.secureRandom = secureRandom; + + if (paramSpec instanceof ECGenParameterSpec) + { + initializeGenerator(((ECGenParameterSpec)paramSpec).getName()); + } + else if (paramSpec instanceof ECNamedCurveGenParameterSpec) + { + initializeGenerator(((ECNamedCurveGenParameterSpec)paramSpec).getName()); + } + else if (paramSpec instanceof NamedParameterSpec) + { + initializeGenerator(((NamedParameterSpec)paramSpec).getName()); + } + else if (paramSpec instanceof EdDSAParameterSpec) + { + initializeGenerator(((EdDSAParameterSpec)paramSpec).getCurveName()); + } + else if (paramSpec instanceof XDHParameterSpec) + { + initializeGenerator(((XDHParameterSpec)paramSpec).getCurveName()); + } + else + { + String name = ECUtil.getNameFrom(paramSpec); + + if (name != null) + { + initializeGenerator(name); + } + else + { + throw new InvalidAlgorithmParameterException("invalid parameterSpec: " + paramSpec); + } + } + } + + private void algorithmCheck(int algorithm) + throws InvalidAlgorithmParameterException + { + if (this.algorithm != algorithm) + { + if (this.algorithm == Ed25519 || this.algorithm == Ed448) + { + throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type"); + } + if (this.algorithm == EdDSA && (algorithm != Ed25519 && algorithm != Ed448)) + { + throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type"); + } + if (this.algorithm == X25519 || this.algorithm == X448) + { + throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type"); + } + if (this.algorithm == XDH && (algorithm != X25519 && algorithm != X448)) + { + throw new InvalidAlgorithmParameterException("parameterSpec for wrong curve type"); + } + this.algorithm = algorithm; + } + } + + private void initializeGenerator(String name) + throws InvalidAlgorithmParameterException + { + if (name.equalsIgnoreCase(EdDSAParameterSpec.Ed448) || name.equals(EdECObjectIdentifiers.id_Ed448.getId())) + { + algorithmCheck(Ed448); + this.generator = new Ed448KeyPairGenerator(); + setupGenerator(Ed448); + } + else if (name.equalsIgnoreCase(EdDSAParameterSpec.Ed25519) || name.equals(EdECObjectIdentifiers.id_Ed25519.getId())) + { + algorithmCheck(Ed25519); + this.generator = new Ed25519KeyPairGenerator(); + setupGenerator(Ed25519); + } + else if (name.equalsIgnoreCase(XDHParameterSpec.X448) || name.equals(EdECObjectIdentifiers.id_X448.getId())) + { + algorithmCheck(X448); + this.generator = new X448KeyPairGenerator(); + setupGenerator(X448); + } + else if (name.equalsIgnoreCase(XDHParameterSpec.X25519) || name.equals(EdECObjectIdentifiers.id_X25519.getId())) + { + algorithmCheck(X25519); + this.generator = new X25519KeyPairGenerator(); + setupGenerator(X25519); + } + } + + public KeyPair generateKeyPair() + { + if (generator == null) + { + throw new IllegalStateException("generator not correctly initialized"); + } + + if (!initialised) + { + setupGenerator(algorithm); + } + + AsymmetricCipherKeyPair kp = generator.generateKeyPair(); + + switch (algorithm) + { + case Ed448: + return new KeyPair(new BCEdDSAPublicKey(kp.getPublic()), new BCEdDSAPrivateKey(kp.getPrivate())); + case Ed25519: + return new KeyPair(new BCEdDSAPublicKey(kp.getPublic()), new BCEdDSAPrivateKey(kp.getPrivate())); + case X448: + return new KeyPair(new BC11XDHPublicKey(kp.getPublic()), new BC11XDHPrivateKey(kp.getPrivate())); + case X25519: + return new KeyPair(new BC11XDHPublicKey(kp.getPublic()), new BC11XDHPrivateKey(kp.getPrivate())); + } + + throw new IllegalStateException("generator not correctly initialized"); + } + + private void setupGenerator(int algorithm) + { + initialised = true; + + if (secureRandom == null) + { + secureRandom = CryptoServicesRegistrar.getSecureRandom(); + } + + switch (algorithm) + { + case Ed448: + generator.init(new Ed448KeyGenerationParameters(secureRandom)); + break; + case EdDSA: + case Ed25519: + generator.init(new Ed25519KeyGenerationParameters(secureRandom)); + break; + case X448: + generator.init(new X448KeyGenerationParameters(secureRandom)); + break; + case XDH: + case X25519: + generator.init(new X25519KeyGenerationParameters(secureRandom)); + break; + } + } + + public static final class EdDSA + extends KeyPairGeneratorSpi + { + public EdDSA() + { + super(EdDSA, null); + } + } + + public static final class Ed448 + extends KeyPairGeneratorSpi + { + public Ed448() + { + super(Ed448, new Ed448KeyPairGenerator()); + } + } + + public static final class Ed25519 + extends KeyPairGeneratorSpi + { + public Ed25519() + { + super(Ed25519, new Ed25519KeyPairGenerator()); + } + } + + public static final class XDH + extends KeyPairGeneratorSpi + { + public XDH() + { + super(XDH, null); + } + } + + public static final class X448 + extends KeyPairGeneratorSpi + { + public X448() + { + super(X448, new X448KeyPairGenerator()); + } + } + + public static final class X25519 + extends KeyPairGeneratorSpi + { + public X25519() + { + super(X25519, new X25519KeyPairGenerator()); + } + } +}