@@ -887,6 +887,225 @@ static void ClearAndReturnToPool(byte[] buffer, int clearSize)
887
887
/// </exception>
888
888
protected abstract bool TryExportPkcs8PrivateKeyCore ( Span < byte > destination , out int bytesWritten ) ;
889
889
890
+ /// <summary>
891
+ /// Attempts to export the current key in the PKCS#8 EncryptedPrivateKeyInfo format into a provided buffer,
892
+ /// using a char-based password.
893
+ /// </summary>
894
+ /// <param name="password">
895
+ /// The password to use when encrypting the key material.
896
+ /// </param>
897
+ /// <param name="pbeParameters">
898
+ /// The password-based encryption (PBE) parameters to use when encrypting the key material.
899
+ /// </param>
900
+ /// <param name="destination">
901
+ /// The buffer to receive the PKCS#8 EncryptedPrivateKeyInfo value.
902
+ /// </param>
903
+ /// <param name="bytesWritten">
904
+ /// When this method returns, contains the number of bytes written to the <paramref name="destination"/> buffer.
905
+ /// This parameter is treated as uninitialized.
906
+ /// </param>
907
+ /// <returns>
908
+ /// <see langword="true" /> if <paramref name="destination"/> was large enough to hold the result;
909
+ /// otherwise, <see langword="false" />.
910
+ /// </returns>
911
+ /// <exception cref="ArgumentNullException">
912
+ /// <paramref name="pbeParameters"/> is <see langword="null"/>.
913
+ /// </exception>
914
+ /// <exception cref="ObjectDisposedException">
915
+ /// This instance has been disposed.
916
+ /// </exception>
917
+ /// <exception cref="CryptographicException">
918
+ /// <para>This instance only represents a public key.</para>
919
+ /// <para>-or-</para>
920
+ /// <para>The private key is not exportable.</para>
921
+ /// <para>-or-</para>
922
+ /// <para>An error occurred while exporting the key.</para>
923
+ /// <para>-or-</para>
924
+ /// <para><paramref name="pbeParameters"/> does not represent a valid password-based encryption algorithm.</para>
925
+ /// </exception>
926
+ public bool TryExportEncryptedPkcs8PrivateKey (
927
+ ReadOnlySpan < char > password ,
928
+ PbeParameters pbeParameters ,
929
+ Span < byte > destination ,
930
+ out int bytesWritten )
931
+ {
932
+ ThrowIfNull ( pbeParameters ) ;
933
+ PasswordBasedEncryption . ValidatePbeParameters ( pbeParameters , password , ReadOnlySpan < byte > . Empty ) ;
934
+ ThrowIfDisposed ( ) ;
935
+
936
+ AsnWriter writer = ExportEncryptedPkcs8PrivateKeyCore < char > (
937
+ password ,
938
+ pbeParameters ,
939
+ KeyFormatHelper . WriteEncryptedPkcs8 ) ;
940
+ return writer . TryEncode ( destination , out bytesWritten ) ;
941
+ }
942
+
943
+ /// <summary>
944
+ /// Attempts to export the current key in the PKCS#8 EncryptedPrivateKeyInfo format into a provided buffer,
945
+ /// using a byte-based password.
946
+ /// </summary>
947
+ /// <param name="passwordBytes">
948
+ /// The password to use when encrypting the key material.
949
+ /// </param>
950
+ /// <param name="pbeParameters">
951
+ /// The password-based encryption (PBE) parameters to use when encrypting the key material.
952
+ /// </param>
953
+ /// <param name="destination">
954
+ /// The buffer to receive the PKCS#8 EncryptedPrivateKeyInfo value.
955
+ /// </param>
956
+ /// <param name="bytesWritten">
957
+ /// When this method returns, contains the number of bytes written to the <paramref name="destination"/> buffer.
958
+ /// This parameter is treated as uninitialized.
959
+ /// </param>
960
+ /// <returns>
961
+ /// <see langword="true" /> if <paramref name="destination"/> was large enough to hold the result;
962
+ /// otherwise, <see langword="false" />.
963
+ /// </returns>
964
+ /// <exception cref="ArgumentNullException">
965
+ /// <paramref name="pbeParameters"/> is <see langword="null"/>.
966
+ /// </exception>
967
+ /// <exception cref="ObjectDisposedException">
968
+ /// This instance has been disposed.
969
+ /// </exception>
970
+ /// <exception cref="CryptographicException">
971
+ /// <para>This instance only represents a public key.</para>
972
+ /// <para>-or-</para>
973
+ /// <para>The private key is not exportable.</para>
974
+ /// <para>-or-</para>
975
+ /// <para>An error occurred while exporting the key.</para>
976
+ /// <para>-or-</para>
977
+ /// <para><paramref name="pbeParameters"/> does not represent a valid password-based encryption algorithm.</para>
978
+ /// </exception>
979
+ public bool TryExportEncryptedPkcs8PrivateKey (
980
+ ReadOnlySpan < byte > passwordBytes ,
981
+ PbeParameters pbeParameters ,
982
+ Span < byte > destination ,
983
+ out int bytesWritten )
984
+ {
985
+ ThrowIfNull ( pbeParameters ) ;
986
+ PasswordBasedEncryption . ValidatePbeParameters ( pbeParameters , ReadOnlySpan < char > . Empty , passwordBytes ) ;
987
+ ThrowIfDisposed ( ) ;
988
+
989
+ AsnWriter writer = ExportEncryptedPkcs8PrivateKeyCore < byte > (
990
+ passwordBytes ,
991
+ pbeParameters ,
992
+ KeyFormatHelper . WriteEncryptedPkcs8 ) ;
993
+ return writer . TryEncode ( destination , out bytesWritten ) ;
994
+ }
995
+
996
+ /// <summary>
997
+ /// Exports the current key in the PKCS#8 EncryptedPrivateKeyInfo format with a byte-based password.
998
+ /// </summary>
999
+ /// <param name="passwordBytes">
1000
+ /// The password to use when encrypting the key material.
1001
+ /// </param>
1002
+ /// <param name="pbeParameters">
1003
+ /// The password-based encryption (PBE) parameters to use when encrypting the key material.
1004
+ /// </param>
1005
+ /// <returns>
1006
+ /// A byte array containing the PKCS#8 EncryptedPrivateKeyInfo representation of the this key.
1007
+ /// </returns>
1008
+ /// <exception cref="ArgumentNullException">
1009
+ /// <paramref name="pbeParameters"/> is <see langword="null"/>.
1010
+ /// </exception>
1011
+ /// <exception cref="ObjectDisposedException">
1012
+ /// This instance has been disposed.
1013
+ /// </exception>
1014
+ /// <exception cref="CryptographicException">
1015
+ /// <para>This instance only represents a public key.</para>
1016
+ /// <para>-or-</para>
1017
+ /// <para>The private key is not exportable.</para>
1018
+ /// <para>-or-</para>
1019
+ /// <para>An error occurred while exporting the key.</para>
1020
+ /// <para>-or-</para>
1021
+ /// <para><paramref name="pbeParameters"/> does not represent a valid password-based encryption algorithm.</para>
1022
+ /// </exception>
1023
+ public byte [ ] ExportEncryptedPkcs8PrivateKey ( ReadOnlySpan < byte > passwordBytes , PbeParameters pbeParameters )
1024
+ {
1025
+ ThrowIfNull ( pbeParameters ) ;
1026
+ PasswordBasedEncryption . ValidatePbeParameters ( pbeParameters , ReadOnlySpan < char > . Empty , passwordBytes ) ;
1027
+ ThrowIfDisposed ( ) ;
1028
+
1029
+ AsnWriter writer = ExportEncryptedPkcs8PrivateKeyCore < byte > (
1030
+ passwordBytes ,
1031
+ pbeParameters ,
1032
+ KeyFormatHelper . WriteEncryptedPkcs8 ) ;
1033
+ return writer . Encode ( ) ;
1034
+ }
1035
+
1036
+ /// <summary>
1037
+ /// Exports the current key in the PKCS#8 EncryptedPrivateKeyInfo format with a char-based password.
1038
+ /// </summary>
1039
+ /// <param name="password">
1040
+ /// The password to use when encrypting the key material.
1041
+ /// </param>
1042
+ /// <param name="pbeParameters">
1043
+ /// The password-based encryption (PBE) parameters to use when encrypting the key material.
1044
+ /// </param>
1045
+ /// <returns>
1046
+ /// A byte array containing the PKCS#8 EncryptedPrivateKeyInfo representation of the this key.
1047
+ /// </returns>
1048
+ /// <exception cref="ArgumentNullException">
1049
+ /// <paramref name="pbeParameters"/> is <see langword="null"/>.
1050
+ /// </exception>
1051
+ /// <exception cref="ObjectDisposedException">
1052
+ /// This instance has been disposed.
1053
+ /// </exception>
1054
+ /// <exception cref="CryptographicException">
1055
+ /// <para>This instance only represents a public key.</para>
1056
+ /// <para>-or-</para>
1057
+ /// <para>The private key is not exportable.</para>
1058
+ /// <para>-or-</para>
1059
+ /// <para>An error occurred while exporting the key.</para>
1060
+ /// <para>-or-</para>
1061
+ /// <para><paramref name="pbeParameters"/> does not represent a valid password-based encryption algorithm.</para>
1062
+ /// </exception>
1063
+ public byte [ ] ExportEncryptedPkcs8PrivateKey ( ReadOnlySpan < char > password , PbeParameters pbeParameters )
1064
+ {
1065
+ ThrowIfNull ( pbeParameters ) ;
1066
+ PasswordBasedEncryption . ValidatePbeParameters ( pbeParameters , password , ReadOnlySpan < byte > . Empty ) ;
1067
+ ThrowIfDisposed ( ) ;
1068
+
1069
+ AsnWriter writer = ExportEncryptedPkcs8PrivateKeyCore < char > (
1070
+ password ,
1071
+ pbeParameters ,
1072
+ KeyFormatHelper . WriteEncryptedPkcs8 ) ;
1073
+ return writer . Encode ( ) ;
1074
+ }
1075
+
1076
+ /// <summary>
1077
+ /// Exports the current key in the PKCS#8 EncryptedPrivateKeyInfo format with a char-based password.
1078
+ /// </summary>
1079
+ /// <param name="password">
1080
+ /// The password to use when encrypting the key material.
1081
+ /// </param>
1082
+ /// <param name="pbeParameters">
1083
+ /// The password-based encryption (PBE) parameters to use when encrypting the key material.
1084
+ /// </param>
1085
+ /// <returns>
1086
+ /// A byte array containing the PKCS#8 EncryptedPrivateKeyInfo representation of the this key.
1087
+ /// </returns>
1088
+ /// <exception cref="ArgumentNullException">
1089
+ /// <paramref name="pbeParameters" /> or <paramref name="password" /> is <see langword="null" />.
1090
+ /// </exception>
1091
+ /// <exception cref="ObjectDisposedException">
1092
+ /// This instance has been disposed.
1093
+ /// </exception>
1094
+ /// <exception cref="CryptographicException">
1095
+ /// <para>This instance only represents a public key.</para>
1096
+ /// <para>-or-</para>
1097
+ /// <para>The private key is not exportable.</para>
1098
+ /// <para>-or-</para>
1099
+ /// <para>An error occurred while exporting the key.</para>
1100
+ /// <para>-or-</para>
1101
+ /// <para><paramref name="pbeParameters"/> does not represent a valid password-based encryption algorithm.</para>
1102
+ /// </exception>
1103
+ public byte [ ] ExportEncryptedPkcs8PrivateKey ( string password , PbeParameters pbeParameters )
1104
+ {
1105
+ ThrowIfNull ( password ) ;
1106
+ return ExportEncryptedPkcs8PrivateKey ( password . AsSpan ( ) , pbeParameters ) ;
1107
+ }
1108
+
890
1109
/// <summary>
891
1110
/// Imports an ML-KEM encapsulation key from an X.509 SubjectPublicKeyInfo structure.
892
1111
/// </summary>
@@ -1452,5 +1671,41 @@ private static void ThrowIfNull(
1452
1671
}
1453
1672
#endif
1454
1673
}
1674
+
1675
+ private AsnWriter ExportEncryptedPkcs8PrivateKeyCore < TChar > (
1676
+ ReadOnlySpan < TChar > password ,
1677
+ PbeParameters pbeParameters ,
1678
+ WriteEncryptedPkcs8Func < TChar > encryptor )
1679
+ {
1680
+ // There are 28 bytes of overhead on a plain PKCS#8 export for an expanded key. Add a little extra for
1681
+ // some extra space.
1682
+ int initialSize = Algorithm . DecapsulationKeySizeInBytes + 32 ;
1683
+ byte [ ] rented = CryptoPool . Rent ( initialSize ) ;
1684
+ int written ;
1685
+
1686
+ while ( ! TryExportPkcs8PrivateKey ( rented , out written ) )
1687
+ {
1688
+ CryptoPool . Return ( rented , 0 ) ;
1689
+ rented = CryptoPool . Rent ( rented . Length * 2 ) ;
1690
+ }
1691
+
1692
+ AsnWriter tmp = new ( AsnEncodingRules . BER , initialCapacity : written ) ;
1693
+
1694
+ try
1695
+ {
1696
+ tmp . WriteEncodedValueForCrypto ( rented . AsSpan ( 0 , written ) ) ;
1697
+ return encryptor ( password , tmp , pbeParameters ) ;
1698
+ }
1699
+ finally
1700
+ {
1701
+ tmp . Reset ( ) ;
1702
+ CryptoPool . Return ( rented , written ) ;
1703
+ }
1704
+ }
1705
+
1706
+ private delegate AsnWriter WriteEncryptedPkcs8Func < TChar > (
1707
+ ReadOnlySpan < TChar > password ,
1708
+ AsnWriter writer ,
1709
+ PbeParameters pbeParameters ) ;
1455
1710
}
1456
1711
}
0 commit comments