メモ:Rで証明書の情報を扱うときはopensslパッケージ
この影響を調べようと思ってopensslコマンドでちまちまやってたわけですが、
Rでも扱えないかな?と思ったらopensslパッケージがあったのでその使い方のメモ。
download_ssl_cert()
あるウェブサーバから証明書チェーンを取得するにはdownload_ssl_cert()
が使えます。
library(openssl) chain <- download_ssl_cert("www.r-project.org", 443) chain #> [[1]] #> [x509 certificate] *.r-project.org #> md5: 9aa98308c79a93da348a6a228b2e48ab #> sha1: 35c5d904e830005b852c2c1b6e5db00773524aad #> #> [[2]] #> [x509 certificate] Starfield Secure Certificate Authority - G2 #> md5: 1cd0976187316dde07259fd6267e2f0e #> sha1: 7edc376dcfd45e6ddf082c160df6ac21835b95d4 #> #> [[3]] #> [x509 certificate] Starfield Root Certificate Authority - G2 #> md5: 497bf0a8bc53a0846d7fd29499f558e9 #> sha1: 9565b778c8a50eb4fefd45c8a658dde2411ead0a #> #> [[4]] #> [x509 certificate] #> md5: 324a4bbbc863699bbe749ac6dd1d4624 #> sha1: ad7e1c28b064ef8f6003402014c3d0e3370eb58a
この4つ並んでいる[x509 certificate]
というやつは何者なんでしょう。
is(chain[[1]]) #> [1] "cert" str(chain[[1]]) #> List of 8 #> $ subject : chr "CN=*.r-project.org,OU=Domain Control Validated" #> $ issuer : chr "CN=Starfield Secure Certificate Authority - G2,OU=http://certs.starfieldtech.com/repository/,O=Starfield Technologies\\, Inc.,L"| __truncated__ #> $ algorithm : chr "sha256WithRSAEncryption" #> $ signature : raw [1:256] 6c a9 bd d1 ... #> $ validity : chr [1:2] "Aug 18 09:39:38 2015 GMT" "Aug 18 09:39:38 2018 GMT" #> $ self_signed: logi FALSE #> $ alt_names : chr [1:2] "*.r-project.org" "r-project.org" #> $ pubkey :List of 5 #> ..$ type : chr "rsa" #> ..$ size : int 2048 #> ..$ ssh : chr "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQ/cx8HyMW9SCLJsC/UKaTh/RskQ33Leicy0prlBc2D ..." #> ..$ fingerprint:Classes 'hash', 'md5' raw [1:16] 6b 37 2d 9b ... #> ..$ data :List of 2 #> .. ..$ e:Class 'bignum' raw [1:3] 01 00 01 #> .. ..$ n:Class 'bignum' raw [1:257] 00 d0 fd cc ...
よくわかりませんが、cert
というクラスのオブジェクトのようです。専用のメソッドもいくつか用意されています。
methods(class = "cert") #> [1] $ $<- [ [[ #> [5] [[<- [<- as.environment as.list #> [9] names print str #> see '?methods' for accessing help and source code
cert
とpubkey
しかし、str
したときの見た目はリストでありながら、実はraw
なのでうまくリストっぽく扱うことができません。
purrr::flatten(chain[[1]]) #> Error: `.x` must be a list (raw) typeof(chain[[1]]) #> [1] "raw"
どうすれば扱えるのかというと、as.list.cert
の中身を覗いてみると、cert_info()
とcert_pubkey()
という関数で情報を取り出せるようです。(エクスポートされていない関数)
openssl:::as.list.cert #> function (x, ...) #> { #> cert <- x #> info <- cert_info(cert) #> info$pubkey <- cert_pubkey(cert) #> info #> } #> <environment: namespace:openssl>
cert_info()
のほうは分かりやすい感じです。
openssl:::cert_info(chain[[1]]) #> $subject #> [1] "CN=*.r-project.org,OU=Domain Control Validated" #> #> $issuer #> [1] "CN=Starfield Secure Certificate Authority - G2,OU=http://certs.starfieldtech.com/repository/,O=Starfield Technologies\\, Inc.,L=Scottsdale,ST=Arizona,C=US" #> #> $algorithm #> [1] "sha256WithRSAEncryption" #> #> $signature #> [1] 6c a9 bd d1 96 3b 70 99 5a 81 cb 20 28 f1 c9 65 bf ae 31 84 8e d3 a1 #> [24] c7 13 cc bc 6b 4b 76 73 16 a9 2b 5a b3 54 a6 54 bb da 3d de 4c b6 39 #> [47] b7 6e a0 09 8c e0 2b 14 5b 81 64 2b e1 99 54 7a 2c e2 b1 82 d4 7e aa #> [70] 5b 1f 0c 49 47 91 f3 ea 56 4a b7 6e 2f 09 68 a7 5c 39 ec 22 92 91 84 #> [93] 49 62 9d 5f ee bd cc 13 76 02 71 ea 10 1d a3 ac 1a 8a 5e fb 56 38 aa #> [116] b5 58 59 d0 25 95 98 25 cb 4e 63 64 10 de 5e bc 8f e1 b3 5a e3 c7 87 #> [139] b5 f9 eb d1 aa 79 69 35 42 5c 60 c2 39 2a 45 94 2d fd 5a 58 0c b2 ae #> [162] 45 0d 1c 10 98 16 1f 53 ff 0a d2 bb 3f b0 00 c7 95 df 40 07 ab 3f 3d #> [185] 06 1b da 6f bb 49 79 04 18 92 a7 72 c7 22 8a 4f bd 35 3f c1 2d b0 94 #> [208] 3d 86 96 66 c0 40 cc fc 42 dc df 84 55 99 87 cc e1 0c e3 e7 16 c9 23 #> [231] 41 95 27 1f 95 40 12 4a 59 3d c9 9c fe f0 78 da 3e a1 3c c9 ff 73 53 #> [254] aa fb 62 #> #> $validity #> [1] "Aug 18 09:39:38 2015 GMT" "Aug 18 09:39:38 2018 GMT" #> #> $self_signed #> [1] FALSE #> #> $alt_names #> [1] "*.r-project.org" "r-project.org"
cert_pubkey()
のほうはpubkey
というクラスで公開鍵の情報を取り出します。
openssl:::cert_pubkey(chain[[1]]) #> [2048-bit rsa public key] #> md5: 6b372d9b5c5759fbb28baf03c609730a is(openssl:::cert_pubkey(chain[[1]])) #> [1] "pubkey"
これをリストにするのはちょっとややこしそうです。
openssl:::as.list.pubkey #> function (x, ...) #> { #> pubkey <- x #> data <- decompose(pubkey) #> type <- ifelse(inherits(pubkey, "ed25519"), "ed25519", pubkey_type(pubkey)) #> size <- pubkey_bitsize(pubkey) #> header <- switch(type, rsa = "ssh-rsa", dsa = "ssh-dss", #> ed25519 = "ssh-ed25519", ecdsa = paste0("ecdsa-sha2-nistp", #> substring(data$curve, 3)), stop("Unsupported keytype: ", #> type)) #> fp <- unlist(unname(fpdata(pubkey))) #> list(type = type, size = size, ssh = paste(header, base64_encode(fp)), #> fingerprint = md5(fp), data = data) #> } #> <environment: namespace:openssl>
このうち、$data$e
がexponent、$data$n
がmodulusであるようです。
as.list(openssl:::cert_pubkey(chain[[1]])) #> $type #> [1] "rsa" #> #> $size #> [1] 2048 #> #> $ssh #> [1] "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQ/cx8HyMW9SCLJsC/UKaTh/RskQ33Leicy0prlBc2Dm3Tk/m1Dn7cpwGZQrsS4ak1IaV5WroeJjWUzEiaJH5Ky4vJOGWJYXoXw5THDFxraaCk4CPd/cQuFkezY6iuEYORuNYoukV9xgXhfaGJN4K8Gan6Hi8syY6HqnOiSt887GUZckO7TQF3RishPlbLzx0G17inlhigDFtoFenG/x043EwkIFJ6pz1jA8h/Us29Rw1+853AwNZ/nOptaUFku6W3uXgf4TLYUn6NzPSKY6lJ34gpSoBVQIIU8hjz2Y3cwlIzgQnrw2qlzDmFmiZtjPTaMrvBzs5QHfsgOBHvc5j/" #> #> $fingerprint #> md5 6b:37:2d:9b:5c:57:59:fb:b2:8b:af:03:c6:09:73:0a #> #> $data #> $data$e #> [b] 65537 #> $data$n #> [b] 2638272027041747783761783549979133009904993477539266489148528104756141183116161853163984969825794002213359659579336056608215733466383159766139002018736633037788760944415871039657395687796561342102059330892378474802087844506583623983593958964128848966396664577492070253302465477713056839221376905333814306381725584171170790982755178470223655368274409275562361074599147461145420231751245480426593043389988210105506540000926552341283069418023609512462144893525398509145051250972555474545313066085477071580898111192447716762311418704485605783286008932951815748561342469340269836474975416443381026444469976424561917215359
tidyな感じにする
こんな感じ?
flatten_cert <- function(x) { result <- as.list(x) # signatureはraw。characterに変換 result$signature <- paste(as.character(result$signature), collapse = ":") # pubkeyはmodulusだけ取り出す pubkey <- as.list(result$pubkey) result$pubkey <- NULL # modulusはbignumなので、rawに変換→characterに変換、というルートにする modulus_raw <- as.raw(as.list(pubkey)$data$n) result$modulus <- paste(as.character(modulus_raw), collapse = ":") # SubjectAltNamesは複数あるのでlistでくるむ result$alt_names <- list(result$alt_names) # validityは有効期限の開始と終了を示す。パースしてそれぞれ別のフィールドに validity <- lubridate::parse_date_time2(result$validity, "mdHMSY", tz = "GMT") result$validity <- NULL result$notBefore <- validity[1] result$notAfter <- validity[2] result } purrr::map_df(chain, flatten_cert) #> # A tibble: 4 × 9 #> subject #> <chr> #> 1 CN=*.r-project.org,OU=Domain Control Validated #> 2 CN=Starfield Secure Certificate Authority - G2,OU=http://certs.starfieldtech.com/repository/,O=Starfield Technol #> 3 CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\\, Inc.,L=Scottsdale,ST=Arizona,C=US #> 4 OU=Starfield Class 2 Certification Authority,O=Starfield Technologies\\, Inc.,C=US #> # ... with 8 more variables: issuer <chr>, algorithm <chr>, signature <chr>, self_signed <lgl>, #> # alt_names <list>, modulus <chr>, notBefore <dttm>, notAfter <dttm>
見づらい…。たぶんsubject
はもっと分解できる気がするけど、issuer
とどう分ければいいのか思いつきませんでした。
感想
むずかしい。Pythonとかだともっとやりやすかったりするんでしょうか。