diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
index b65f22ddf..1b68abd02 100644
--- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs
+++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -18,6 +18,9 @@
namespace Org.BouncyCastle.Pkcs
{
+ ///
+ /// A factory to produce (PKCS#8) objects from Bouncy Castle private key parameters.
+ ///
public static class PrivateKeyInfoFactory
{
private static readonly HashSet cryptoProOids = new HashSet
@@ -29,17 +32,28 @@ public static class PrivateKeyInfoFactory
CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB,
};
+ ///
+ /// Create a representation of a private key.
+ ///
+ ///
+ /// Example of exporting a private key to PKCS#8 bytes:
+ ///
+ /// byte[] pkcs8Bytes = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey).GetEncoded();
+ ///
+ ///
+ /// The private key parameters.
+ /// The object.
public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter privateKey) =>
CreatePrivateKeyInfo(privateKey, null);
- /**
- * Create a PrivateKeyInfo representation of a private key with attributes.
- *
- * @param privateKey the key to be encoded into the info object.
- * @param attributes the set of attributes to be included.
- * @return the appropriate PrivateKeyInfo
- * @throws java.io.IOException on an error encoding the key
- */
+ ///
+ /// Create a representation of a private key with attributes.
+ ///
+ /// The key to be encoded into the info object.
+ /// The set of attributes to be included.
+ /// The appropriate .
+ /// If is null.
+ /// If a public key is passed instead of a private key.
public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter privateKey, Asn1Set attributes)
{
if (privateKey == null)
@@ -267,9 +281,23 @@ public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter private
throw new ArgumentException("Class provided is not convertible: " + Platform.GetTypeName(privateKey));
}
+ ///
+ /// Create a from an encrypted representation using a passphrase.
+ ///
+ /// The password for decryption.
+ /// The encrypted private key information.
+ /// A object.
public static PrivateKeyInfo CreatePrivateKeyInfo(char[] passPhrase, EncryptedPrivateKeyInfo encInfo) =>
CreatePrivateKeyInfo(passPhrase, false, encInfo);
+ ///
+ /// Create a from an encrypted representation using a passphrase.
+ ///
+ /// The password for decryption.
+ /// If true, uses a specific zero-padding for PKCS#12 PBE (for compatibility).
+ /// The encrypted private key information.
+ /// A object.
+ /// If the encryption algorithm is unknown.
public static PrivateKeyInfo CreatePrivateKeyInfo(char[] passPhrase, bool wrongPkcs12Zero,
EncryptedPrivateKeyInfo encInfo)
{
diff --git a/crypto/src/security/DotNetUtilities.cs b/crypto/src/security/DotNetUtilities.cs
index b42b8671a..b898b6160 100644
--- a/crypto/src/security/DotNetUtilities.cs
+++ b/crypto/src/security/DotNetUtilities.cs
@@ -54,17 +54,76 @@ public static SystemX509.X509Certificate ToX509Certificate(X509CertificateStruct
public static SystemX509.X509Certificate ToX509Certificate(X509Certificate x509Cert) =>
ToX509Certificate(x509Cert.CertificateStructure);
+ ///
+ /// Create a Bouncy Castle from a .NET .
+ ///
+ /// The .NET certificate.
+ /// A Bouncy Castle .
public static X509Certificate FromX509Certificate(SystemX509.X509Certificate x509Cert) =>
new X509Certificate(x509Cert.GetRawCertData());
+ ///
+ /// Create a Bouncy Castle from a .NET .
+ ///
+ /// The .NET certificate.
+ /// A Bouncy Castle .
public static X509Certificate FromX509Certificate(SystemX509.X509Certificate2 x509Cert) =>
new X509Certificate(x509Cert.RawData);
+ ///
+ /// Extract the (an X.509 ASN.1 type used for public keys) from a .NET
+ /// .
+ ///
+ /// The .NET certificate.
+ /// A object.
+ /// If is null.
+ public static SubjectPublicKeyInfo GetSubjectPublicKeyInfo(SystemX509.X509Certificate2 certificate)
+ {
+ if (certificate == null)
+ throw new ArgumentNullException(nameof(certificate));
+
+#if NET6_0_OR_GREATER
+ return SubjectPublicKeyInfo.GetInstance(certificate.PublicKey.ExportSubjectPublicKeyInfo());
+#else
+ var bcCert = FromX509Certificate(certificate);
+ return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(bcCert.GetPublicKey());
+#endif
+ }
+
+ ///
+ /// Extract the DER-encoded bytes from a .NET
+ /// .
+ ///
+ /// The .NET certificate.
+ /// A byte array containing the DER-encoded public key info.
+ /// If is null.
+ public static byte[] GetSubjectPublicKeyInfoDer(SystemX509.X509Certificate2 certificate)
+ {
+ if (certificate == null)
+ throw new ArgumentNullException(nameof(certificate));
+
+#if NET6_0_OR_GREATER
+ return certificate.PublicKey.ExportSubjectPublicKeyInfo();
+#else
+ return GetSubjectPublicKeyInfo(certificate).GetEncoded(Asn1Encodable.Der);
+#endif
+ }
+
+ ///
+ /// Extract a DSA key pair from a .NET object.
+ ///
+ /// The .NET DSA object.
+ /// An containing the BC DSA keys.
public static AsymmetricCipherKeyPair GetDsaKeyPair(DSA dsa)
{
return GetDsaKeyPair(dsa.ExportParameters(true));
}
+ ///
+ /// Extract a DSA key pair from .
+ ///
+ /// The .NET DSA parameters.
+ /// An containing the BC DSA keys.
public static AsymmetricCipherKeyPair GetDsaKeyPair(DSAParameters dp)
{
DsaPublicKeyParameters pubKey = GetDsaPublicKey(dp);
@@ -76,11 +135,21 @@ public static AsymmetricCipherKeyPair GetDsaKeyPair(DSAParameters dp)
return new AsymmetricCipherKeyPair(pubKey, privKey);
}
+ ///
+ /// Extract DSA public key parameters from a .NET object.
+ ///
+ /// The .NET DSA object.
+ /// A object.
public static DsaPublicKeyParameters GetDsaPublicKey(DSA dsa)
{
return GetDsaPublicKey(dsa.ExportParameters(false));
}
+ ///
+ /// Extract DSA public key parameters from .
+ ///
+ /// The .NET DSA parameters.
+ /// A object.
public static DsaPublicKeyParameters GetDsaPublicKey(DSAParameters dp)
{
DsaValidationParameters validationParameters = (dp.Seed != null)
@@ -99,16 +168,32 @@ public static DsaPublicKeyParameters GetDsaPublicKey(DSAParameters dp)
}
#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER
+ ///
+ /// Extract an EC key pair from a .NET object.
+ ///
+ /// The .NET ECDsa object.
+ /// An containing the BC EC keys.
public static AsymmetricCipherKeyPair GetECDsaKeyPair(ECDsa ecDsa)
{
return GetECKeyPair("ECDSA", ecDsa.ExportParameters(true));
}
+ ///
+ /// Extract EC public key parameters from a .NET object.
+ ///
+ /// The .NET ECDsa object.
+ /// An object.
public static ECPublicKeyParameters GetECDsaPublicKey(ECDsa ecDsa)
{
return GetECPublicKey("ECDSA", ecDsa.ExportParameters(false));
}
+ ///
+ /// Extract an EC key pair from .
+ ///
+ /// The algorithm name (e.g., "ECDSA").
+ /// The .NET EC parameters.
+ /// An containing the BC EC keys.
public static AsymmetricCipherKeyPair GetECKeyPair(string algorithm, ECParameters ec)
{
ECPublicKeyParameters pubKey = GetECPublicKey(algorithm, ec);
@@ -121,6 +206,12 @@ public static AsymmetricCipherKeyPair GetECKeyPair(string algorithm, ECParameter
return new AsymmetricCipherKeyPair(pubKey, privKey);
}
+ ///
+ /// Extract EC public key parameters from .
+ ///
+ /// The algorithm name (e.g., "ECDSA").
+ /// The .NET EC parameters.
+ /// An object.
public static ECPublicKeyParameters GetECPublicKey(string algorithm, ECParameters ec)
{
X9ECParameters x9 = GetX9ECParameters(ec.Curve);
@@ -154,11 +245,21 @@ private static X9ECParameters GetX9ECParameters(ECCurve curve)
}
#endif
+ ///
+ /// Extract an RSA key pair from a .NET object.
+ ///
+ /// The .NET RSA object.
+ /// An containing the BC RSA keys.
public static AsymmetricCipherKeyPair GetRsaKeyPair(RSA rsa)
{
return GetRsaKeyPair(rsa.ExportParameters(true));
}
+ ///
+ /// Extract an RSA key pair from .
+ ///
+ /// The .NET RSA parameters.
+ /// An containing the BC RSA keys.
public static AsymmetricCipherKeyPair GetRsaKeyPair(RSAParameters rp)
{
RsaKeyParameters pubKey = GetRsaPublicKey(rp);
@@ -176,13 +277,22 @@ public static AsymmetricCipherKeyPair GetRsaKeyPair(RSAParameters rp)
return new AsymmetricCipherKeyPair(pubKey, privKey);
}
+ ///
+ /// Extract RSA public key parameters from a .NET object.
+ ///
+ /// The .NET RSA object.
+ /// An object.
public static RsaKeyParameters GetRsaPublicKey(RSA rsa)
{
return GetRsaPublicKey(rsa.ExportParameters(false));
}
- public static RsaKeyParameters GetRsaPublicKey(
- RSAParameters rp)
+ ///
+ /// Extract RSA public key parameters from .
+ ///
+ /// The .NET RSA parameters.
+ /// An object.
+ public static RsaKeyParameters GetRsaPublicKey(RSAParameters rp)
{
return new RsaKeyParameters(
false,
@@ -190,6 +300,12 @@ public static RsaKeyParameters GetRsaPublicKey(
new BigInteger(1, rp.Exponent));
}
+ ///
+ /// Extract an asymmetric key pair from a .NET object.
+ ///
+ /// The .NET private key object.
+ /// An containing the BC keys.
+ /// If the algorithm is not supported.
public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey)
{
if (privateKey is DSA dsa)
@@ -205,28 +321,41 @@ public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey)
throw new ArgumentException("Unsupported algorithm specified", nameof(privateKey));
}
-
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
+ ///
+ /// Create a .NET instance from Bouncy Castle RSA public key parameters.
+ ///
+ /// The BC RSA public key.
+ /// A .NET instance.
public static RSA ToRSA(RsaKeyParameters rsaKey)
{
- // TODO This appears to not work for private keys (when no CRT info)
return CreateRSAProvider(ToRSAParameters(rsaKey));
}
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
+ ///
+ /// Create a .NET instance from Bouncy Castle RSA public key parameters.
+ ///
+ /// The BC RSA public key.
+ /// The .NET CspParameters.
+ /// A .NET instance.
public static RSA ToRSA(RsaKeyParameters rsaKey, CspParameters csp)
{
- // TODO This appears to not work for private keys (when no CRT info)
return CreateRSAProvider(ToRSAParameters(rsaKey), csp);
}
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
+ ///
+ /// Create a .NET instance from Bouncy Castle RSA private CRT parameters.
+ ///
+ /// The BC RSA private CRT keys.
+ /// A .NET instance.
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
{
return CreateRSAProvider(ToRSAParameters(privKey));
@@ -235,6 +364,12 @@ public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
+ ///
+ /// Create a .NET instance from Bouncy Castle RSA private CRT parameters and CSP info.
+ ///
+ /// The BC RSA private CRT keys.
+ /// The .NET CspParameters.
+ /// A .NET instance.
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey, CspParameters csp)
{
return CreateRSAProvider(ToRSAParameters(privKey), csp);
@@ -243,6 +378,11 @@ public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey, CspParameters csp)
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
+ ///
+ /// Create a .NET instance from Bouncy Castle RSA private CRT structure.
+ ///
+ /// The BC RSA private CRT keys.
+ /// A .NET instance.
public static RSA ToRSA(RsaPrivateKeyStructure privKey)
{
return CreateRSAProvider(ToRSAParameters(privKey));
@@ -251,11 +391,22 @@ public static RSA ToRSA(RsaPrivateKeyStructure privKey)
#if NET5_0_OR_GREATER
[SupportedOSPlatform("windows")]
#endif
+ ///
+ /// Create a .NET instance from Bouncy Castle RSA private CRT structure and CSP info.
+ ///
+ /// The BC RSA private CRT keys.
+ /// The .NET CspParameters.
+ /// A .NET instance.
public static RSA ToRSA(RsaPrivateKeyStructure privKey, CspParameters csp)
{
return CreateRSAProvider(ToRSAParameters(privKey), csp);
}
+ ///
+ /// Convert Bouncy Castle RSA public key parameters to .NET .
+ ///
+ /// The BC RSA key.
+ /// A .NET object.
public static RSAParameters ToRSAParameters(RsaKeyParameters rsaKey)
{
RSAParameters rp = new RSAParameters();
@@ -267,6 +418,11 @@ public static RSAParameters ToRSAParameters(RsaKeyParameters rsaKey)
return rp;
}
+ ///
+ /// Convert Bouncy Castle RSA private CRT parameters to .NET .
+ ///
+ /// The BC RSA key.
+ /// A .NET object.
public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = new RSAParameters();
@@ -281,6 +437,11 @@ public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
return rp;
}
+ ///
+ /// Convert Bouncy Castle RSA private CRT structure to .NET .
+ ///
+ /// The BC RSA key.
+ /// A .NET object.
public static RSAParameters ToRSAParameters(RsaPrivateKeyStructure privKey)
{
RSAParameters rp = new RSAParameters();
diff --git a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
index c798d0362..7639b8077 100644
--- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
+++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
@@ -17,8 +17,13 @@
namespace Org.BouncyCastle.X509
{
///
- /// A factory to produce Public Key Info Objects.
+ /// A factory to produce (an X.509 ASN.1 type used for public keys) objects from
+ /// Bouncy Castle public key parameters.
///
+ ///
+ /// This class handles the correct encoding of the AlgorithmIdentifier for various algorithms, including mandatory
+ /// parameters like the DER NULL for RSA.
+ ///
public static class SubjectPublicKeyInfoFactory
{
private static readonly HashSet cryptoProOids = new HashSet
@@ -31,11 +36,21 @@ public static class SubjectPublicKeyInfoFactory
};
///
- /// Create a Subject Public Key Info object for a given public key.
+ /// Create a object for a given public key.
///
- /// One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters
- /// A subject public key info object.
- /// Throw exception if object provided is not one of the above.
+ ///
+ /// Example of converting a .NET X509Certificate2 to a DER-encoded SubjectPublicKeyInfo byte array:
+ ///
+ /// var publicKey = DotNetUtilities.FromX509Certificate(certificate).GetPublicKey();
+ /// byte[] encoded = SubjectPublicKeyInfoFactory
+ /// .CreateSubjectPublicKeyInfo(publicKey).GetEncoded(Asn1Encodable.Der);
+ ///
+ ///
+ /// The public key parameters (e.g., ,
+ /// , etc.).
+ /// A object representing the public key.
+ /// If is null.
+ /// If a private key is passed instead of a public key.
public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey)
{
if (publicKey == null)
diff --git a/crypto/test/src/security/test/TestDotNetUtil.cs b/crypto/test/src/security/test/TestDotNetUtil.cs
index b730fdfb2..2f54adee6 100644
--- a/crypto/test/src/security/test/TestDotNetUtil.cs
+++ b/crypto/test/src/security/test/TestDotNetUtil.cs
@@ -11,6 +11,7 @@
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
@@ -73,6 +74,38 @@ public void TestRsaInterop()
}
}
+#if NET6_0_OR_GREATER
+ [Test]
+ public void TestGetSubjectPublicKeyInfoDer()
+ {
+ RsaKeyPairGenerator pGen = new RsaKeyPairGenerator();
+ pGen.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
+ AsymmetricCipherKeyPair pair = pGen.GenerateKeyPair();
+
+ X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+ certGen.SetSerialNumber(BigInteger.One);
+ certGen.SetIssuerDN(new X509Name("CN=Test Issuer"));
+ certGen.SetSubjectDN(new X509Name("CN=Test Subject"));
+ certGen.SetNotBefore(DateTime.UtcNow.AddDays(-1));
+ certGen.SetNotAfter(DateTime.UtcNow.AddDays(1));
+ certGen.SetPublicKey(pair.Public);
+
+ ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WithRSA", pair.Private);
+ X509Certificate bcCert = certGen.Generate(signatureFactory);
+
+ var dotNetCert = (SystemX509.X509Certificate2)DotNetUtilities.ToX509Certificate(bcCert);
+
+ byte[] encoded = DotNetUtilities.GetSubjectPublicKeyInfoDer(dotNetCert);
+
+ // OID for rsaEncryption (06 09 2a 86 48 86 f7 0d 01 01 01) followed by NULL (05 00)
+ string hexEncoded = Hex.ToHexString(encoded).ToLowerInvariant();
+ string expectedOidAndNull = "06092a864886f70d0101010500";
+
+ Assert.IsTrue(hexEncoded.Contains(expectedOidAndNull),
+ "GetSubjectPublicKeyInfoDer failed to produce correct RSA encoding with NULL parameters.");
+ }
+#endif
+
[Test]
public void TestX509CertificateConversion()
{
diff --git a/crypto/test/src/security/test/TestEncodings.cs b/crypto/test/src/security/test/TestEncodings.cs
index 330054e84..12d16220f 100644
--- a/crypto/test/src/security/test/TestEncodings.cs
+++ b/crypto/test/src/security/test/TestEncodings.cs
@@ -2,9 +2,11 @@
using NUnit.Framework;
+using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
@@ -176,6 +178,40 @@ public void TestDSA()
}
+ [Test]
+ public void TestRsaPublicKeyInfoEncodingHasNullParameters()
+ {
+ RsaKeyPairGenerator pGen = new RsaKeyPairGenerator();
+ pGen.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
+ AsymmetricCipherKeyPair pair = pGen.GenerateKeyPair();
+ RsaKeyParameters pubKey = (RsaKeyParameters)pair.Public;
+
+ SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey);
+ byte[] encoded = info.GetEncoded(Asn1Encodable.Der);
+
+ // RFC 8017 / PKCS#1 v2.2: AlgorithmIdentifier for rsaEncryption (1.2.840.113549.1.1.1) MUST include
+ // DER NULL (05 00) in its parameters field.
+ string hexEncoded = Hex.ToHexString(encoded).ToLowerInvariant();
+ string expectedSequence = "06092a864886f70d0101010500";
+
+ Assert.IsTrue(hexEncoded.Contains(expectedSequence),
+ "RSA AlgorithmIdentifier in SubjectPublicKeyInfo missing mandatory NULL parameters (05 00).");
+ }
+
+ [Test]
+ public void TestSubjectPublicKeyInfoFactoryRsaConsistency()
+ {
+ RsaKeyPairGenerator pGen = new RsaKeyPairGenerator();
+ pGen.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
+ AsymmetricCipherKeyPair pair = pGen.GenerateKeyPair();
+
+ SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pair.Public);
+ AlgorithmIdentifier algId = info.AlgorithmID;
+
+ Assert.AreEqual(PkcsObjectIdentifiers.RsaEncryption, algId.Algorithm);
+ Assert.IsInstanceOf(algId.Parameters, "RSA AlgorithmIdentifier parameters should be DerNull.");
+ }
+
[Test]
public void TestGost2012()
{