import { BigInteger , nbi , parseBigInt } from "./jsbn" ; import { SecureRandom } from "./rng" ; function pkcs1pad1 ( s , n ) { if ( n < s . length + 22 ) { console . error ( "Message too long for RSA" ) ; return null ; } const len = n - s . length - 6 ; let filler = "" ; for ( let f = 0 ; f < len ; f += 2 ) { filler += "ff" ; } const m = "0001" + filler + "00" + s ; return parseBigInt ( m , 16 ) ; } function pkcs1pad2 ( s , n ) { if ( n < s . length + 11 ) { console . error ( "Message too long for RSA" ) ; return null ; } const ba = [ ] ; let i = s . length - 1 ; while ( i >= 0 && n > 0 ) { const c = s . charCodeAt ( i -- ) ; if ( c < 128 ) { ba [ -- n ] = c ; } else if ( ( c > 127 ) && ( c < 2048 ) ) { ba [ -- n ] = ( c & 63 ) | 128 ; ba [ -- n ] = ( c >> 6 ) | 192 ; } else { ba [ -- n ] = ( c & 63 ) | 128 ; ba [ -- n ] = ( ( c >> 6 ) & 63 ) | 128 ; ba [ -- n ] = ( c >> 12 ) | 224 ; } } ba [ -- n ] = 0 ; const rng = new SecureRandom ( ) ; const x = [ ] ; while ( n > 2 ) { x [ 0 ] = 0 ; while ( x [ 0 ] == 0 ) { rng . nextBytes ( x ) ; } ba [ -- n ] = x [ 0 ] ; } ba [ -- n ] = 2 ; ba [ -- n ] = 0 ; return new BigInteger ( ba ) ; } export class RSAKey { constructor ( ) { this . n = null ; this . e = 0 ; this . d = null ; this . p = null ; this . q = null ; this . dmp1 = null ; this . dmq1 = null ; this . coeff = null ; } public doPublic ( x ) { return x . modPowInt ( this . e , this . n ) ; } public doPrivate ( x ) { if ( this . p == null || this . q == null ) { return x . modPow ( this . d , this . n ) ; } let xp = x . mod ( this . p ) . modPow ( this . dmp1 , this . p ) ; const xq = x . mod ( this . q ) . modPow ( this . dmq1 , this . q ) ; while ( xp . compareTo ( xq ) < 0 ) { xp = xp . add ( this . p ) ; } return xp . subtract ( xq ) . multiply ( this . coeff ) . mod ( this . p ) . multiply ( this . q ) . add ( xq ) ; } public setPublic ( N , E ) { if ( N != null && E != null && N . length > 0 && E . length > 0 ) { this . n = parseBigInt ( N , 16 ) ; this . e = parseInt ( E , 16 ) ; } else { console . error ( "Invalid RSA public key" ) ; } } public encrypt ( text ) { const maxLength = ( this . n . bitLength ( ) + 7 ) >> 3 ; const m = pkcs1pad2 ( text , maxLength ) ; if ( m == null ) { return null ; } const c = this . doPublic ( m ) ; if ( c == null ) { return null ; } let h = c . toString ( 16 ) ; let length = h . length ; for ( let i = 0 ; i < maxLength * 2 - length ; i ++ ) { h = "0" + h ; } return h ; } public setPrivate ( N , E , D ) { if ( N != null && E != null && N . length > 0 && E . length > 0 ) { this . n = parseBigInt ( N , 16 ) ; this . e = parseInt ( E , 16 ) ; this . d = parseBigInt ( D , 16 ) ; } else { console . error ( "Invalid RSA private key" ) ; } } public setPrivateEx ( N , E , D , P , Q , DP , DQ , C ) { if ( N != null && E != null && N . length > 0 && E . length > 0 ) { this . n = parseBigInt ( N , 16 ) ; this . e = parseInt ( E , 16 ) ; this . d = parseBigInt ( D , 16 ) ; this . p = parseBigInt ( P , 16 ) ; this . q = parseBigInt ( Q , 16 ) ; this . dmp1 = parseBigInt ( DP , 16 ) ; this . dmq1 = parseBigInt ( DQ , 16 ) ; this . coeff = parseBigInt ( C , 16 ) ; } else { console . error ( "Invalid RSA private key" ) ; } } public generate ( B , E ) { const rng = new SecureRandom ( ) ; const qs = B >> 1 ; this . e = parseInt ( E , 16 ) ; const ee = new BigInteger ( E , 16 ) ; for ( ; ; ) { for ( ; ; ) { this . p = new BigInteger ( B - qs , 1 , rng ) ; if ( this . p . subtract ( BigInteger . ONE ) . gcd ( ee ) . compareTo ( BigInteger . ONE ) == 0 && this . p . isProbablePrime ( 10 ) ) { break ; } } for ( ; ; ) { this . q = new BigInteger ( qs , 1 , rng ) ; if ( this . q . subtract ( BigInteger . ONE ) . gcd ( ee ) . compareTo ( BigInteger . ONE ) == 0 && this . q . isProbablePrime ( 10 ) ) { break ; } } if ( this . p . compareTo ( this . q ) <= 0 ) { const t = this . p ; this . p = this . q ; this . q = t ; } const p1 = this . p . subtract ( BigInteger . ONE ) ; const q1 = this . q . subtract ( BigInteger . ONE ) ; const phi = p1 . multiply ( q1 ) ; if ( phi . gcd ( ee ) . compareTo ( BigInteger . ONE ) == 0 ) { this . n = this . p . multiply ( this . q ) ; this . d = ee . modInverse ( phi ) ; this . dmp1 = this . d . mod ( p1 ) ; this . dmq1 = this . d . mod ( q1 ) ; this . coeff = this . q . modInverse ( this . p ) ; break ; } } } public decrypt ( ctext ) { const c = parseBigInt ( ctext , 16 ) ; const m = this . doPrivate ( c ) ; if ( m == null ) { return null ; } return pkcs1unpad2 ( m , ( this . n . bitLength ( ) + 7 ) >> 3 ) ; } public generateAsync ( B , E , callback ) { const rng = new SecureRandom ( ) ; const qs = B >> 1 ; this . e = parseInt ( E , 16 ) ; const ee = new BigInteger ( E , 16 ) ; const rsa = this ; const loop1 = function ( ) { const loop4 = function ( ) { if ( rsa . p . compareTo ( rsa . q ) <= 0 ) { const t = rsa . p ; rsa . p = rsa . q ; rsa . q = t ; } const p1 = rsa . p . subtract ( BigInteger . ONE ) ; const q1 = rsa . q . subtract ( BigInteger . ONE ) ; const phi = p1 . multiply ( q1 ) ; if ( phi . gcd ( ee ) . compareTo ( BigInteger . ONE ) == 0 ) { rsa . n = rsa . p . multiply ( rsa . q ) ; rsa . d = ee . modInverse ( phi ) ; rsa . dmp1 = rsa . d . mod ( p1 ) ; rsa . dmq1 = rsa . d . mod ( q1 ) ; rsa . coeff = rsa . q . modInverse ( rsa . p ) ; setTimeout ( function ( ) { callback ( ) ; } , 0 ) ; } else { setTimeout ( loop1 , 0 ) ; } } ; const loop3 = function ( ) { rsa . q = nbi ( ) ; rsa . q . fromNumberAsync ( qs , 1 , rng , function ( ) { rsa . q . subtract ( BigInteger . ONE ) . gcda ( ee , function ( r ) { if ( r . compareTo ( BigInteger . ONE ) == 0 && rsa . q . isProbablePrime ( 10 ) ) { setTimeout ( loop4 , 0 ) ; } else { setTimeout ( loop3 , 0 ) ; } } ) ; } ) ; } ; const loop2 = function ( ) { rsa . p = nbi ( ) ; rsa . p . fromNumberAsync ( B - qs , 1 , rng , function ( ) { rsa . p . subtract ( BigInteger . ONE ) . gcda ( ee , function ( r ) { if ( r . compareTo ( BigInteger . ONE ) == 0 && rsa . p . isProbablePrime ( 10 ) ) { setTimeout ( loop3 , 0 ) ; } else { setTimeout ( loop2 , 0 ) ; } } ) ; } ) ; } ; setTimeout ( loop2 , 0 ) ; } ; setTimeout ( loop1 , 0 ) ; } public sign ( text , digestMethod , digestName ) { const header = getDigestHeader ( digestName ) ; const digest = header + digestMethod ( text ) . toString ( ) ; const m = pkcs1pad1 ( digest , this . n . bitLength ( ) / 4 ) ; if ( m == null ) { return null ; } const c = this . doPrivate ( m ) ; if ( c == null ) { return null ; } const h = c . toString ( 16 ) ; if ( ( h . length & 1 ) == 0 ) { return h ; } else { return "0" + h ; } } public verify ( text , signature , digestMethod ) { const c = parseBigInt ( signature , 16 ) ; const m = this . doPublic ( c ) ; if ( m == null ) { return null ; } const unpadded = m . toString ( 16 ) . replace ( /^1f+00/ , "" ) ; const digest = removeDigestHeader ( unpadded ) ; return digest == digestMethod ( text ) . toString ( ) ; } public encryptLong ( text ) { let res = '' ; const maxLen = ( ( this . n . bitLength ( ) + 7 ) >> 3 ) - 11 ; const textArr = this . setSplitChn ( text , maxLen ) ; textArr . forEach ( v => { res += this . encrypt ( v ) ; } ) ; return res ; } public decryptLong ( ctext ) { let res = '' ; const maxLen = ( this . n . bitLength ( ) + 7 ) >> 3 ; const splitMaxLen = maxLen * 2 ; if ( ctext . length > splitMaxLen ) { const ctextArr = ctext . match ( new RegExp ( '.{1,' + splitMaxLen + '}' , 'g' ) ) || [ ] ; const mArr = [ ] ; for ( let i = 0 ; i < ctextArr . length ; i ++ ) { const c = parseBigInt ( ctextArr [ i ] , 16 ) ; const m = this . doPrivate ( c ) ; if ( m == null ) { return null ; } mArr . push ( m ) ; } res = pkcs1unpad2Long ( mArr , maxLen ) ; } else { res = this . decrypt ( ctext ) ; } return res ; } private setSplitChn ( str , maxLen , res = [ ] ) { const arr = str . split ( '' ) ; let len = 0 ; for ( let i = 0 ; i < arr . length ; i ++ ) { const charCode = arr [ i ] . charCodeAt ( 0 ) ; if ( charCode <= 0x007f ) { len += 1 ; } else if ( charCode <= 0x07ff ) { len += 2 ; } else if ( charCode <= 0xffff ) { len += 3 ; } else { len += 4 ; } if ( len > maxLen ) { const currentStr = str . substring ( 0 , i ) ; res . push ( currentStr ) ; return this . setSplitChn ( str . substring ( i ) , maxLen , res ) ; } } res . push ( str ) ; return res ; } protected n : BigInteger ; protected e : number ; protected d : BigInteger ; protected p : BigInteger ; protected q : BigInteger ; protected dmp1 : BigInteger ; protected dmq1 : BigInteger ; protected coeff : BigInteger ; } function pkcs1unpad2 ( d , n ) { const b = d . toByteArray ( ) ; let i = 0 ; while ( i < b . length && b [ i ] == 0 ) { ++ i ; } if ( b . length - i != n - 1 || b [ i ] != 2 ) { return null ; } ++ i ; while ( b [ i ] != 0 ) { if ( ++ i >= b . length ) { return null ; } } let ret = "" ; while ( ++ i < b . length ) { const c = b [ i ] & 255 ; if ( c < 128 ) { ret += String . fromCharCode ( c ) ; } else if ( ( c > 191 ) && ( c < 224 ) ) { ret += String . fromCharCode ( ( ( c & 31 ) << 6 ) | ( b [ i + 1 ] & 63 ) ) ; ++ i ; } else { ret += String . fromCharCode ( ( ( c & 15 ) << 12 ) | ( ( b [ i + 1 ] & 63 ) << 6 ) | ( b [ i + 2 ] & 63 ) ) ; i += 2 ; } } return ret ; } function pkcs1unpad2Long ( dArr , n ) { let bArr = [ ] ; for ( let j = 0 ; j < dArr . length ; j ++ ) { const d = dArr [ j ] ; const b = d . toByteArray ( ) ; let i = 0 ; while ( i < b . length && b [ i ] == 0 ) { ++ i ; } if ( b . length - i != n - 1 || b [ i ] != 2 ) { return null ; } ++ i ; while ( b [ i ] != 0 ) { if ( ++ i >= b . length ) { return null ; } } bArr = bArr . concat ( b . slice ( i + 1 ) ) ; } const b = bArr ; let i = - 1 ; let ret = "" ; while ( ++ i < b . length ) { const c = b [ i ] & 255 ; if ( c < 128 ) { ret += String . fromCharCode ( c ) ; } else if ( ( c > 191 ) && ( c < 224 ) ) { ret += String . fromCharCode ( ( ( c & 31 ) << 6 ) | ( b [ i + 1 ] & 63 ) ) ; ++ i ; } else { ret += String . fromCharCode ( ( ( c & 15 ) << 12 ) | ( ( b [ i + 1 ] & 63 ) << 6 ) | ( b [ i + 2 ] & 63 ) ) ; i += 2 ; } } return ret ; } const DIGEST_HEADERS = { md2 : "3020300c06082a864886f70d020205000410" , md5 : "3020300c06082a864886f70d020505000410" , sha1 : "3021300906052b0e03021a05000414" , sha224 : "302d300d06096086480165030402040500041c" , sha256 : "3031300d060960864801650304020105000420" , sha384 : "3041300d060960864801650304020205000430" , sha512 : "3051300d060960864801650304020305000440" , ripemd160 : "3021300906052b2403020105000414" } ; function getDigestHeader ( name ) { return DIGEST_HEADERS [ name ] || "" ; } function removeDigestHeader ( str ) { for ( const name in DIGEST_HEADERS ) { if ( DIGEST_HEADERS . hasOwnProperty ( name ) ) { const header = DIGEST_HEADERS [ name ] ; const len = header . length ; if ( str . substr ( 0 , len ) == header ) { return str . substr ( len ) ; } } } return str ; }