Commit a080bfe

mo <mo.khan@gmail.com>
2017-12-17 19:18:10
support decryption with multiple encryption keypairs.
1 parent 91893e9
Changed files (2)
lib/saml/kit/xml_decryption.rb
@@ -1,10 +1,10 @@
 module Saml
   module Kit
     class XmlDecryption
-      attr_reader :private_key
+      attr_reader :private_keys
 
       def initialize(configuration: Saml::Kit.configuration)
-        @private_key = configuration.private_keys(use: :encryption).last
+        @private_keys = configuration.private_keys(use: :encryption)
       end
 
       def decrypt(data)
@@ -19,7 +19,16 @@ module Saml
       def symmetric_key_from(encrypted_data)
         encrypted_key = encrypted_data['KeyInfo']['EncryptedKey']
         cipher_text = Base64.decode64(encrypted_key['CipherData']['CipherValue'])
-        to_plaintext(cipher_text, private_key, encrypted_key["EncryptionMethod"]['Algorithm'])
+        attempts = private_keys.count
+        private_keys.each do |private_key|
+          begin
+            attempts -= 1
+            return to_plaintext(cipher_text, private_key, encrypted_key["EncryptionMethod"]['Algorithm'])
+          rescue OpenSSL::PKey::RSAError => error
+            Saml::Kit.logger.error(error)
+            raise if attempts.zero?
+          end
+        end
       end
 
       def to_plaintext(cipher_text, symmetric_key, algorithm)
spec/saml/xml_decryption_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Saml::Kit::XmlDecryption do
       expect(decrypted.strip).to eql(secret)
     end
 
-    it 'raise an error when it cannot decrypt the data' do
+    it 'attemps to decrypt with each encryption keypair' do
       certificate_pem, private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create
       public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
       private_key = OpenSSL::PKey::RSA.new(private_key_pem, password)
@@ -81,6 +81,49 @@ RSpec.describe Saml::Kit::XmlDecryption do
         }
       }
 
+      _, other_private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create
+      other_private_key = OpenSSL::PKey::RSA.new(other_private_key_pem, password)
+
+      subject = described_class.new(configuration: double(private_keys: [other_private_key, private_key]))
+      decrypted = subject.decrypt(data)
+      expect(decrypted.strip).to eql(secret)
+    end
+
+    it 'raise an error when it cannot decrypt the data' do
+      certificate_pem, _ = Saml::Kit::SelfSignedCertificate.new(password).create
+      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
+
+      cipher = OpenSSL::Cipher.new('AES-128-CBC')
+      cipher.encrypt
+      key = cipher.random_key
+      iv = cipher.random_iv
+      encrypted = cipher.update(secret) + cipher.final
+
+      data = {
+        "EncryptedData"=> {
+          "xmlns:xenc"=>"http://www.w3.org/2001/04/xmlenc#",
+          "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
+          "Type"=>"http://www.w3.org/2001/04/xmlenc#Element",
+          "EncryptionMethod"=> {
+            "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#aes128-cbc"
+          },
+          "KeyInfo"=> {
+            "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
+            "EncryptedKey"=> {
+              "EncryptionMethod"=>{
+                "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+              },
+              "CipherData"=>{
+                "CipherValue"=> Base64.encode64(public_key.public_encrypt(key))
+              }
+            }
+          },
+          "CipherData"=>{
+            "CipherValue"=> Base64.encode64(iv + encrypted)
+          }
+        }
+      }
+
       new_private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create[1]
       new_private_key = OpenSSL::PKey::RSA.new(new_private_key_pem, password)
       subject = described_class.new(configuration: double(private_keys: [new_private_key]))