using System; using System.IO; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cryptlib; using Org.BouncyCastle.Asn1.CryptoPro; using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Gnu; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.UA; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Security { public static class PublicKeyFactory { public static AsymmetricKeyParameter CreateKey(byte[] keyInfoData) => CreateKey(SubjectPublicKeyInfo.GetInstance(keyInfoData)); public static AsymmetricKeyParameter CreateKey(Stream inStr) => CreateKey(SubjectPublicKeyInfo.GetInstance(Asn1Object.FromStream(inStr))); public static AsymmetricKeyParameter CreateKey(SubjectPublicKeyInfo keyInfo) { AlgorithmIdentifier algID = keyInfo.Algorithm; DerObjectIdentifier algOid = algID.Algorithm; // TODO See RSAUtil.isRsaOid in Java build if (algOid.Equals(PkcsObjectIdentifiers.RsaEncryption) || algOid.Equals(X509ObjectIdentifiers.IdEARsa) || algOid.Equals(PkcsObjectIdentifiers.IdRsassaPss) || algOid.Equals(PkcsObjectIdentifiers.IdRsaesOaep)) { RsaPublicKeyStructure pubKey = RsaPublicKeyStructure.GetInstance( keyInfo.ParsePublicKey()); return new RsaKeyParameters(false, pubKey.Modulus, pubKey.PublicExponent); } else if (algOid.Equals(X9ObjectIdentifiers.DHPublicNumber)) { Asn1Sequence seq = Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object()); DHPublicKey dhPublicKey = DHPublicKey.GetInstance(keyInfo.ParsePublicKey()); BigInteger y = dhPublicKey.Y.Value; if (IsPkcsDHParam(seq)) return ReadPkcsDHParam(algOid, y, seq); DomainParameters dp = DomainParameters.GetInstance(seq); BigInteger p = dp.P.Value; BigInteger g = dp.G.Value; BigInteger q = dp.Q.Value; BigInteger j = dp.J?.Value; DHValidationParameters validation = null; ValidationParams validationParams = dp.ValidationParams; if (validationParams != null) { validation = new DHValidationParameters(validationParams.Seed.GetBytes(), // TODO Perhaps avoid forcing unsigned interpretation and add guards elsewhere validationParams.PgenCounter.IntPositiveValueExact); } return new DHPublicKeyParameters(y, new DHParameters(p, g, q, j, validation)); } else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement)) { Asn1Sequence seq = Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object()); DerInteger derY = (DerInteger)keyInfo.ParsePublicKey(); return ReadPkcsDHParam(algOid, derY.Value, seq); } else if (algOid.Equals(OiwObjectIdentifiers.ElGamalAlgorithm)) { ElGamalParameter para = ElGamalParameter.GetInstance(algID.Parameters); DerInteger derY = (DerInteger)keyInfo.ParsePublicKey(); return new ElGamalPublicKeyParameters( derY.Value, new ElGamalParameters(para.P, para.G)); } else if (algOid.Equals(X9ObjectIdentifiers.IdDsa) || algOid.Equals(OiwObjectIdentifiers.DsaWithSha1)) { DerInteger derY = (DerInteger)keyInfo.ParsePublicKey(); Asn1Encodable ae = algID.Parameters; DsaParameters parameters = null; if (ae != null) { DsaParameter para = DsaParameter.GetInstance(ae.ToAsn1Object()); parameters = new DsaParameters(para.P, para.Q, para.G); } return new DsaPublicKeyParameters(derY.Value, parameters); } /* * TODO id-ecDH (SecObjectIdentifiers.ecdh) and/or id-ecMQV (SecObjectIdentifiers.ecmqv) could be supported * if we could properly restrict usage of the resulting key. */ else if (algOid.Equals(X9ObjectIdentifiers.IdECPublicKey)) { X962Parameters parameters = X962Parameters.GetInstance(algID.Parameters); ECDomainParameters domainParameters = ECDomainParameters.FromX962Parameters(parameters); ECPoint q = domainParameters.Curve.DecodePoint(keyInfo.PublicKey.GetBytes()); return new ECPublicKeyParameters("EC", q, domainParameters); } else if (ECGost3410Utilities.IsKeyAlgorithmOid(algOid)) { var ecGost3410Parameters = ECGost3410Utilities.CreateECGost3410Parameters(algID); Asn1OctetString key; try { key = Asn1OctetString.GetInstance(keyInfo.ParsePublicKey()); } catch (IOException e) { throw new ArgumentException("error recovering ECGOST3410 public key", e); } // Little-endian X then Y, each a field element int fieldSize = ECGost3410Utilities.GetFieldElementEncodingLength(ecGost3410Parameters); int keySize = 2 * fieldSize; byte[] keyEnc = key.GetOctets(); if (keyEnc.Length != keySize) throw new ArgumentException("invalid length for ECGOST3410 public key"); byte[] x9Encoding = new byte[1 + keySize]; x9Encoding[0] = 0x04; for (int i = 1; i <= fieldSize; ++i) { x9Encoding[i] = keyEnc[fieldSize - i]; x9Encoding[i + fieldSize] = keyEnc[keySize - i]; } ECPoint q = ecGost3410Parameters.Curve.DecodePoint(x9Encoding); string algorithm = ECGost3410Utilities.GetAlgorithmName(algOid, ecGost3410Parameters); return new ECPublicKeyParameters(algorithm, q, ecGost3410Parameters); } else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x94)) { Gost3410PublicKeyAlgParameters algParams = Gost3410PublicKeyAlgParameters.GetInstance(algID.Parameters); Asn1OctetString key; try { key = (Asn1OctetString)keyInfo.ParsePublicKey(); } catch (IOException e) { throw new ArgumentException("error recovering GOST3410_94 public key", e); } byte[] keyBytes = key.GetOctets(); BigInteger y = new BigInteger(1, keyBytes, bigEndian: false); return new Gost3410PublicKeyParameters(y, algParams.PublicKeyParamSet); } else if (algOid.Equals(EdECObjectIdentifiers.id_X25519) || algOid.Equals(CryptlibObjectIdentifiers.curvey25519)) { return new X25519PublicKeyParameters(GetRawKey(keyInfo)); } else if (algOid.Equals(EdECObjectIdentifiers.id_X448)) { return new X448PublicKeyParameters(GetRawKey(keyInfo)); } else if (algOid.Equals(EdECObjectIdentifiers.id_Ed25519) || algOid.Equals(GnuObjectIdentifiers.Ed25519)) { return new Ed25519PublicKeyParameters(GetRawKey(keyInfo)); } else if (algOid.Equals(EdECObjectIdentifiers.id_Ed448)) { return new Ed448PublicKeyParameters(GetRawKey(keyInfo)); } else if (MLDsaParameters.ByOid.TryGetValue(algOid, out MLDsaParameters mlDsaParameters)) { return GetMLDsaPublicKey(mlDsaParameters, keyInfo.PublicKey); } else if (CompositeMLDsaParameters.ByOid.TryGetValue(algOid, out CompositeMLDsaParameters compositeMLDsaParameters)) { return CompositeMLDsaPublicKeyParameters.FromEncoding(compositeMLDsaParameters, keyInfo.PublicKey.GetOctets()); } else if (MLKemParameters.ByOid.TryGetValue(algOid, out MLKemParameters mlKemParameters)) { return GetMLKemPublicKey(mlKemParameters, keyInfo.PublicKey); } else if (SlhDsaParameters.ByOid.TryGetValue(algOid, out SlhDsaParameters slhDsaParameters)) { return GetSlhDsaPublicKey(slhDsaParameters, keyInfo.PublicKey); } else if (UAObjectIdentifiers.dstu4145be.Equals(algOid) || UAObjectIdentifiers.dstu4145le.Equals(algOid)) { bool isLE = UAObjectIdentifiers.dstu4145le.Equals(algOid); Dstu4145Params dstuParams = Dstu4145Params.GetInstance(algID.Parameters); Asn1OctetString key; try { key = (Asn1OctetString)keyInfo.ParsePublicKey(); } catch (Exception) { throw new ArgumentException("error recovering DSTU public key"); } byte[] keyOctets = key.GetOctets(); if (isLE) { keyOctets = Arrays.Reverse(keyOctets); } ECDomainParameters ecDomain; if (dstuParams.IsNamedCurve) { ecDomain = Dstu4145NamedCurves.GetByOid(dstuParams.NamedCurve); } else { Dstu4145ECBinary ecBinary = dstuParams.ECBinary; BigInteger b = new BigInteger(1, ecBinary.B.GetOctets(), bigEndian: !isLE); Dstu4145BinaryField field = ecBinary.Field; ECCurve curve = new F2mCurve(field.M, field.K, field.J, field.L, ecBinary.A.Value, b, null, null); byte[] bpOctets = ecBinary.BP.GetOctets(); if (isLE) { bpOctets = Arrays.Reverse(bpOctets); } ECPoint g = Dstu4145PointEncoder.DecodePoint(curve, bpOctets); ecDomain = new ECDomainParameters(curve, g, ecBinary.N.Value); } ECPoint q = Dstu4145PointEncoder.DecodePoint(ecDomain.Curve, keyOctets); return new ECPublicKeyParameters(q, ecDomain); } else { throw new SecurityUtilityException("algorithm identifier in public key not recognised: " + algOid); } } internal static MLDsaPublicKeyParameters GetMLDsaPublicKey(MLDsaParameters mlDsaParameters, DerBitString publicKey) { if (publicKey.IsOctetAligned()) { int publicKeyLength = mlDsaParameters.ParameterSet.PublicKeyLength; int bytesLength = publicKey.GetBytesLength(); if (bytesLength == publicKeyLength) // TODO[pqc] Avoid redundant copies? return MLDsaPublicKeyParameters.FromEncoding(mlDsaParameters, encoding: publicKey.GetOctets()); // TODO[pqc] Remove support for legacy/prototype formats? if (bytesLength > publicKeyLength) { try { Asn1Object obj = Asn1Object.FromMemoryStream(publicKey.GetOctetMemoryStream()); if (obj is Asn1OctetString oct) { if (oct.GetOctetsLength() == publicKeyLength) return MLDsaPublicKeyParameters.FromEncoding(mlDsaParameters, encoding: oct.GetOctets()); } } catch (Exception) { } } } throw new ArgumentException("invalid " + mlDsaParameters.Name + " public key"); } internal static MLKemPublicKeyParameters GetMLKemPublicKey(MLKemParameters mlKemParameters, DerBitString publicKey) { if (publicKey.IsOctetAligned()) { int publicKeyLength = mlKemParameters.ParameterSet.Engine.PublicKeyBytes; int bytesLength = publicKey.GetBytesLength(); if (bytesLength == publicKeyLength) // TODO[pqc] Avoid redundant copies? return MLKemPublicKeyParameters.FromEncoding(mlKemParameters, encoding: publicKey.GetOctets()); } throw new ArgumentException("invalid " + mlKemParameters.Name + " public key"); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static ReadOnlySpan GetRawKey(SubjectPublicKeyInfo keyInfo) { /* * TODO[RFC 8422] * - Require keyInfo.Algorithm.Parameters == null? */ return keyInfo.PublicKey.GetOctetsSpan(); } #else private static byte[] GetRawKey(SubjectPublicKeyInfo keyInfo) { /* * TODO[RFC 8422] * - Require keyInfo.Algorithm.Parameters == null? */ return keyInfo.PublicKey.GetOctets(); } #endif internal static SlhDsaPublicKeyParameters GetSlhDsaPublicKey(SlhDsaParameters slhDsaParameters, DerBitString publicKey) { if (publicKey.IsOctetAligned()) { int publicKeyLength = slhDsaParameters.ParameterSet.PublicKeyLength; int bytesLength = publicKey.GetBytesLength(); if (bytesLength == publicKeyLength) // TODO[pqc] Avoid redundant copies? return SlhDsaPublicKeyParameters.FromEncoding(slhDsaParameters, encoding: publicKey.GetOctets()); // TODO[api] Eventually remove legacy support for OCTET STRING encoding if (bytesLength > publicKeyLength) { try { Asn1Object obj = Asn1Object.FromMemoryStream(publicKey.GetOctetMemoryStream()); if (obj is Asn1OctetString oct) { if (oct.GetOctetsLength() == 4 + publicKeyLength) { byte[] octets = oct.GetOctets(); byte[] encoding = Arrays.CopyOfRange(octets, 4, octets.Length); return SlhDsaPublicKeyParameters.FromEncoding(slhDsaParameters, encoding); } } } catch (Exception) { // Ignore } } } throw new ArgumentException("invalid " + slhDsaParameters.Name + " public key"); } private static bool IsPkcsDHParam(Asn1Sequence seq) { if (seq.Count == 2) return true; if (seq.Count > 3) return false; DerInteger l = DerInteger.GetInstance(seq[2]); DerInteger p = DerInteger.GetInstance(seq[0]); return l.Value.CompareTo(BigInteger.ValueOf(p.Value.BitLength)) <= 0; } private static DHPublicKeyParameters ReadPkcsDHParam(DerObjectIdentifier algOid, BigInteger y, Asn1Sequence seq) { DHParameter para = DHParameter.GetInstance(seq); BigInteger lVal = para.L; int l = lVal == null ? 0 : lVal.IntValue; DHParameters dhParams = new DHParameters(para.P, para.G, null, l); return new DHPublicKeyParameters(y, dhParams, algOid); } } }