From b0ea02e65a01cf62004de0851284da7a8868d406 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Sun, 16 Jun 2013 14:37:48 -0700 Subject: [PATCH] Allow DSA public keys. DSA can't be used for encryption with the bouncycastle library, so make sure this situation is properly handled. --- .../src/com/cloud/vm/UserVmManagerImpl.java | 63 +++++++---------- .../com/cloud/utils/ssh/SSHKeysHelper.java | 2 +- .../com/cloud/utils/crypto/RSAHelperTest.java | 50 ++++++++++++++ .../cloud/utils/ssh/SSHKeysHelperTest.java | 69 +++++++++++++++++++ 4 files changed, 143 insertions(+), 41 deletions(-) create mode 100644 utils/test/com/cloud/utils/crypto/RSAHelperTest.java create mode 100644 utils/test/com/cloud/utils/ssh/SSHKeysHelperTest.java diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 1c8ab75f183..44a7d0668b2 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -509,18 +509,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // update the password in vm_details table too // Check if an SSH key pair was selected for the instance and if so // use it to encrypt & save the vm password - String sshPublicKey = userVm.getDetail("SSH.PublicKey"); - if (sshPublicKey != null && !sshPublicKey.equals("") - && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey( - sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); - } - - userVm.setDetail("Encrypted.Password", encryptedPasswd); - _vmDao.saveDetails(userVm); - } + encryptAndStorePassword(userVm, password); } else { throw new CloudRuntimeException( "Failed to reset password for the virtual machine "); @@ -643,13 +632,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (template != null && template.getEnablePassword()) { userVm.setPassword(password); //update the encrypted password in vm_details table too - if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); - } - userVm.setDetail("Encrypted.Password", encryptedPasswd); - } + encryptAndStorePassword(userVm, password); } _vmDao.saveDetails(userVm); } else { @@ -3304,18 +3287,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // Check if an SSH key pair was selected for the instance and if so // use it to encrypt & save the vm password - String sshPublicKey = vm.getDetail("SSH.PublicKey"); - if (sshPublicKey != null && !sshPublicKey.equals("") - && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey( - sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); - } - - vm.setDetail("Encrypted.Password", encryptedPasswd); - _vmDao.saveDetails(vm); - } + encryptAndStorePassword(vm, password); params = new HashMap(); if (additionalParams != null) { @@ -4621,15 +4593,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // update the password in vm_details table too // Check if an SSH key pair was selected for the instance and if so // use it to encrypt & save the vm password - String sshPublicKey = vm.getDetail("SSH.PublicKey"); - if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("VM reset is completed but error occurred when encrypting newly created password"); - } - vm.setDetail("Encrypted.Password", encryptedPasswd); - _vmDao.saveDetails(vm); - } + encryptAndStorePassword(vm, password); } else { throw new CloudRuntimeException("VM reset is completed but failed to reset password for the virtual machine "); } @@ -4717,5 +4681,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (vm.getState() == State.Running) collectVmDiskStatistics(vm); } + + private void encryptAndStorePassword(UserVmVO vm, String password) { + String sshPublicKey = vm.getDetail("SSH.PublicKey"); + if (sshPublicKey != null && !sshPublicKey.equals("") + && password != null && !password.equals("saved_password")) { + if (!sshPublicKey.startsWith("ssh-rsa")) { + s_logger.warn("Only RSA public keys can be used to encrypt a vm password."); + return; + } + String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey( + sshPublicKey, password); + if (encryptedPasswd == null) { + throw new CloudRuntimeException("Error encrypting password"); + } + + vm.setDetail("Encrypted.Password", encryptedPasswd); + _vmDao.saveDetails(vm); + } + } } diff --git a/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java b/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java index 2755c548048..a14e5a4a9d7 100644 --- a/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java +++ b/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java @@ -82,7 +82,7 @@ public class SSHKeysHelper { if (!keyMaterial.contains(" ")) keyMaterial = new String(Base64.decodeBase64(keyMaterial.getBytes())); - if (!keyMaterial.startsWith("ssh-rsa") || !keyMaterial.contains(" ")) + if ((!keyMaterial.startsWith("ssh-rsa") && !keyMaterial.startsWith("ssh-dss")) || !keyMaterial.contains(" ")) return null; String[] key = keyMaterial.split(" "); diff --git a/utils/test/com/cloud/utils/crypto/RSAHelperTest.java b/utils/test/com/cloud/utils/crypto/RSAHelperTest.java new file mode 100644 index 00000000000..d1f496e1ab1 --- /dev/null +++ b/utils/test/com/cloud/utils/crypto/RSAHelperTest.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.utils.crypto; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.cloud.utils.crypt.RSAHelper; + +public class RSAHelperTest { + @Test + public void testEncryptWithRSA() { + String rsaKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2D2Cs0XAEqm+ajJpumIPrMpKp0CWtIW+8ZY2/MJCW" + + "hge1eY18u9I3PPnkMVJsTOaN0wQojjw4AkKgKjNZXA9wyUq56UyN/stmipu8zifWPgxQGDRkuzzZ6buk" + + "ef8q2Awjpo8hv5/0SRPJxQLEafESnUP+Uu/LUwk5VVC7PHzywJRUGFuzDl/uT72+6hqpL2YpC6aTl4/P" + + "2eDvUQhCdL9dBmUSFX8ftT53W1jhsaQl7mPElVgSCtWz3IyRkogobMPrpJW/IPKEiojKIuvNoNv4CDR6" + + "ybeVjHOJMb9wi62rXo+CzUsW0Y4jPOX/OykAm5vrNOhQhw0aaBcv5XVv8BRX"; + String encryptedString = RSAHelper.encryptWithSSHPublicKey(rsaKey, "content"); + assertNotNull(encryptedString); + } + + @Test + public void testEncryptWithDSA() { + String dssKey = "ssh-dss AAAAB3NzaC1kc3MAAACBALbaewDnzZ5AcGbZno7VW1m7Si3Q+yEANXZioVupfSwOP0q9aP2iV"+ + "tyqq575JnUVZXMDR2Gr254F/qCJ0TKAvucN0gcd2XslX4jBcu1Z7s7YZf6d7fC58k0NE6/keokJNKhQO" + + "i56iirRzSA/YFrD64mzfq6rEmai0q7GjGGP0RT1AAAAFQDO5++6JonyqnoRkV9Yl1OaEOPjVwAAAIAYA" + + "tqtKtU/INlTIuL3wt3nyKzwPUnz3fqxP5Ger3OlRZsOahalTFt2OF5jGGmCunyBTRteOetZObr0QhUIF" + + "4bSDr6UiYYYbH1ES0ws/t1mDIeTh3UUHV1QYACN6c07FKyKLMtB9AthiG2FMLKCEedG3NeXItuNzsuQD" + + "+n/K1rzMAAAAIBi5SM4pFPiB7BvTZvARV56vrG5QNgWVazSwbwgl/EACiWYbRauHDUQA9f+Rq+ayWcsR" + + "os1CD+Q81y9SmlQaZVKkSPZLxXfu5bi3s4o431xjilhZdt4vKbj2pK364IjghJPNBBfmRXzlj9awKxr/" + + "UebZcBgNRyeky7VZSbbF2jQSQ=="; + String encryptedString = RSAHelper.encryptWithSSHPublicKey(dssKey, "content"); + assertNull(encryptedString); + } +} diff --git a/utils/test/com/cloud/utils/ssh/SSHKeysHelperTest.java b/utils/test/com/cloud/utils/ssh/SSHKeysHelperTest.java new file mode 100644 index 00000000000..6402e3ef347 --- /dev/null +++ b/utils/test/com/cloud/utils/ssh/SSHKeysHelperTest.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.utils.ssh; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SSHKeysHelperTest { + @Test + public void rsaKeyTest() { + String rsaKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2D2Cs0XAEqm+ajJpumIPrMpKp0CWtIW+8ZY2/MJCW" + + "hge1eY18u9I3PPnkMVJsTOaN0wQojjw4AkKgKjNZXA9wyUq56UyN/stmipu8zifWPgxQGDRkuzzZ6buk" + + "ef8q2Awjpo8hv5/0SRPJxQLEafESnUP+Uu/LUwk5VVC7PHzywJRUGFuzDl/uT72+6hqpL2YpC6aTl4/P" + + "2eDvUQhCdL9dBmUSFX8ftT53W1jhsaQl7mPElVgSCtWz3IyRkogobMPrpJW/IPKEiojKIuvNoNv4CDR6" + + "ybeVjHOJMb9wi62rXo+CzUsW0Y4jPOX/OykAm5vrNOhQhw0aaBcv5XVv8BRX test@testkey"; + String storedRsaKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2D2Cs0XAEqm+ajJpumIPrMpKp0CWtIW+8ZY2/MJCW" + + "hge1eY18u9I3PPnkMVJsTOaN0wQojjw4AkKgKjNZXA9wyUq56UyN/stmipu8zifWPgxQGDRkuzzZ6buk" + + "ef8q2Awjpo8hv5/0SRPJxQLEafESnUP+Uu/LUwk5VVC7PHzywJRUGFuzDl/uT72+6hqpL2YpC6aTl4/P" + + "2eDvUQhCdL9dBmUSFX8ftT53W1jhsaQl7mPElVgSCtWz3IyRkogobMPrpJW/IPKEiojKIuvNoNv4CDR6" + + "ybeVjHOJMb9wi62rXo+CzUsW0Y4jPOX/OykAm5vrNOhQhw0aaBcv5XVv8BRX"; + String parsedKey = SSHKeysHelper.getPublicKeyFromKeyMaterial(rsaKey); + String fingerprint = SSHKeysHelper.getPublicKeyFingerprint(parsedKey); + + assertTrue(storedRsaKey.equals(parsedKey)); + assertTrue("f6:96:3f:f4:78:f7:80:11:6c:f8:e3:2b:40:20:f1:14".equals(fingerprint)); + + } + + @Test + public void dsaKeyTest() { + String dssKey = "ssh-dss AAAAB3NzaC1kc3MAAACBALbaewDnzZ5AcGbZno7VW1m7Si3Q+yEANXZioVupfSwOP0q9aP2iV"+ + "tyqq575JnUVZXMDR2Gr254F/qCJ0TKAvucN0gcd2XslX4jBcu1Z7s7YZf6d7fC58k0NE6/keokJNKhQO" + + "i56iirRzSA/YFrD64mzfq6rEmai0q7GjGGP0RT1AAAAFQDO5++6JonyqnoRkV9Yl1OaEOPjVwAAAIAYA" + + "tqtKtU/INlTIuL3wt3nyKzwPUnz3fqxP5Ger3OlRZsOahalTFt2OF5jGGmCunyBTRteOetZObr0QhUIF" + + "4bSDr6UiYYYbH1ES0ws/t1mDIeTh3UUHV1QYACN6c07FKyKLMtB9AthiG2FMLKCEedG3NeXItuNzsuQD" + + "+n/K1rzMAAAAIBi5SM4pFPiB7BvTZvARV56vrG5QNgWVazSwbwgl/EACiWYbRauHDUQA9f+Rq+ayWcsR" + + "os1CD+Q81y9SmlQaZVKkSPZLxXfu5bi3s4o431xjilhZdt4vKbj2pK364IjghJPNBBfmRXzlj9awKxr/" + + "UebZcBgNRyeky7VZSbbF2jQSQ== test key"; + String storedDssKey = "ssh-dss AAAAB3NzaC1kc3MAAACBALbaewDnzZ5AcGbZno7VW1m7Si3Q+yEANXZioVupfSwOP0q9aP2iV"+ + "tyqq575JnUVZXMDR2Gr254F/qCJ0TKAvucN0gcd2XslX4jBcu1Z7s7YZf6d7fC58k0NE6/keokJNKhQO" + + "i56iirRzSA/YFrD64mzfq6rEmai0q7GjGGP0RT1AAAAFQDO5++6JonyqnoRkV9Yl1OaEOPjVwAAAIAYA" + + "tqtKtU/INlTIuL3wt3nyKzwPUnz3fqxP5Ger3OlRZsOahalTFt2OF5jGGmCunyBTRteOetZObr0QhUIF" + + "4bSDr6UiYYYbH1ES0ws/t1mDIeTh3UUHV1QYACN6c07FKyKLMtB9AthiG2FMLKCEedG3NeXItuNzsuQD" + + "+n/K1rzMAAAAIBi5SM4pFPiB7BvTZvARV56vrG5QNgWVazSwbwgl/EACiWYbRauHDUQA9f+Rq+ayWcsR" + + "os1CD+Q81y9SmlQaZVKkSPZLxXfu5bi3s4o431xjilhZdt4vKbj2pK364IjghJPNBBfmRXzlj9awKxr/" + + "UebZcBgNRyeky7VZSbbF2jQSQ=="; + String parsedKey = SSHKeysHelper.getPublicKeyFromKeyMaterial(dssKey); + String fingerprint = SSHKeysHelper.getPublicKeyFingerprint(parsedKey); + + assertTrue(storedDssKey.equals(parsedKey)); + assertTrue("fc:6e:ef:31:93:f8:92:2b:a9:03:c7:06:90:f5:ec:bb".equals(fingerprint)); + + } +}