2018-05-28 00:53:43 +02:00
/ * *
* @ author n1474335 [ n1474335 @ gmail . com ]
* @ copyright Crown Copyright 2016
* @ license Apache - 2.0
* /
import r from "jsrsasign" ;
import { fromBase64 } from "../lib/Base64" ;
import { toHex } from "../lib/Hex" ;
import { formatByteStr , formatDnStr } from "../lib/PublicKey" ;
import Operation from "../Operation" ;
import Utils from "../Utils" ;
/ * *
* Parse X . 509 certificate operation
* /
class ParseX509Certificate extends Operation {
/ * *
* ParseX509Certificate constructor
* /
constructor ( ) {
super ( ) ;
this . name = "Parse X.509 certificate" ;
this . module = "PublicKey" ;
this . description = "X.509 is an ITU-T standard for a public key infrastructure (PKI) and Privilege Management Infrastructure (PMI). It is commonly involved with SSL/TLS security.<br><br>This operation displays the contents of a certificate in a human readable format, similar to the openssl command line tool.<br><br>Tags: X509, server hello, handshake" ;
2018-08-21 20:07:13 +02:00
this . infoURL = "https://wikipedia.org/wiki/X.509" ;
2018-05-28 00:53:43 +02:00
this . inputType = "string" ;
this . outputType = "string" ;
this . args = [
{
"name" : "Input format" ,
"type" : "option" ,
"value" : [ "PEM" , "DER Hex" , "Base64" , "Raw" ]
}
] ;
this . patterns = [
{
"match" : "^-+BEGIN CERTIFICATE-+\\r?\\n[\\da-z+/\\n\\r]+-+END CERTIFICATE-+\\r?\\n?$" ,
"flags" : "i" ,
"args" : [
"PEM"
]
}
] ;
}
/ * *
* @ param { string } input
* @ param { Object [ ] } args
* @ returns { string }
* /
run ( input , args ) {
if ( ! input . length ) {
return "No input" ;
}
const cert = new r . X509 ( ) ,
inputFormat = args [ 0 ] ;
switch ( inputFormat ) {
case "DER Hex" :
input = input . replace ( /\s/g , "" ) ;
cert . readCertHex ( input ) ;
break ;
case "PEM" :
cert . readCertPEM ( input ) ;
break ;
case "Base64" :
cert . readCertHex ( toHex ( fromBase64 ( input , null , "byteArray" ) , "" ) ) ;
break ;
case "Raw" :
cert . readCertHex ( toHex ( Utils . strToByteArray ( input ) , "" ) ) ;
break ;
default :
throw "Undefined input format" ;
}
const sn = cert . getSerialNumberHex ( ) ,
issuer = cert . getIssuerString ( ) ,
subject = cert . getSubjectString ( ) ,
pk = cert . getPublicKey ( ) ,
pkFields = [ ] ,
sig = cert . getSignatureValueHex ( ) ;
let pkStr = "" ,
sigStr = "" ,
extensions = "" ;
// Public Key fields
pkFields . push ( {
key : "Algorithm" ,
value : pk . type
} ) ;
if ( pk . type === "EC" ) { // ECDSA
pkFields . push ( {
key : "Curve Name" ,
value : pk . curveName
} ) ;
pkFields . push ( {
key : "Length" ,
value : ( ( ( new r . BigInteger ( pk . pubKeyHex , 16 ) ) . bitLength ( ) - 3 ) / 2 ) + " bits"
} ) ;
pkFields . push ( {
key : "pub" ,
value : formatByteStr ( pk . pubKeyHex , 16 , 18 )
} ) ;
} else if ( pk . type === "DSA" ) { // DSA
pkFields . push ( {
key : "pub" ,
value : formatByteStr ( pk . y . toString ( 16 ) , 16 , 18 )
} ) ;
pkFields . push ( {
key : "P" ,
value : formatByteStr ( pk . p . toString ( 16 ) , 16 , 18 )
} ) ;
pkFields . push ( {
key : "Q" ,
value : formatByteStr ( pk . q . toString ( 16 ) , 16 , 18 )
} ) ;
pkFields . push ( {
key : "G" ,
value : formatByteStr ( pk . g . toString ( 16 ) , 16 , 18 )
} ) ;
} else if ( pk . e ) { // RSA
pkFields . push ( {
key : "Length" ,
value : pk . n . bitLength ( ) + " bits"
} ) ;
pkFields . push ( {
key : "Modulus" ,
value : formatByteStr ( pk . n . toString ( 16 ) , 16 , 18 )
} ) ;
pkFields . push ( {
key : "Exponent" ,
value : pk . e + " (0x" + pk . e . toString ( 16 ) + ")"
} ) ;
} else {
pkFields . push ( {
key : "Error" ,
value : "Unknown Public Key type"
} ) ;
}
// Format Public Key fields
for ( let i = 0 ; i < pkFields . length ; i ++ ) {
pkStr += ` ${ pkFields [ i ] . key } : ${ ( pkFields [ i ] . value + "\n" ) . padStart (
18 - ( pkFields [ i ] . key . length + 3 ) + pkFields [ i ] . value . length + 1 ,
" "
) } ` ;
}
// Signature fields
let breakoutSig = false ;
try {
breakoutSig = r . ASN1HEX . dump ( sig ) . indexOf ( "SEQUENCE" ) === 0 ;
} catch ( err ) {
// Error processing signature, output without further breakout
}
if ( breakoutSig ) { // DSA or ECDSA
sigStr = ` r: ${ formatByteStr ( r . ASN1HEX . getV ( sig , 4 ) , 16 , 18 ) }
s : $ { formatByteStr ( r . ASN1HEX . getV ( sig , 48 ) , 16 , 18 ) } ` ;
} else { // RSA or unknown
sigStr = ` Signature: ${ formatByteStr ( sig , 16 , 18 ) } ` ;
}
// Extensions
try {
extensions = cert . getInfo ( ) . split ( "X509v3 Extensions:\n" ) [ 1 ] . split ( "signature" ) [ 0 ] ;
} catch ( err ) { }
const issuerStr = formatDnStr ( issuer , 2 ) ,
nbDate = formatDate ( cert . getNotBefore ( ) ) ,
naDate = formatDate ( cert . getNotAfter ( ) ) ,
subjectStr = formatDnStr ( subject , 2 ) ;
return ` Version: ${ cert . version } (0x ${ Utils . hex ( cert . version - 1 ) } )
Serial number : $ { new r . BigInteger ( sn , 16 ) . toString ( ) } ( 0 x$ { sn } )
Algorithm ID : $ { cert . getSignatureAlgorithmField ( ) }
Validity
2018-12-30 04:15:07 +01:00
Not Before : $ { nbDate } ( dd - mm - yyyy hh : mm : ss ) ( $ { cert . getNotBefore ( ) } )
Not After : $ { naDate } ( dd - mm - yyyy hh : mm : ss ) ( $ { cert . getNotAfter ( ) } )
2018-05-28 00:53:43 +02:00
Issuer
$ { issuerStr }
Subject
$ { subjectStr }
Public Key
$ { pkStr . slice ( 0 , - 1 ) }
Certificate Signature
Algorithm : $ { cert . getSignatureAlgorithmName ( ) }
$ { sigStr }
Extensions
$ { extensions } ` ;
}
}
/ * *
* Formats dates .
*
* @ param { string } dateStr
* @ returns { string }
* /
function formatDate ( dateStr ) {
2018-12-30 17:46:18 +01:00
if ( dateStr . length === 13 ) { // UTC Time
2018-12-30 18:06:48 +01:00
dateStr = ( dateStr [ 0 ] < "5" ? "20" : "19" ) + dateStr ;
2018-12-30 17:46:18 +01:00
}
return dateStr [ 6 ] + dateStr [ 7 ] + "/" +
dateStr [ 4 ] + dateStr [ 5 ] + "/" +
dateStr [ 0 ] + dateStr [ 1 ] + dateStr [ 2 ] + dateStr [ 3 ] + " " +
2018-05-28 00:53:43 +02:00
dateStr [ 8 ] + dateStr [ 9 ] + ":" +
2018-12-30 17:46:18 +01:00
dateStr [ 10 ] + dateStr [ 11 ] + ":" +
dateStr [ 12 ] + dateStr [ 13 ] ;
2018-05-28 00:53:43 +02:00
}
2018-05-29 00:42:43 +02:00
export default ParseX509Certificate ;