@@ -45,13 +45,53 @@ public static class PEMReader
4545 {
4646 #region Public Methods
4747 /// <summary>
48+ /// Import an RSA private key from PEM.
49+ /// </summary>
50+ public static RSA ImportRsaPrivateKeyFromPEM (
51+ byte [ ] pemDataBlob ,
52+ string password = null )
53+ {
54+ AsymmetricAlgorithm key = ImportPrivateKey ( pemDataBlob , password ) ;
55+ if ( key is RSA rsaKey )
56+ {
57+ return rsaKey ;
58+ }
59+ else
60+ {
61+ throw new CryptographicException ( "PEM data does not contain a valid RSA private key" ) ;
62+ }
63+ }
64+
65+ /// <summary>
66+ /// Import an ECDSa private key from PEM.
67+ /// </summary>
68+ public static ECDsa ImportECDsaPrivateKeyFromPEM (
69+ byte [ ] pemDataBlob ,
70+ string password = null )
71+ {
72+ AsymmetricAlgorithm key = ImportPrivateKey ( pemDataBlob , password ) ;
73+ if ( key is ECDsa ecKey )
74+ {
75+ return ecKey ;
76+ }
77+ else
78+ {
79+ throw new CryptographicException ( "PEM data does not contain a valid RSA private key" ) ;
80+ }
81+ }
82+
83+
84+ #endregion
85+
86+ #region Private
87+ /// <summary>
4888 /// Import a private key from PEM.
4989 /// </summary>
50- public static RSA ImportPrivateKeyFromPEM (
90+ private static AsymmetricAlgorithm ImportPrivateKey (
5191 byte [ ] pemDataBlob ,
5292 string password = null )
5393 {
54- RSA rsaPrivateKey = null ;
94+
5595 Org . BouncyCastle . OpenSsl . PemReader pemReader ;
5696 using ( var pemStreamReader = new StreamReader ( new MemoryStream ( pemDataBlob ) , Encoding . UTF8 , true ) )
5797 {
@@ -64,30 +104,35 @@ public static RSA ImportPrivateKeyFromPEM(
64104 var pwFinder = new Password ( password . ToCharArray ( ) ) ;
65105 pemReader = new Org . BouncyCastle . OpenSsl . PemReader ( pemStreamReader , pwFinder ) ;
66106 }
107+
108+ AsymmetricAlgorithm key = null ;
67109 try
68110 {
69111 // find the private key in the PEM blob
70112 object pemObject = pemReader . ReadObject ( ) ;
71113 while ( pemObject != null )
72114 {
73- RsaPrivateCrtKeyParameters privateKey = null ;
74115 if ( pemObject is Org . BouncyCastle . Crypto . AsymmetricCipherKeyPair keypair )
75116 {
76- privateKey = keypair . Private as RsaPrivateCrtKeyParameters ;
117+ pemObject = keypair . Private ;
77118 }
78119
79- if ( privateKey == null )
120+ // Check for an RSA private key
121+ if ( pemObject is RsaPrivateCrtKeyParameters rsaParams )
80122 {
81- privateKey = pemObject as RsaPrivateCrtKeyParameters ;
123+ var rsa = RSA . Create ( ) ;
124+ rsa . ImportParameters ( DotNetUtilities . ToRSAParameters ( rsaParams ) ) ;
125+ key = rsa ;
126+ break ;
82127 }
83-
84- if ( privateKey != null )
128+ // Check for an EC private key
129+ if ( pemObject is ECPrivateKeyParameters ecParams )
85130 {
86- rsaPrivateKey = RSA . Create ( ) ;
87- rsaPrivateKey . ImportParameters ( DotNetUtilities . ToRSAParameters ( privateKey ) ) ;
131+ var ecdsa = CreateECDsaFromECPrivateKey ( ecParams ) ;
132+ key = ecdsa ;
88133 break ;
89134 }
90-
135+
91136 // read next object
92137 pemObject = pemReader . ReadObject ( ) ;
93138 }
@@ -96,14 +141,78 @@ public static RSA ImportPrivateKeyFromPEM(
96141 {
97142 pemReader . Reader . Dispose ( ) ;
98143 }
144+ if ( key == null )
145+ {
146+ throw new CryptographicException ( "PEM data blob does not contain a private key." ) ;
147+ }
148+ return key ;
149+ }
150+ }
151+
152+ private static ECDsa CreateECDsaFromECPrivateKey ( ECPrivateKeyParameters eCPrivateKeyParameters )
153+ {
154+ var domainParams = eCPrivateKeyParameters . Parameters ;
155+
156+ // calculate keySize round up (bitLength + 7) / 8
157+ int keySizeBytes = ( domainParams . N . BitLength + 7 ) / 8 ;
158+
159+ var curveOid = eCPrivateKeyParameters . PublicKeyParamSet . Id ;
160+ var curve = ECCurve . CreateFromOid ( new Oid ( curveOid ) ) ;
161+
162+ var q = domainParams . G . Multiply ( eCPrivateKeyParameters . D ) . Normalize ( ) ;
163+ var x = q . AffineXCoord . ToBigInteger ( ) . ToByteArrayUnsigned ( ) ;
164+ var y = q . AffineYCoord . ToBigInteger ( ) . ToByteArrayUnsigned ( ) ;
165+ var d = eCPrivateKeyParameters . D . ToByteArrayUnsigned ( ) ;
166+
167+ // pad all to the same length since ToByteArrayUnsigned might drop leading zeroes
168+ x = PadWithLeadingZeros ( x , keySizeBytes ) ;
169+ y = PadWithLeadingZeros ( y , keySizeBytes ) ;
170+ d = PadWithLeadingZeros ( d , keySizeBytes ) ;
171+
172+
173+ var ecParams = new ECParameters {
174+ Curve = curve ,
175+ Q =
176+ {
177+ X = x ,
178+ Y = y
179+ } ,
180+ D = d
181+ } ;
182+
183+ var ecdsa = ECDsa . Create ( ) ;
184+ ecdsa . ImportParameters ( ecParams ) ;
185+
186+ return ecdsa ;
187+ }
188+
189+ /// <summary>
190+ /// Pads a byte array with leading zeros to reach the specifieed size
191+ /// If the input is allready the given size, it just returns it
192+ /// </summary>
193+ /// <param name="arrayToPad">Provided array to pad</param>
194+ /// <param name="desiredSize">The desired total length of byte array after padding</param>
195+ /// <returns></returns>
196+ private static byte [ ] PadWithLeadingZeros ( byte [ ] arrayToPad , int desiredSize )
197+ {
198+ if ( arrayToPad . Length == desiredSize )
199+ {
200+ return arrayToPad ;
99201 }
100202
101- if ( rsaPrivateKey == null )
203+ int paddingLength = desiredSize - arrayToPad . Length ;
204+ if ( paddingLength < 0 )
102205 {
103- throw new CryptographicException ( "PEM data blob does not contain a private key .") ;
206+ throw new ArgumentException ( $ "Input byte array is larger than the desired size { desiredSize } bytes .") ;
104207 }
105208
106- return rsaPrivateKey ;
209+ var paddedArray = new byte [ desiredSize ] ;
210+
211+ // Right-align the arrayToPad into paddedArray
212+ Buffer . BlockCopy ( arrayToPad , 0 , paddedArray , paddingLength , arrayToPad . Length ) ;
213+
214+ return paddedArray ;
215+
107216 }
108217 #endregion
109218
0 commit comments