diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index e69ee8e6e79a79afcfb36704d8365dd71980d96e..6ec22258303cb73b3b4f5861bc0e539a3fc07972 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -6642,6 +6642,7 @@ name = "sp-session"
 version = "2.0.0"
 dependencies = [
  "sp-api 2.0.0",
+ "sp-core 2.0.0",
  "sp-runtime 2.0.0",
  "sp-std 2.0.0",
 ]
diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs
index 4416cbf13ee004feb7a03df8a781748e4e470b40..1276f20d1709c4f909d1147d1edd4f2e89c86bc4 100644
--- a/substrate/bin/node-template/runtime/src/lib.rs
+++ b/substrate/bin/node-template/runtime/src/lib.rs
@@ -360,6 +360,12 @@ impl_runtime_apis! {
 		fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
 			opaque::SessionKeys::generate(seed)
 		}
+
+		fn decode_session_keys(
+			encoded: Vec<u8>,
+		) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
+			opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
+		}
 	}
 
 	impl fg_primitives::GrandpaApi<Block> for Runtime {
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index ecc16ee143fa3c3c811649bb4545a6ffca099d1a..23cd7405dda6055a38d0f5d6581a22f755350ce7 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -798,6 +798,12 @@ impl_runtime_apis! {
 		fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
 			SessionKeys::generate(seed)
 		}
+
+		fn decode_session_keys(
+			encoded: Vec<u8>,
+		) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
+			SessionKeys::decode_into_raw_public_keys(&encoded)
+		}
 	}
 }
 
diff --git a/substrate/client/keystore/src/lib.rs b/substrate/client/keystore/src/lib.rs
index f8794aa05e8f679d2e2462b04ac5366405736675..ef81c40c10f51f91a6aea3f17797e38ef29141ae 100644
--- a/substrate/client/keystore/src/lib.rs
+++ b/substrate/client/keystore/src/lib.rs
@@ -72,7 +72,8 @@ impl std::error::Error for Error {
 /// Every pair that is being generated by a `seed`, will be placed in memory.
 pub struct Store {
 	path: Option<PathBuf>,
-	additional: HashMap<(KeyTypeId, Vec<u8>), Vec<u8>>,
+	/// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed`
+	additional: HashMap<(KeyTypeId, Vec<u8>), String>,
 	password: Option<Protected<String>>,
 }
 
@@ -97,25 +98,22 @@ impl Store {
 		}))
 	}
 
-	/// Get the public/private key pair for the given public key and key type.
-	fn get_additional_pair<Pair: PairT>(
+	/// Get the key phrase for the given public key and key type from the in-memory store.
+	fn get_additional_pair(
 		&self,
-		public: &Pair::Public,
+		public: &[u8],
 		key_type: KeyTypeId,
-	) -> Result<Option<Pair>> {
-		let key = (key_type, public.to_raw_vec());
-		self.additional
-			.get(&key)
-			.map(|bytes| Pair::from_seed_slice(bytes).map_err(|_| Error::InvalidSeed))
-			.transpose()
+	) -> Option<&String> {
+		let key = (key_type, public.to_vec());
+		self.additional.get(&key)
 	}
 
 	/// Insert the given public/private key pair with the given key type.
 	///
 	/// Does not place it into the file system store.
-	fn insert_ephemeral_pair<Pair: PairT>(&mut self, pair: &Pair, key_type: KeyTypeId) {
+	fn insert_ephemeral_pair<Pair: PairT>(&mut self, pair: &Pair, seed: &str, key_type: KeyTypeId) {
 		let key = (key_type, pair.public().to_raw_vec());
-		self.additional.insert(key, pair.to_raw_vec());
+		self.additional.insert(key, seed.into());
 	}
 
 	/// Insert a new key with anonymous crypto.
@@ -179,7 +177,7 @@ impl Store {
 		key_type: KeyTypeId,
 	) -> Result<Pair> {
 		let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?;
-		self.insert_ephemeral_pair(&pair, key_type);
+		self.insert_ephemeral_pair(&pair, seed, key_type);
 		Ok(pair)
 	}
 
@@ -190,20 +188,24 @@ impl Store {
 		self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID).map(Into::into)
 	}
 
+	/// Get the key phrase for a given public key and key type.
+	fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<String> {
+		if let Some(phrase) = self.get_additional_pair(public, key_type) {
+			return Ok(phrase.clone())
+		}
+
+		let path = self.key_file_path(public, key_type).ok_or_else(|| Error::Unavailable)?;
+		let file = File::open(path)?;
+
+		serde_json::from_reader(&file).map_err(Into::into)
+	}
+
 	/// Get a key pair for the given public key and key type.
 	pub fn key_pair_by_type<Pair: PairT>(&self,
 		public: &Pair::Public,
 		key_type: KeyTypeId,
 	) -> Result<Pair> {
-		if let Some(pair) = self.get_additional_pair(public, key_type)? {
-			return Ok(pair)
-		}
-
-		let path = self.key_file_path(public.as_slice(), key_type)
-			.ok_or_else(|| Error::Unavailable)?;
-		let file = File::open(path)?;
-
-		let phrase: String = serde_json::from_reader(&file)?;
+		let phrase = self.key_phrase_by_type(public.as_slice(), key_type)?;
 		let pair = Pair::from_string(
 			&phrase,
 			self.password.as_ref().map(|p| &***p),
@@ -328,6 +330,10 @@ impl BareCryptoStore for Store {
 	fn password(&self) -> Option<&str> {
 		self.password.as_ref().map(|x| x.as_str())
 	}
+
+	fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
+		public_keys.iter().all(|(p, t)| self.key_phrase_by_type(&p, *t).is_ok())
+	}
 }
 
 #[cfg(test)]
diff --git a/substrate/client/rpc-api/src/author/error.rs b/substrate/client/rpc-api/src/author/error.rs
index 8ebd06f8de430a5f908fc6337f1237d0e7bad5f9..f1b56910086b5cb671689d612d9d2062d4722141 100644
--- a/substrate/client/rpc-api/src/author/error.rs
+++ b/substrate/client/rpc-api/src/author/error.rs
@@ -54,6 +54,9 @@ pub enum Error {
 	/// Some random issue with the key store. Shouldn't happen.
 	#[display(fmt="The key store is unavailable")]
 	KeyStoreUnavailable,
+	/// Invalid session keys encoding.
+	#[display(fmt="Session keys are not encoded correctly")]
+	InvalidSessionKeys,
 }
 
 impl std::error::Error for Error {
diff --git a/substrate/client/rpc-api/src/author/mod.rs b/substrate/client/rpc-api/src/author/mod.rs
index cbdbd38154490b88324826f46b429c5988ee29b7..4fbf0c73a366696109b4efc4043f7171479991c9 100644
--- a/substrate/client/rpc-api/src/author/mod.rs
+++ b/substrate/client/rpc-api/src/author/mod.rs
@@ -39,7 +39,8 @@ pub trait AuthorApi<Hash, BlockHash> {
 
 	/// Insert a key into the keystore.
 	#[rpc(name = "author_insertKey")]
-	fn insert_key(&self,
+	fn insert_key(
+		&self,
 		key_type: String,
 		suri: String,
 		public: Bytes,
@@ -49,6 +50,20 @@ pub trait AuthorApi<Hash, BlockHash> {
 	#[rpc(name = "author_rotateKeys")]
 	fn rotate_keys(&self) -> Result<Bytes>;
 
+	/// Checks if the keystore has private keys for the given session public keys.
+	///
+	/// `session_keys` is the SCALE encoded session keys object from the runtime.
+	///
+	/// Returns `true` iff all private keys could be found.
+	#[rpc(name = "author_hasSessionKeys")]
+	fn has_session_keys(&self, session_keys: Bytes) -> Result<bool>;
+
+	/// Checks if the keystore has private keys for the given public key and key type.
+	///
+	/// Returns `true` if a private key could be found.
+	#[rpc(name = "author_hasKey")]
+	fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool>;
+
 	/// Returns all pending extrinsics, potentially grouped by sender.
 	#[rpc(name = "author_pendingExtrinsics")]
 	fn pending_extrinsics(&self) -> Result<Vec<Bytes>>;
diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs
index 9891abc47d03049de791f0b656a1e0f17ba89a3f..06bdcf883c6891548717cd4d44824e0816ba0fd9 100644
--- a/substrate/client/rpc/src/author/mod.rs
+++ b/substrate/client/rpc/src/author/mod.rs
@@ -112,6 +112,22 @@ where
 		).map(Into::into).map_err(|e| Error::Client(Box::new(e)))
 	}
 
+	fn has_session_keys(&self, session_keys: Bytes) -> Result<bool> {
+		let best_block_hash = self.client.chain_info().best_hash;
+		let keys = self.client.runtime_api().decode_session_keys(
+			&generic::BlockId::Hash(best_block_hash),
+			session_keys.to_vec(),
+		).map_err(|e| Error::Client(Box::new(e)))?
+			.ok_or_else(|| Error::InvalidSessionKeys)?;
+
+		Ok(self.keystore.read().has_keys(&keys))
+	}
+
+	fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool> {
+		let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
+		Ok(self.keystore.read().has_keys(&[(public_key.to_vec(), key_type)]))
+	}
+
 	fn submit_extrinsic(&self, ext: Bytes) -> FutureResult<TxHash<P>> {
 		let xt = match Decode::decode(&mut &ext[..]) {
 			Ok(xt) => xt,
diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs
index 9fe51d62f51157f2f665b2376d0059c3ec39e100..7de109bc5d62c0ac49b5742091ef97fb39b1d7dc 100644
--- a/substrate/client/rpc/src/author/tests.rs
+++ b/substrate/client/rpc/src/author/tests.rs
@@ -16,12 +16,12 @@
 
 use super::*;
 
-use std::sync::Arc;
+use std::{mem, sync::Arc};
 use assert_matches::assert_matches;
 use codec::Encode;
 use sp_core::{
-	H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore}, traits::BareCryptoStorePtr, ed25519,
-	crypto::Pair,
+	H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore},
+	traits::BareCryptoStorePtr, ed25519, crypto::{Pair, Public},
 };
 use rpc::futures::Stream as _;
 use substrate_test_runtime_client::{
@@ -237,3 +237,59 @@ fn should_rotate_keys() {
 	assert_eq!(session_keys.ed25519, ed25519_key_pair.public().into());
 	assert_eq!(session_keys.sr25519, sr25519_key_pair.public().into());
 }
+
+#[test]
+fn test_has_session_keys() {
+	let setup = TestSetup::default();
+	let p = setup.author();
+
+	let non_existent_public_keys = TestSetup::default()
+		.author()
+		.rotate_keys()
+		.expect("Rotates the keys");
+
+	let public_keys = p.rotate_keys().expect("Rotates the keys");
+	let test_vectors = vec![
+		(public_keys, Ok(true)),
+		(vec![1, 2, 3].into(), Err(Error::InvalidSessionKeys)),
+		(non_existent_public_keys, Ok(false)),
+	];
+
+	for (keys, result) in test_vectors {
+		assert_eq!(
+			result.map_err(|e| mem::discriminant(&e)),
+			p.has_session_keys(keys).map_err(|e| mem::discriminant(&e)),
+		);
+	}
+}
+
+#[test]
+fn test_has_key() {
+	let setup = TestSetup::default();
+	let p = setup.author();
+
+	let suri = "//Alice";
+	let alice_key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
+	p.insert_key(
+		String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
+		suri.to_string(),
+		alice_key_pair.public().0.to_vec().into(),
+	).expect("Insert key");
+	let bob_key_pair = ed25519::Pair::from_string("//Bob", None).expect("Generates keypair");
+
+	let test_vectors = vec![
+		(alice_key_pair.public().to_raw_vec().into(), ED25519, Ok(true)),
+		(alice_key_pair.public().to_raw_vec().into(), SR25519, Ok(false)),
+		(bob_key_pair.public().to_raw_vec().into(), ED25519, Ok(false)),
+	];
+
+	for (key, key_type, result) in test_vectors {
+		assert_eq!(
+			result.map_err(|e| mem::discriminant(&e)),
+			p.has_key(
+				key,
+				String::from_utf8(key_type.0.to_vec()).expect("Keytype is a valid string"),
+			).map_err(|e| mem::discriminant(&e)),
+		);
+	}
+}
diff --git a/substrate/primitives/application-crypto/src/ed25519.rs b/substrate/primitives/application-crypto/src/ed25519.rs
index c92c70495e6cb56a5332ceba1161e7e3ef2f932d..414715a10684c18b49a33fe6bb1fc08c20639d4a 100644
--- a/substrate/primitives/application-crypto/src/ed25519.rs
+++ b/substrate/primitives/application-crypto/src/ed25519.rs
@@ -53,4 +53,8 @@ impl RuntimePublic for Public {
 	fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
 		sp_io::crypto::ed25519_verify(&signature, msg.as_ref(), self)
 	}
+
+	fn to_raw_vec(&self) -> Vec<u8> {
+		sp_core::crypto::Public::to_raw_vec(self)
+	}
 }
diff --git a/substrate/primitives/application-crypto/src/lib.rs b/substrate/primitives/application-crypto/src/lib.rs
index db1bdf0c7a523522ce43efbd244ca293f9dc80af..fed4a6bf8e92ef65ad3818053e406261f8c236fb 100644
--- a/substrate/primitives/application-crypto/src/lib.rs
+++ b/substrate/primitives/application-crypto/src/lib.rs
@@ -301,6 +301,10 @@ macro_rules! app_crypto_public_common {
 			fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
 				<$public as $crate::RuntimePublic>::verify(self.as_ref(), msg, &signature.as_ref())
 			}
+
+			fn to_raw_vec(&self) -> $crate::Vec<u8> {
+				<$public as $crate::RuntimePublic>::to_raw_vec(&self.0)
+			}
 		}
 	}
 }
diff --git a/substrate/primitives/application-crypto/src/sr25519.rs b/substrate/primitives/application-crypto/src/sr25519.rs
index 34d9797bd4ea8feb15ca5fc5bbc972b7f21fcb34..59c6f19b6f2668939054127347f5a87ed6989907 100644
--- a/substrate/primitives/application-crypto/src/sr25519.rs
+++ b/substrate/primitives/application-crypto/src/sr25519.rs
@@ -53,4 +53,8 @@ impl RuntimePublic for Public {
 	fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
 		sp_io::crypto::sr25519_verify(&signature, msg.as_ref(), self)
 	}
+
+	fn to_raw_vec(&self) -> Vec<u8> {
+		sp_core::crypto::Public::to_raw_vec(self)
+	}
 }
diff --git a/substrate/primitives/application-crypto/src/traits.rs b/substrate/primitives/application-crypto/src/traits.rs
index 4b500bb2a63c9adfe4e45790b0577f9a5b01c084..2af039a88df4c3734fbce447072ebcd051ebcc41 100644
--- a/substrate/primitives/application-crypto/src/traits.rs
+++ b/substrate/primitives/application-crypto/src/traits.rs
@@ -106,10 +106,13 @@ pub trait RuntimePublic: Sized {
 
 	/// Verify that the given signature matches the given message using this public key.
 	fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
+
+	/// Returns `Self` as raw vec.
+	fn to_raw_vec(&self) -> Vec<u8>;
 }
 
 /// A runtime interface for an application's public key.
-pub trait RuntimeAppPublic: Sized  {
+pub trait RuntimeAppPublic: Sized {
 	/// An identifier for this application-specific key type.
 	const ID: KeyTypeId;
 
@@ -136,6 +139,9 @@ pub trait RuntimeAppPublic: Sized  {
 
 	/// Verify that the given signature matches the given message using this public key.
 	fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
+
+	/// Returns `Self` as raw vec.
+	fn to_raw_vec(&self) -> Vec<u8>;
 }
 
 /// Something that bound to a fixed `RuntimeAppPublic`.
diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs
index ce2b75c37535d8fdd64513b677a5222d6de64ad7..3cc6c50f76b1c20e765df9947d2b8331226423c0 100644
--- a/substrate/primitives/core/src/crypto.rs
+++ b/substrate/primitives/core/src/crypto.rs
@@ -19,7 +19,6 @@
 // end::description[]
 
 use sp_std::hash::Hash;
-#[cfg(feature = "full_crypto")]
 use sp_std::vec::Vec;
 #[cfg(feature = "std")]
 use sp_std::convert::TryInto;
@@ -520,8 +519,7 @@ pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + Pa
 	fn from_slice(data: &[u8]) -> Self;
 
 	/// Return a `Vec<u8>` filled with raw data.
-	#[cfg(feature = "std")]
-	fn to_raw_vec(&self) -> Vec<u8> { self.as_slice().to_owned() }
+	fn to_raw_vec(&self) -> Vec<u8> { self.as_slice().to_vec() }
 
 	/// Return a slice filled with raw data.
 	fn as_slice(&self) -> &[u8] { self.as_ref() }
diff --git a/substrate/primitives/core/src/testing.rs b/substrate/primitives/core/src/testing.rs
index 35286acb63f310d2bc7bf8e560c17e84903cc171..0def3454bc8991ef37f6694a40b738a99dfc076b 100644
--- a/substrate/primitives/core/src/testing.rs
+++ b/substrate/primitives/core/src/testing.rs
@@ -127,6 +127,10 @@ impl crate::traits::BareCryptoStore for KeyStore {
 	fn password(&self) -> Option<&str> {
 		None
 	}
+
+	fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
+		public_keys.iter().all(|(k, t)| self.keys.get(&t).and_then(|s| s.get(k)).is_some())
+	}
 }
 
 /// Macro for exporting functions from wasm in with the expected signature for using it with the
diff --git a/substrate/primitives/core/src/traits.rs b/substrate/primitives/core/src/traits.rs
index 020b5e2dc54612604b870773ace9a16aeafb0082..bd02d39fb55a50e400fdd50a3666b9a1a7c28cec 100644
--- a/substrate/primitives/core/src/traits.rs
+++ b/substrate/primitives/core/src/traits.rs
@@ -69,6 +69,11 @@ pub trait BareCryptoStore: Send + Sync {
 
 	/// Get the password for this store.
 	fn password(&self) -> Option<&str>;
+
+	/// Checks if the private keys for the given public key and key type combinations exist.
+	///
+	/// Returns `true` iff all private keys could be found.
+	fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool;
 }
 
 /// A pointer to the key store.
diff --git a/substrate/primitives/offchain/src/lib.rs b/substrate/primitives/offchain/src/lib.rs
index 1dd20db0dd846d4b33ca18e6bd83ad8223b6e8c4..ae02fed4967685e7cb419d49064a63634497a749 100644
--- a/substrate/primitives/offchain/src/lib.rs
+++ b/substrate/primitives/offchain/src/lib.rs
@@ -19,8 +19,6 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 #![warn(missing_docs)]
 
-use sp_runtime::traits::NumberFor;
-
 /// Local Storage Prefix used by the Offchain Worker API to
 pub const STORAGE_PREFIX: &[u8] = b"storage";
 
@@ -31,7 +29,7 @@ sp_api::decl_runtime_apis! {
 		/// Starts the off-chain task for given block number.
 		#[skip_initialize_block]
 		#[changed_in(2)]
-		fn offchain_worker(number: NumberFor<Block>);
+		fn offchain_worker(number: sp_runtime::traits::NumberFor<Block>);
 
 		/// Starts the off-chain task for given block header.
 		#[skip_initialize_block]
diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs
index 516d58112647db643a3e0c88e7ff50b4f9fa9a10..c86638b57b310ab436742a77cf1ff1272728b21e 100644
--- a/substrate/primitives/runtime/src/testing.rs
+++ b/substrate/primitives/runtime/src/testing.rs
@@ -115,6 +115,10 @@ impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId {
 
 		u64::from_le_bytes(msg_signature) == *signature
 	}
+
+	fn to_raw_vec(&self) -> Vec<u8> {
+		AsRef::<[u8]>::as_ref(self).to_vec()
+	}
 }
 
 impl OpaqueKeys for UintAuthorityId {
diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs
index 08bbd83ca54e24442d288cee336406abc3a2d9a9..7d889cae9b2fa44b159f36ba459262d21cfda405 100644
--- a/substrate/primitives/runtime/src/traits.rs
+++ b/substrate/primitives/runtime/src/traits.rs
@@ -1106,12 +1106,14 @@ macro_rules! count {
 #[macro_export]
 macro_rules! impl_opaque_keys {
 	(
+		$( #[ $attr:meta ] )*
 		pub struct $name:ident {
 			$(
 				pub $field:ident: $type:ty,
 			)*
 		}
 	) => {
+		$( #[ $attr ] )*
 		#[derive(
 			Default, Clone, PartialEq, Eq,
 			$crate::codec::Encode,
@@ -1143,6 +1145,37 @@ macro_rules! impl_opaque_keys {
 				};
 				$crate::codec::Encode::encode(&keys)
 			}
+
+			/// Converts `Self` into a `Vec` of `(raw public key, KeyTypeId)`.
+			pub fn into_raw_public_keys(
+				self,
+			) -> $crate::sp_std::vec::Vec<($crate::sp_std::vec::Vec<u8>, $crate::KeyTypeId)> {
+				let mut keys = Vec::new();
+				$(
+					keys.push((
+						$crate::RuntimeAppPublic::to_raw_vec(&self.$field),
+						<
+							<
+								$type as $crate::BoundToRuntimeAppPublic
+							>::Public as $crate::RuntimeAppPublic
+						>::ID,
+					));
+				)*
+
+				keys
+			}
+
+			/// Decode `Self` from the given `encoded` slice and convert `Self` into the raw public
+			/// keys (see [`Self::into_raw_public_keys`]).
+			///
+			/// Returns `None` when the decoding failed, otherwise `Some(_)`.
+			pub fn decode_into_raw_public_keys(
+				encoded: &[u8],
+			) -> Option<$crate::sp_std::vec::Vec<($crate::sp_std::vec::Vec<u8>, $crate::KeyTypeId)>> {
+				<Self as $crate::codec::Decode>::decode(&mut &encoded[..])
+					.ok()
+					.map(|s| s.into_raw_public_keys())
+			}
 		}
 
 		impl $crate::traits::OpaqueKeys for $name {
diff --git a/substrate/primitives/session/Cargo.toml b/substrate/primitives/session/Cargo.toml
index b7c72e0c681fec52e161a92048cbb8a2a966ae15..24475e94141632487ba7852823ce12bf7b4c4391 100644
--- a/substrate/primitives/session/Cargo.toml
+++ b/substrate/primitives/session/Cargo.toml
@@ -7,8 +7,9 @@ edition = "2018"
 [dependencies]
 sp-api = { version = "2.0.0", default-features = false, path = "../api" }
 sp-std = { version = "2.0.0", default-features = false, path = "../std" }
+sp-core = { version = "2.0.0", default-features = false, path = "../core" }
 sp-runtime = { version = "2.0.0", optional = true, path = "../runtime" }
 
 [features]
 default = [ "std" ]
-std = [ "sp-api/std", "sp-std/std", "sp-runtime" ]
+std = [ "sp-api/std", "sp-std/std", "sp-runtime", "sp-core/std" ]
diff --git a/substrate/primitives/session/src/lib.rs b/substrate/primitives/session/src/lib.rs
index c61218ca6bacd1afb2c9714a650a94c9e9d05b72..8e2a68d0509832e855226e904f9e872f343d17d4 100644
--- a/substrate/primitives/session/src/lib.rs
+++ b/substrate/primitives/session/src/lib.rs
@@ -25,6 +25,8 @@ use sp_runtime::{generic::BlockId, traits::Block as BlockT};
 #[cfg(feature = "std")]
 use sp_api::ProvideRuntimeApi;
 
+use sp_core::crypto::KeyTypeId;
+
 sp_api::decl_runtime_apis! {
 	/// Session keys runtime api.
 	pub trait SessionKeys {
@@ -36,6 +38,11 @@ sp_api::decl_runtime_apis! {
 		///
 		/// Returns the concatenated SCALE encoded public keys.
 		fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8>;
+
+		/// Decode the given public session keys.
+		///
+		/// Returns the list of public raw public keys + key type.
+		fn decode_session_keys(encoded: Vec<u8>) -> Option<Vec<(Vec<u8>, KeyTypeId)>>;
 	}
 }
 
diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs
index e68ed5f6fc17e919bdd6040ad0e3a5863d99dc21..b8e858c3a4c128adc36397294b75f2ac0eb5ff45 100644
--- a/substrate/test-utils/runtime/src/lib.rs
+++ b/substrate/test-utils/runtime/src/lib.rs
@@ -631,6 +631,12 @@ cfg_if! {
 				fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
 					SessionKeys::generate(None)
 				}
+
+				fn decode_session_keys(
+					encoded: Vec<u8>,
+				) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
+					SessionKeys::decode_into_raw_public_keys(&encoded)
+				}
 			}
 
 			impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
@@ -847,6 +853,12 @@ cfg_if! {
 				fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
 					SessionKeys::generate(None)
 				}
+
+				fn decode_session_keys(
+					encoded: Vec<u8>,
+				) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
+					SessionKeys::decode_into_raw_public_keys(&encoded)
+				}
 			}
 
 			impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {