boring_imp/
pkey.rs

1//! Public/private key processing.
2//!
3//! Asymmetric public key algorithms solve the problem of establishing and sharing
4//! secret keys to securely send and receive messages.
5//! This system uses a pair of keys: a public key, which can be freely
6//! distributed, and a private key, which is kept to oneself. An entity may
7//! encrypt information using a user's public key. The encrypted information can
8//! only be deciphered using that user's private key.
9//!
10//! This module offers support for five popular algorithms:
11//!
12//! * RSA
13//!
14//! * DSA
15//!
16//! * Diffie-Hellman
17//!
18//! * Elliptic Curves
19//!
20//! * HMAC
21//!
22//! These algorithms rely on hard mathematical problems - namely integer factorization,
23//! discrete logarithms, and elliptic curve relationships - that currently do not
24//! yield efficient solutions. This property ensures the security of these
25//! cryptographic algorithms.
26//!
27//! # Example
28//!
29//! Generate a 2048-bit RSA public/private key pair and print the public key.
30//!
31//! ```rust
32//! use boring::rsa::Rsa;
33//! use boring::pkey::PKey;
34//! use std::str;
35//!
36//! let rsa = Rsa::generate(2048).unwrap();
37//! let pkey = PKey::from_rsa(rsa).unwrap();
38//!
39//! let pub_key: Vec<u8> = pkey.public_key_to_pem().unwrap();
40//! println!("{:?}", str::from_utf8(pub_key.as_slice()).unwrap());
41//! ```
42
43use crate::ffi;
44use foreign_types::{ForeignType, ForeignTypeRef};
45use libc::{c_int, c_long};
46use std::ffi::CString;
47use std::fmt;
48use std::mem;
49use std::ptr;
50
51use crate::bio::MemBioSlice;
52use crate::dh::Dh;
53use crate::dsa::Dsa;
54use crate::ec::EcKey;
55use crate::error::ErrorStack;
56use crate::rsa::Rsa;
57use crate::util::{invoke_passwd_cb, CallbackState};
58use crate::{cvt, cvt_p};
59
60/// A tag type indicating that a key only has parameters.
61pub enum Params {}
62
63/// A tag type indicating that a key only has public components.
64pub enum Public {}
65
66/// A tag type indicating that a key has private components.
67pub enum Private {}
68
69/// An identifier of a kind of key.
70#[derive(Debug, Copy, Clone, PartialEq, Eq)]
71pub struct Id(c_int);
72
73impl Id {
74    pub const RSA: Id = Id(ffi::EVP_PKEY_RSA);
75    pub const DSA: Id = Id(ffi::EVP_PKEY_DSA);
76    pub const DH: Id = Id(ffi::EVP_PKEY_DH);
77    pub const EC: Id = Id(ffi::EVP_PKEY_EC);
78    pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
79    pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
80    pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
81    pub const X448: Id = Id(ffi::EVP_PKEY_X448);
82
83    /// Creates a `Id` from an integer representation.
84    pub fn from_raw(value: c_int) -> Id {
85        Id(value)
86    }
87
88    /// Returns the integer representation of the `Id`.
89    #[allow(clippy::trivially_copy_pass_by_ref)]
90    pub fn as_raw(&self) -> c_int {
91        self.0
92    }
93}
94
95/// A trait indicating that a key has parameters.
96#[allow(clippy::missing_safety_doc)]
97pub unsafe trait HasParams {}
98
99unsafe impl HasParams for Params {}
100
101unsafe impl<T> HasParams for T where T: HasPublic {}
102
103/// A trait indicating that a key has public components.
104#[allow(clippy::missing_safety_doc)]
105pub unsafe trait HasPublic {}
106
107unsafe impl HasPublic for Public {}
108
109unsafe impl<T> HasPublic for T where T: HasPrivate {}
110
111/// A trait indicating that a key has private components.
112#[allow(clippy::missing_safety_doc)]
113pub unsafe trait HasPrivate {}
114
115unsafe impl HasPrivate for Private {}
116
117generic_foreign_type_and_impl_send_sync! {
118    type CType = ffi::EVP_PKEY;
119    fn drop = ffi::EVP_PKEY_free;
120
121    /// A public or private key.
122    pub struct PKey<T>;
123    /// Reference to [`PKey`].
124    pub struct PKeyRef<T>;
125}
126
127impl<T> ToOwned for PKeyRef<T> {
128    type Owned = PKey<T>;
129
130    fn to_owned(&self) -> PKey<T> {
131        unsafe {
132            EVP_PKEY_up_ref(self.as_ptr());
133            PKey::from_ptr(self.as_ptr())
134        }
135    }
136}
137
138impl<T> PKeyRef<T> {
139    /// Returns a copy of the internal RSA key.
140    ///
141    /// This corresponds to [`EVP_PKEY_get1_RSA`].
142    ///
143    /// [`EVP_PKEY_get1_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_RSA.html
144    pub fn rsa(&self) -> Result<Rsa<T>, ErrorStack> {
145        unsafe {
146            let rsa = cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))?;
147            Ok(Rsa::from_ptr(rsa))
148        }
149    }
150
151    /// Returns a copy of the internal DSA key.
152    ///
153    /// This corresponds to [`EVP_PKEY_get1_DSA`].
154    ///
155    /// [`EVP_PKEY_get1_DSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DSA.html
156    pub fn dsa(&self) -> Result<Dsa<T>, ErrorStack> {
157        unsafe {
158            let dsa = cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))?;
159            Ok(Dsa::from_ptr(dsa))
160        }
161    }
162
163    /// Returns a copy of the internal DH key.
164    ///
165    /// This corresponds to [`EVP_PKEY_get1_DH`].
166    ///
167    /// [`EVP_PKEY_get1_DH`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DH.html
168    pub fn dh(&self) -> Result<Dh<T>, ErrorStack> {
169        unsafe {
170            let dh = cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))?;
171            Ok(Dh::from_ptr(dh))
172        }
173    }
174
175    /// Returns a copy of the internal elliptic curve key.
176    ///
177    /// This corresponds to [`EVP_PKEY_get1_EC_KEY`].
178    ///
179    /// [`EVP_PKEY_get1_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_EC_KEY.html
180    pub fn ec_key(&self) -> Result<EcKey<T>, ErrorStack> {
181        unsafe {
182            let ec_key = cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))?;
183            Ok(EcKey::from_ptr(ec_key))
184        }
185    }
186
187    /// Returns the `Id` that represents the type of this key.
188    ///
189    /// This corresponds to [`EVP_PKEY_id`].
190    ///
191    /// [`EVP_PKEY_id`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_id.html
192    pub fn id(&self) -> Id {
193        unsafe { Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) }
194    }
195
196    /// Returns the maximum size of a signature in bytes.
197    ///
198    /// This corresponds to [`EVP_PKEY_size`].
199    ///
200    /// [`EVP_PKEY_size`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_size.html
201    pub fn size(&self) -> usize {
202        unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize }
203    }
204}
205
206impl<T> PKeyRef<T>
207where
208    T: HasPublic,
209{
210    to_pem! {
211        /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
212        ///
213        /// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
214        ///
215        /// This corresponds to [`PEM_write_bio_PUBKEY`].
216        ///
217        /// [`PEM_write_bio_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_PUBKEY.html
218        public_key_to_pem,
219        ffi::PEM_write_bio_PUBKEY
220    }
221
222    to_der! {
223        /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
224        ///
225        /// This corresponds to [`i2d_PUBKEY`].
226        ///
227        /// [`i2d_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_PUBKEY.html
228        public_key_to_der,
229        ffi::i2d_PUBKEY
230    }
231
232    /// Returns the size of the key.
233    ///
234    /// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the
235    /// group order for an elliptic curve key, for example.
236    pub fn bits(&self) -> u32 {
237        unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
238    }
239
240    /// Compares the public component of this key with another.
241    pub fn public_eq<U>(&self, other: &PKeyRef<U>) -> bool
242    where
243        U: HasPublic,
244    {
245        unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
246    }
247}
248
249impl<T> PKeyRef<T>
250where
251    T: HasPrivate,
252{
253    private_key_to_pem! {
254        /// Serializes the private key to a PEM-encoded PKCS#8 PrivateKeyInfo structure.
255        ///
256        /// The output will have a header of `-----BEGIN PRIVATE KEY-----`.
257        ///
258        /// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`].
259        ///
260        /// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html
261        private_key_to_pem_pkcs8,
262        /// Serializes the private key to a PEM-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
263        ///
264        /// The output will have a header of `-----BEGIN ENCRYPTED PRIVATE KEY-----`.
265        ///
266        /// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`].
267        ///
268        /// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html
269        private_key_to_pem_pkcs8_passphrase,
270        ffi::PEM_write_bio_PKCS8PrivateKey
271    }
272
273    to_der! {
274        /// Serializes the private key to a DER-encoded key type specific format.
275        ///
276        /// This corresponds to [`i2d_PrivateKey`].
277        ///
278        /// [`i2d_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_PrivateKey.html
279        private_key_to_der,
280        ffi::i2d_PrivateKey
281    }
282
283    // This isn't actually PEM output, but `i2d_PKCS8PrivateKey_bio` is documented to be
284    // "identical to the corresponding PEM function", and it's declared in pem.h.
285    private_key_to_pem! {
286        /// Serializes the private key to a DER-encoded PKCS#8 PrivateKeyInfo structure.
287        ///
288        /// This corresponds to [`i2d_PKCS8PrivateKey_bio`].
289        ///
290        /// [`i2d_PKCS8PrivateKey_bio`]: https://www.openssl.org/docs/man1.1.1/man3/i2d_PKCS8PrivateKey_bio.html
291        private_key_to_der_pkcs8,
292        /// Serializes the private key to a DER-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
293        ///
294        /// This corresponds to [`i2d_PKCS8PrivateKey_bio`].
295        ///
296        /// [`i2d_PKCS8PrivateKey_bio`]: https://www.openssl.org/docs/man1.1.1/man3/i2d_PKCS8PrivateKey_bio.html
297        private_key_to_der_pkcs8_passphrase,
298        ffi::i2d_PKCS8PrivateKey_bio
299    }
300}
301
302impl<T> fmt::Debug for PKey<T> {
303    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
304        let alg = match self.id() {
305            Id::RSA => "RSA",
306            Id::DSA => "DSA",
307            Id::DH => "DH",
308            Id::EC => "EC",
309            Id::ED25519 => "Ed25519",
310            Id::ED448 => "Ed448",
311            _ => "unknown",
312        };
313        fmt.debug_struct("PKey").field("algorithm", &alg).finish()
314        // TODO: Print details for each specific type of key
315    }
316}
317
318impl<T> Clone for PKey<T> {
319    fn clone(&self) -> PKey<T> {
320        PKeyRef::to_owned(self)
321    }
322}
323
324impl<T> PKey<T> {
325    /// Creates a new `PKey` containing an RSA key.
326    ///
327    /// This corresponds to [`EVP_PKEY_assign_RSA`].
328    ///
329    /// [`EVP_PKEY_assign_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_RSA.html
330    pub fn from_rsa(rsa: Rsa<T>) -> Result<PKey<T>, ErrorStack> {
331        unsafe {
332            let evp = cvt_p(ffi::EVP_PKEY_new())?;
333            let pkey = PKey::from_ptr(evp);
334            cvt(ffi::EVP_PKEY_assign(
335                pkey.0,
336                ffi::EVP_PKEY_RSA,
337                rsa.as_ptr() as *mut _,
338            ))?;
339            mem::forget(rsa);
340            Ok(pkey)
341        }
342    }
343
344    /// Creates a new `PKey` containing an elliptic curve key.
345    ///
346    /// This corresponds to [`EVP_PKEY_assign_EC_KEY`].
347    ///
348    /// [`EVP_PKEY_assign_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_EC_KEY.html
349    pub fn from_ec_key(ec_key: EcKey<T>) -> Result<PKey<T>, ErrorStack> {
350        unsafe {
351            let evp = cvt_p(ffi::EVP_PKEY_new())?;
352            let pkey = PKey::from_ptr(evp);
353            cvt(ffi::EVP_PKEY_assign(
354                pkey.0,
355                ffi::EVP_PKEY_EC,
356                ec_key.as_ptr() as *mut _,
357            ))?;
358            mem::forget(ec_key);
359            Ok(pkey)
360        }
361    }
362}
363
364impl PKey<Private> {
365    private_key_from_pem! {
366        /// Deserializes a private key from a PEM-encoded key type specific format.
367        ///
368        /// This corresponds to [`PEM_read_bio_PrivateKey`].
369        ///
370        /// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
371        private_key_from_pem,
372
373        /// Deserializes a private key from a PEM-encoded encrypted key type specific format.
374        ///
375        /// This corresponds to [`PEM_read_bio_PrivateKey`].
376        ///
377        /// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
378        private_key_from_pem_passphrase,
379
380        /// Deserializes a private key from a PEM-encoded encrypted key type specific format.
381        ///
382        /// The callback should fill the password into the provided buffer and return its length.
383        ///
384        /// This corresponds to [`PEM_read_bio_PrivateKey`].
385        ///
386        /// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
387        private_key_from_pem_callback,
388        PKey<Private>,
389        ffi::PEM_read_bio_PrivateKey
390    }
391
392    from_der! {
393        /// Decodes a DER-encoded private key.
394        ///
395        /// This function will automatically attempt to detect the underlying key format, and
396        /// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific
397        /// formats.
398        ///
399        /// This corresponds to [`d2i_AutoPrivateKey`].
400        ///
401        /// [`d2i_AutoPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_AutoPrivateKey.html
402        private_key_from_der,
403        PKey<Private>,
404        ffi::d2i_AutoPrivateKey,
405        ::libc::c_long
406    }
407
408    /// Deserializes a DER-formatted PKCS#8 unencrypted private key.
409    ///
410    /// This method is mainly for interoperability reasons. Encrypted keyfiles should be preferred.
411    pub fn private_key_from_pkcs8(der: &[u8]) -> Result<PKey<Private>, ErrorStack> {
412        unsafe {
413            ffi::init();
414            let len = der.len().min(c_long::MAX as usize) as c_long;
415            let p8inf = cvt_p(ffi::d2i_PKCS8_PRIV_KEY_INFO(
416                ptr::null_mut(),
417                &mut der.as_ptr(),
418                len,
419            ))?;
420            let res = cvt_p(ffi::EVP_PKCS82PKEY(p8inf)).map(|p| PKey::from_ptr(p));
421            ffi::PKCS8_PRIV_KEY_INFO_free(p8inf);
422            res
423        }
424    }
425
426    /// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password
427    /// if the key is encrypted.
428    ///
429    /// The callback should copy the password into the provided buffer and return the number of
430    /// bytes written.
431    pub fn private_key_from_pkcs8_callback<F>(
432        der: &[u8],
433        callback: F,
434    ) -> Result<PKey<Private>, ErrorStack>
435    where
436        F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>,
437    {
438        unsafe {
439            ffi::init();
440            let mut cb = CallbackState::new(callback);
441            let bio = MemBioSlice::new(der)?;
442            cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
443                bio.as_ptr(),
444                ptr::null_mut(),
445                Some(invoke_passwd_cb::<F>),
446                &mut cb as *mut _ as *mut _,
447            ))
448            .map(|p| PKey::from_ptr(p))
449        }
450    }
451
452    /// Deserializes a DER-formatted PKCS#8 private key, using the supplied password if the key is
453    /// encrypted.
454    ///
455    /// # Panics
456    ///
457    /// Panics if `passphrase` contains an embedded null.
458    pub fn private_key_from_pkcs8_passphrase(
459        der: &[u8],
460        passphrase: &[u8],
461    ) -> Result<PKey<Private>, ErrorStack> {
462        unsafe {
463            ffi::init();
464            let bio = MemBioSlice::new(der)?;
465            let passphrase = CString::new(passphrase).unwrap();
466            cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
467                bio.as_ptr(),
468                ptr::null_mut(),
469                None,
470                passphrase.as_ptr() as *const _ as *mut _,
471            ))
472            .map(|p| PKey::from_ptr(p))
473        }
474    }
475}
476
477impl PKey<Public> {
478    from_pem! {
479        /// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
480        ///
481        /// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
482        ///
483        /// This corresponds to [`PEM_read_bio_PUBKEY`].
484        ///
485        /// [`PEM_read_bio_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PUBKEY.html
486        public_key_from_pem,
487        PKey<Public>,
488        ffi::PEM_read_bio_PUBKEY
489    }
490
491    from_der! {
492        /// Decodes a DER-encoded SubjectPublicKeyInfo structure.
493        ///
494        /// This corresponds to [`d2i_PUBKEY`].
495        ///
496        /// [`d2i_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_PUBKEY.html
497        public_key_from_der,
498        PKey<Public>,
499        ffi::d2i_PUBKEY,
500        ::libc::c_long
501    }
502}
503
504use crate::ffi::EVP_PKEY_up_ref;
505
506#[cfg(test)]
507mod tests {
508    use crate::ec::EcKey;
509    use crate::nid::Nid;
510    use crate::rsa::Rsa;
511    use crate::symm::Cipher;
512
513    use super::*;
514
515    #[test]
516    fn test_to_password() {
517        let rsa = Rsa::generate(2048).unwrap();
518        let pkey = PKey::from_rsa(rsa).unwrap();
519        let pem = pkey
520            .private_key_to_pem_pkcs8_passphrase(Cipher::aes_128_cbc(), b"foobar")
521            .unwrap();
522        PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap();
523        assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err());
524    }
525
526    #[test]
527    fn test_unencrypted_pkcs8() {
528        let key = include_bytes!("../test/pkcs8-nocrypt.der");
529        PKey::private_key_from_pkcs8(key).unwrap();
530    }
531
532    #[test]
533    fn test_encrypted_pkcs8_passphrase() {
534        let key = include_bytes!("../test/pkcs8.der");
535        PKey::private_key_from_pkcs8_passphrase(key, b"mypass").unwrap();
536    }
537
538    #[test]
539    fn test_encrypted_pkcs8_callback() {
540        let mut password_queried = false;
541        let key = include_bytes!("../test/pkcs8.der");
542        PKey::private_key_from_pkcs8_callback(key, |password| {
543            password_queried = true;
544            password[..6].copy_from_slice(b"mypass");
545            Ok(6)
546        })
547        .unwrap();
548        assert!(password_queried);
549    }
550
551    #[test]
552    fn test_private_key_from_pem() {
553        let key = include_bytes!("../test/key.pem");
554        PKey::private_key_from_pem(key).unwrap();
555    }
556
557    #[test]
558    fn test_public_key_from_pem() {
559        let key = include_bytes!("../test/key.pem.pub");
560        PKey::public_key_from_pem(key).unwrap();
561    }
562
563    #[test]
564    fn test_public_key_from_der() {
565        let key = include_bytes!("../test/key.der.pub");
566        PKey::public_key_from_der(key).unwrap();
567    }
568
569    #[test]
570    fn test_private_key_from_der() {
571        let key = include_bytes!("../test/key.der");
572        PKey::private_key_from_der(key).unwrap();
573    }
574
575    #[test]
576    fn test_pem() {
577        let key = include_bytes!("../test/key.pem");
578        let key = PKey::private_key_from_pem(key).unwrap();
579
580        let priv_key = key.private_key_to_pem_pkcs8().unwrap();
581        let pub_key = key.public_key_to_pem().unwrap();
582
583        // As a super-simple verification, just check that the buffers contain
584        // the `PRIVATE KEY` or `PUBLIC KEY` strings.
585        assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY"));
586        assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY"));
587    }
588
589    #[test]
590    fn test_der_pkcs8() {
591        let key = include_bytes!("../test/key.der");
592        let key = PKey::private_key_from_der(key).unwrap();
593
594        let priv_key = key.private_key_to_der_pkcs8().unwrap();
595
596        // Check that this has the correct PKCS#8 version number and algorithm.
597        assert_eq!(hex::encode(&priv_key[4..=6]), "020100"); // Version 0
598        assert_eq!(hex::encode(&priv_key[9..=19]), "06092a864886f70d010101"); // Algorithm RSA/PKCS#1
599    }
600
601    #[test]
602    fn test_rsa_accessor() {
603        let rsa = Rsa::generate(2048).unwrap();
604        let pkey = PKey::from_rsa(rsa).unwrap();
605        pkey.rsa().unwrap();
606        assert_eq!(pkey.id(), Id::RSA);
607        assert!(pkey.dsa().is_err());
608    }
609
610    #[test]
611    fn test_ec_key_accessor() {
612        let ec_key = EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
613        let pkey = PKey::from_ec_key(ec_key).unwrap();
614        pkey.ec_key().unwrap();
615        assert_eq!(pkey.id(), Id::EC);
616        assert!(pkey.rsa().is_err());
617    }
618}