R将包含反斜杠的字符串从文件传递给charToRaw而不转义

R pass a string containing backslashes from a file to charToRaw without escaping

我正在尝试从受密码保护的文件中将数据库的密钥读入 R 并将其转换为原始文件,如下所示:

假设我的密钥是\xb@\xErd\xD5b\x1bs。我的目标是获得与将密钥作为字符串直接传递给 charToRaw 函数时得到的相同的原始密钥:

rawkey1 <- charToRaw("\xb@\xErd\xD5b\x1bs")

> rawkey1
[1] 0b 40 0e 72 64 d5 62 1b 73

我可以将其保存在 .csv 文件中并将其读回 R:

savemykey <- data.table(keyinbytes = "\xb@\xErd\xD5b\x1bs")

write.csv(savemykey, file = "My_key.csv")

mykey <- read.csv("My_key.csv", header = TRUE, stringsAsFactors = FALSE)

然后我可以将其转换为原始格式并产生所需的结果:

> rawkey2 = charToRaw(mykey$keyinbytes)
> rawkey2
[1] 0b 40 0e 72 64 d5 62 1b 73

直接传递给 charToRaw 函数和读取包含密钥的 csv 文件生成的原始密钥是相同的:

> rawkey1 == rawkey2
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

到目前为止一切顺利。唯一的问题是密钥是包含敏感信息的数据库的密钥,因此我想将其存储在受密码保护的文件中。

我能想到的唯一方法是使用 Microsoft Excel(使用 excel.link 包重新读取并提供密码作为参数);然而,似乎在创建 .xlsx 文件时,反斜杠在读回时被解释为转义符。这导致从字符到原始的错误转换:

library(xlsx)
write.xlsx2(savemykey, file = "My_key.xlsx", append = FALSE)

然后我打开 Microsoft Excel 文件,分配密码 "mypassword",保存并用 excel.link 包读回:

library(excel.link)
mykey <- xl.read.file("My_key.xlsx", xl.sheet = 1, password = "mypassword")

# Re-running the conversion:
rawkey3 = charToRaw(mykey$keyinbytes)

> rawkey3
[1] 3f 40 3f 72 64 d5 62 3f 73

如果我将此结果与第一个键进行比较,它不匹配:

> rawkey3 == rawkey1
[1] FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE

这是因为在读取 Microsoft Excel 文件时,R 已将反斜杠解释为转义符,将其后的字符转义并替换为 '?',见下文:

# Key as assigned object in R:
> savemykey$keyinbytes
[1] "\v@6rdÕb3s"

# Key read in from Microsoft Excel file:
> mykey$keyinbytes
[1] "?@?rdÕb?s"

根据我目前的尝试,似乎如果我将密钥保存在任何可以保存为纯文本(.csv、.txt 或直接在 R 脚本中并获取它的文件类型中),通过对反斜杠的正确评估将密钥读回 R,并转换为原始字节的正确模式。但是,我一直找不到任何密码保护纯文本文件/.csv 或 R 脚本的方法。

我想:

任何有关如何执行此操作的想法都将不胜感激。

我确实找到了一种创建加密文本文件的方法(可以是数据,也可以是稍作修改的 R 脚本)。 Stephane Doyen 在这里创建了一个脚本,用于创建一个加密文件,然后在使用摘要包读回后解密它:https://github.com/sdoyen/r_password_crypt .

工作原理如下:

# Load libraries

# This does the encryption and decryption
require(digest) 

# This allows users to enter a password securely with a masked widget
require(getPass)

# I'll put the details I want to encrypt into a data.table
require(data.table) 

创建并输入密码(长度必须是 16 个字母数字字符的倍数,例如 'myfavouritepw123'):

mypw <- charToRaw(getPass("Enter the password for your login details file:"))

加载 Stephane 的 write.aes 和 read.aes 函数:

# To encrypt and password protect a file:
write.aes <- function(df,filename, key) {
  require(digest)
  zz <- textConnection("out","w")
  write.csv(df,zz, row.names=F)
  close(zz)
  out <- paste(out,collapse="\n")
  raw <- charToRaw(out)
  raw <- c(raw,as.raw(rep(0,16-length(raw)%%16)))
  aes <- AES(key,mode="ECB")
  aes$encrypt(raw)
  writeBin(aes$encrypt(raw),filename)  
}


# To decrypt the file with a password after reading it back in:
read.aes <- function(filename,key) {
  require(digest)
  dat <- readBin(filename,"raw",n=1000)
  aes <- AES(key,mode="ECB")
  raw <- aes$decrypt(dat, raw=TRUE)
  txt <- rawToChar(raw[raw>0])
  read.csv(text=txt, stringsAsFactors = F)
}    

创建您要加密的文件:

注意:使用 write.aes 加密将导致“\”及其前面的字符被误解。为避免这种情况,将密钥转换为原始并将原始字节保存为单个字符串。使用 paste0collapse 将表示每个字节的字符粘贴在一起(sep 不起作用)。

mysecretlogin1 <- data.table(keyinbytes = paste0(charToRaw("\xb@\xErd\xD5b\x1bs"), collapse = " "))

将 data.table 写入加密文件 write.aes 使用您之前创建的密码作为密钥:

write.aes(df = mysecretlogin1, filename = "mysecretkey.txt", key = mypw)

读回文件并用您的密码解密:

mypw <- charToRaw(getPass("Enter the password for your login details file:"))

mysecretlogin2 <- data.table(read.aes(filename = "mysecretkey.txt", key = mypw))

检查导出和导入的文件是否相同:

    > mysecretlogin1 == mysecretlogin2
     keyinbytes
[1,]       TRUE

要以原始形式使用密钥,可以使用此函数将字符串转换回原始字节(在每个字节前添加“0x”允许将它们作为原始向量传递给不带引号的列表) :

makeraw <- function(characterstring) {
  mystring <- strsplit(characterstring, " ")
  mystring <- lapply(mystring, function(x) paste0("0x", x))
  mystring <- as.raw(unlist(mystring))
  mystring
}

应用函数:

myrawkey <- makeraw(mysecretlogin2$keyinbytes)

检查它是否有效:

> myrawkey
[1] 0b 40 0e 72 64 d5 62 1b 73
> str(myrawkey)
 raw [1:9] 0b 40 0e 72 ...
> is.raw(myrawkey)
[1] TRUE

此解决方案中的 'key'(没有双关语!)通过将密钥存储为(漂亮的字母数字)原始字节的字符串版本来避免将反斜杠误解为转义字符的整个问题。