软件安全不是安全软件。此处我们关注的主题包括身份验证、Access Control、机密性、加密和权限管理。
...
DSA dsa = new DSACryptoServiceProvider(1024);
...
...
DSA_generate_parameters_ex(dsa, 1024, NULL, 0, NULL, NULL, NULL);
...
...
dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160)
privatekey := new(dsa.PrivateKey)
privatekey.PublicKey.Parameters = *params
dsa.GenerateKey(privatekey, rand.Reader)
...
...
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA256PRNG", "SUN");
keyGen.initialize(1024, random);
...
...
from Crypto.PublicKey import DSA
key = DSA.generate(1024)
...
require 'openssl'
...
key = OpenSSL::PKey::DSA.new(1024)
...
EVP_SignUpdate
的调用,这将会导致创建不基于任何数据的签名:
...
rv = EVP_SignInit(ctx, EVP_sha512());
...
rv = EVP_SignFinal(ctx, sig, &sig_len, key);
...
update
的调用,这将会导致创建不基于任何数据的签名:
...
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(keyPair.getPrivate());
...
byte[] signatureBytes = sig.sign();
...
...
DSA dsa1 = new DSACryptoServiceProvider(Convert.ToInt32(TextBox1.Text));
...
key_len
的用例没有几个,即使在这些用例中,也应该有适当的保护来验证它不仅是一个数值而且还在密钥大小值的合理范围内。对于大多数用例,这应该是一个足够高的硬编码数字。
...
dsa.GenerateParameters(params, rand.Reader, key_len)
privatekey := new(dsa.PrivateKey)
privatekey.PublicKey.Parameters = *params
dsa.GenerateKey(privatekey, rand.Reader)
...
key_len
的能力。在此类情况下,您应该验证它不仅是一个数值而且还处于密钥大小值的合理范围内。对于大多数用例,请选择一个足够大的硬编码密钥大小。
require 'openssl'
...
key_len = io.read.to_i
key = OpenSSL::PKey::DSA.new(key_len)
...
key_len
的用例没有几个,即使在这些用例中,也应该有适当的保护来验证它不仅是一个数值而且还在密钥大小值的合理范围内。对于大多数用例,这应该是一个足够高的硬编码数字。示例 2:以下代码使用
Properties props = System.getProperties();
...
properties.setProperty("org.jcp.xml.dsig.secureValidation", "false");
XMLCryptoContext.setProperty
禁用 XML 签名安全验证:
DOMCryptoContext cryptoContext = new DOMCryptoContext() {...};
...
cryptoContext.setProperty("org.jcp.xml.dsig.secureValidation", false);
...
CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
key,
kCCKeySizeDES, // 64-bit key size
iv,
plaintext,
sizeof(plaintext),
ciphertext,
sizeof(ciphertext),
&numBytesEncrypted);
...
...
let iv = getTrueRandomIV()
...
let cStatus = CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmDES),
UInt32(kCCOptionPKCS7Padding),
key,
keyLength,
iv,
plaintext,
plaintextLength,
ciphertext,
ciphertextLength,
&numBytesEncrypted)
...
String
会导致熵大量丢失。String
示例 1:以下代码创建了加密密钥,然后将其转换为 String
。
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
...
KeyGenerator keygen = KeyGenerator.newInstance("AES");
...
SecretKey cryptoKey = keygen.generateKey();
byte[] rawCryptoKey = cryptoKey.getEncoded();
...
String key = new String(rawCryptoKey);
...
String
,但是对于为构造函数分配了字符集有效范围外的字节时将发生何种变化,却没有相关说明。事实上,与原始的 rawCryptoKey
加密密钥相比,key
很可能会造成熵的大量丢失。
static public byte[] EncryptWithRSA(byte[] plaintext, RSAParameters key) {
try {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(key);
return rsa.Encrypt(plaintext, false);
}
catch(CryptographicException e) {
Console.WriteLine(e.Message);
return null;
}
}
void encrypt_with_rsa(BIGNUM *out, BIGNUM *in, RSA *key) {
u_char *inbuf, *outbuf;
int ilen;
...
ilen = BN_num_bytes(in);
inbuf = xmalloc(ilen);
BN_bn2bin(in, inbuf);
if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, RSA_NO_PADDING)) <= 0) {
fatal("encrypt_with_rsa() failed");
}
...
}
...
import "crypto/rsa"
...
plaintext := []byte("Attack at dawn")
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, &k.PublicKey, plaintext)
...
public Cipher getRSACipher() {
Cipher rsa = null;
try {
rsa = javax.crypto.Cipher.getInstance("RSA/NONE/NoPadding");
}
catch (java.security.NoSuchAlgorithmException e) {
log("this should never happen", e);
}
catch (javax.crypto.NoSuchPaddingException e) {
log("this should never happen", e);
}
return rsa;
}
+ (NSData *) encryptData:(NSData *) plaintextData withKey:(SecKeyRef *) publicKey {
CFErrorRef error = nil;
NSData *ciphertextData = (NSData*) CFBridgingRelease(
SecKeyCreateEncryptedData(*publicKey,
kSecKeyAlgorithmRSAEncryptionPKCS1,
(__bridge CFDataRef) plaintextData,
&error));
if (error) {
// handle error ...
}
return ciphertextData;
}
function encrypt($input, $key) {
$output='';
openssl_public_encrypt($input, $output, $key, OPENSSL_NO_PADDING);
return $output;
}
...
from Crypto.PublicKey import RSA
message = 'Attack at dawn'
key = RSA.importKey(open('pubkey.der').read())
ciphertext = key.encrypt(message)
...
require 'openssl'
...
key = OpenSSL::PKey::RSA.new 2048
public_encrypted = key.public_encrypt(data) #padding type not specified
...
Example 1
中,仅使用字符串来调用 OpenSSL::PKey::RSA#public_encrypt
,不指定要使用的填充类型。填充类型默认为 OpenSSL::PKey::RSA::PKCS1_PADDING
。
func encrypt(data plaintextData:Data, publicKey:SecKey) throws -> Data {
var error: Unmanaged<CFError>?
guard let ciphertextData = SecKeyCreateEncryptedData(publicKey,
.rsaEncryptionPKCS1,
plaintextData as CFData,
&error) else {
throw error!.takeRetainedValue() as Error
}
return ciphertextData as Data;
}
...
Blob iv = Blob.valueOf('1234567890123456');
Blob encrypted = Crypto.encrypt('AES128', encKey, iv, input);
...
byte[] iv = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
using (SymmetricAlgorithm aesAlgo = SymmetricAlgorithm.Create("AES"))
{
...
aesAlgo.IV = iv;
...
}
unsigned char * iv = "12345678";
EVP_EncryptInit_ex(&ctx, EVP_idea_gcm(), NULL, key, iv);
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
)
...
block, err := aes.NewCipher(key)
...
mode := cipher.NewCBCEncrypter(block, key)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
byte[] iv = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
IvParameterSpec ips = new IvParameterSpec(iv);
...
const iv = "hardcoded"
const cipher = crypto.createCipheriv("aes-192-ccm", key, iv)
...
NSString *iv = @"1234567812345678"; //Bad idea to hard code IV
char ivPtr[kCCBlockSizeAES128];
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSASCIIStringEncoding];
...
ccStatus = CCCrypt( kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
[key cStringUsingEncoding:NSASCIIStringEncoding],
kCCKeySizeAES128,
[ivPtr], /*IV should be something random (not null and not constant)*/
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted
);
nil
) 时选择了 CBC 模式,则将使用全零的 IV。
from Crypto.Cipher import AES
from Crypto import Random
...
key = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CTR, IV=key)
require 'openssl'
...
cipher = OpenSSL::Cipher::AES.new('256-GCM')
cipher.encrypt
@key = cipher.random_key
cipher.iv=@key
encrypted = cipher.update(data) + cipher.final # encrypts data without hardcoded IV
...
...
let cStatus = CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmAES128),
UInt32(kCCOptionPKCS7Padding),
key,
keyLength,
"0123456789012345",
plaintext,
plaintextLength,
ciphertext,
ciphertextLength,
&numBytesEncrypted)
nil
) 时选择了 CBC 模式,则将使用全零的 IV。
...
var objAesCryptoService = new AesCryptoServiceProvider();
objAesCryptoService.Mode = CipherMode.ECB;
objAesCryptoService.Padding = PaddingMode.PKCS7;
objAesCryptoService.Key = securityKeyArray;
var objCrytpoTransform = objAesCryptoService.CreateEncryptor();
...
EVP_EncryptInit_ex(&ctx, EVP_aes_256_ecb(), NULL, key, iv);
...
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
...
...
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
...
...
ccStatus = CCCrypt( kCCEncrypt,
kCCAlgorithmAES,
kCCOptionECBMode, // Uses ECB mode
key,
kCCKeySizeAES128,
iv,
plaintext,
sizeof(plaintext),
ciphertext,
sizeof(ciphertext),
&numBytesEncrypted);
...
from Crypto.Cipher import AES
from Crypto import Random
...
key = Random.new().read(AES.block_size)
random_iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_ECB, random_iv)
require 'openssl'
...
cipher = OpenSSL::Cipher::AES.new('256-ECB')
...
ccStatus = CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmAES128),
UInt32(kCCOptionECBMode),
keyData.bytes,
keyLength,
keyData.bytes,
data.bytes,
data.length,
cryptData.mutableBytes,
cryptData.length,
&numBytesEncrypted)
...
static public byte[] EncryptWithRSA(byte[] plaintext, RSAParameters key) {
try {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512);
rsa.ImportParameters(key);
return rsa.Encrypt(plaintext, true);
}
catch(CryptographicException e) {
Console.WriteLine(e.Message);
return null;
}
}
EVP_PKEY * get_RSA_key() {
unsigned long err;
EVP_PKEY * pkey;
RSA * rsa;
rsa = RSA_generate_key(512, 35, NULL, NULL);
if (rsa == NULL) {
err = ERR_get_error();
printf("Error = %s\n",ERR_reason_error_string(err));
return NULL;
}
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
return pkey;
}
...
myPrivateKey := rsa.GenerateKey(rand.Reader, 1024);
...
public static KeyPair getRSAKey() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(512);
KeyPair key = keyGen.generateKeyPair();
return key;
}
...
crmfObject = crypto.generateCRMFRequest(
"CN=" + name.value,
password.value,
authenticator,
keyTransportCert,
"setCRMFRequest();",
512, null, "rsa-dual-use");
...
...
CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
key,
kCCKeySizeDES, // 64-bit key size
iv,
plaintext,
sizeof(plaintext),
ciphertext,
sizeof(ciphertext),
&numBytesEncrypted);
...
...
$keysize = 1024;
$options = array('private_key_bits' => $keysize, 'private_key_type' => OPENSSL_KEYTYPE_RSA);
$res = openssl_pkey_new($options);
...
...
from Crypto.PublicKey import RSA
key = RSA.generate(1024)
...
require 'openssl'
...
pkey = OpenSSL::PKey::RSA.new 1024
...
...
let iv = getTrueRandomIV()
...
let cStatus = CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmDES),
UInt32(kCCOptionPKCS7Padding),
key,
UInt32(kCCKeySizeDES), // 64-bit key size
iv,
plaintext,
plaintextLength,
ciphertext,
ciphertextLength,
&numBytesEncrypted)
...
EVP_DecryptUpdate
的调用,这将导致密文解密失败。
...
EVP_DecryptInit_ex(&ctx, EVP_aes_256_gcm(), NULL, key, iv);
...
if(!EVP_DecryptFinal_ex(&ctx, outBuf+outBytes, &tmpOutBytes))
prtErrAndExit(1, "ERROR: EVP_DecryptFinal_ex did not work...\n");
...
KeyGenerator
的初始化步骤,可能导致使用比推荐的密钥更小的密钥:
...
final String CIPHER_INPUT = "123456ABCDEFG";
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecretKey secretKey = keyGenerator.generateKey();
byte[] byteKey = secretKey.getEncoded();
....
OpenSSL::Cipher#update
的调用,这将导致密文解密失败:
require 'openssl'
...
decipher = OpenSSL::Cipher::AES.new(128, :GCM)
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.final #missed update method
...
import (
"crypto/aes"
"crypto/cipher"
"os"
)
...
iv = b'1234567890123456'
CTRstream = cipher.NewCTR(block, iv)
CTRstream.XORKeyStream(plaintext, ciphertext)
...
f := os.Create("data.enc")
f.Write(ciphertext)
f.Close()
Example 1
中,由于 iv
设置为常量初始化向量,因此容易受到重用攻击。
...
const cipher = crypto.createCipheriv("AES-256-CTR", key, 'iv')
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
fs.writeFile('my_encrypted_data', ciphertext, function (err) {
...
});
from Crypto.Cipher import AES
from Crypto import Random
...
key = Random.new().read(AES.block_size)
iv = b'1234567890123456'
cipher = AES.new(key, AES.MODE_CTR, iv, counter)
...
encrypted = cipher.encrypt(data)
f = open("data.enc", "wb")
f.write(encrypted)
f.close()
...
Example 1
中,由于 iv
设置为常量初始化向量,因此容易受到重用攻击。
require 'openssl'
...
cipher = OpenSSL::Cipher.new('AES-256-CTR')
cipher.encrypt
cipher.iv='iv'
...
encrypted = cipher.update(data) + cipher.final
File.open('my_encrypted_data', 'w') do |file|
file.write(encrypted)
end
Example 1
中,由于 OpenSSL::Cipher#iv=
设置为常量初始化向量,因此容易受到重用攻击。
...
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(Convert.ToInt32(tx.Text));
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够通过修改文本框值 tx.Text
来操纵加密算法的密钥大小参数。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。
...
rsa.GenerateKey(random, user_input)
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够操纵加密算法的密钥大小参数,因为变量 user_input
可由用户控制。一旦软件发布,撤消与用户控制的密钥大小相关的问题就会非常困难。很难知道恶意用户是否控制了给定加密操作的密钥大小。
...
Properties prop = new Properties();
prop.load(new FileInputStream("config.properties"));
String keySize = prop.getProperty("keySize");
...
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
Integer.parseInt(keySize)
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够通过修改 keySize
属性来操纵加密算法的密钥大小参数。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。
...
@property (strong, nonatomic) IBOutlet UITextField *inputTextField;
...
CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key,
sizeof(_inputTextField.text),
iv,
plaintext,
sizeof(plaintext),
ciphertext,
sizeof(ciphertext),
&numBytesEncrypted);
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够通过修改 UITextField inputTextField
中的文本来操纵加密算法的密钥大小参数。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。
...
$hash = hash_pbkdf2('sha256', $password, $random_salt, 100000, strlen($password));
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够操纵加密算法的密钥大小参数,因为变量 user_input
可由用户控制。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。
...
dk = hashlib.pbkdf2_hmac('sha256', password, random_salt, 100000, dklen=user_input)
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够操纵加密算法的密钥大小参数,因为变量 user_input
可由用户控制。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。
...
dk = OpenSSL::PKCS5.pbkdf2_hmac(password, random_salt, 100000, user_input, digest)
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够操纵加密算法的密钥大小参数,因为变量 user_input
可由用户控制。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。
...
@IBOutlet weak var inputTextField : UITextField!
...
let key = (inputTextField.text as NSString).dataUsingEncoding(NSUTF8StringEncoding)
let keyPointer = UnsafePointer<UInt8>(key.bytes)
let keyLength = size_t(key.length)
...
let operation : CCOperation = UInt32(kCCEncrypt)
let algoritm : CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options : CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
CCCrypt(operation,
algorithm,
options,
keyPointer,
keyLength,
iv,
plaintextPointer,
plaintextLength,
ciphertextPointer,
ciphertextLength,
&numBytesEncrypted)
...
Example 1
中的代码将成功运行,但任何有权使用此功能的人将能够通过修改 UITextField inputTextField
中的文本来操纵加密算法的密钥大小参数。一旦程序发布,撤消与用户控制的密钥大小相关的问题就会非常困难,因为很难知道恶意用户是否确定了给定加密操作的密钥大小。doSecurityCheck()
执行了安全检查,并且它可被其子类覆盖。
public class BadSecurityCheck {
private int id;
public BadSecurityCheck() {
doSecurityCheck();
id = 1;
}
protected void doSecurityCheck() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SomePermission("SomeAction"));
}
}
}
SecurityManager
权限,则将抛出 SecurityException
异常,这是运行时异常并将阻止程序进一步执行。由于 BadSecurityCheck
不是 final
,并且方法 doSecurityCheck()
是 protected
而非 final
,因此意味着可以为此类创建子类以覆盖此函数。 doSecurityCheck()
由子类所覆盖:
public class EvilSubclass extends BadSecurityCheck {
private int id;
public EvilSubclass() {
super();
}
protected void doSecurityCheck() {
//do nothing
}
}
EvilSubclass
进行实例化时,构造函数首先调用 super()
,以调用超类的构造函数。这反过来会调用函数 doSecurityCheck()
,但 Java 从超类中查找之前将先在子类中查找函数,从而调用受攻击者控制且会绕过安全检查的方法,因此 id
仍将设置为 1
。
<sp:TransportBinding>
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpToken/>
</wsp:Policy>
</sp:TransportToken>
...
</sp:TransportBinding>